[java-opensaml] branch OSJ-246 updated: Revamp initialization handling, fix logging, and add XML variant.

Scott Cantor cantor.2 at osu.edu
Fri Apr 3 13:45:21 EDT 2020


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch OSJ-246
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=b565c5c509cd916eecf0c3ca3a848daa7648b23b

The following commit(s) were added to refs/heads/OSJ-246 by this push:
       new  b565c5c   Revamp initialization handling, fix logging, and add XML variant.
b565c5c is described below

commit b565c5c509cd916eecf0c3ca3a848daa7648b23b
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Apr 3 13:45:15 2020 -0400

    Revamp initialization handling, fix logging, and add XML variant.
---
 .../client/AbstractClientStorageServiceStore.java  |  38 ++--
 .../impl/client/ClientStorageServiceStore.java     |  13 ++
 .../impl/client/JSONClientStorageServiceStore.java |  26 +--
 .../impl/client/XMLClientStorageServiceStore.java  | 231 +++++++++++++++++++++
 4 files changed, 271 insertions(+), 37 deletions(-)

diff --git a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/AbstractClientStorageServiceStore.java b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/AbstractClientStorageServiceStore.java
index 58f3057..0d2f2e6 100644
--- a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/AbstractClientStorageServiceStore.java
+++ b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/AbstractClientStorageServiceStore.java
@@ -48,32 +48,16 @@ public abstract class AbstractClientStorageServiceStore implements ClientStorage
     @Nonnull @NonnullElements private final Map<String, Map<String, MutableStorageRecord<?>>> contextMap;
     
     /** Data source. */
-    @Nonnull private final ClientStorageSource source;
+    @Nonnull private ClientStorageSource source;
     
     /** Dirty bit. */
     private boolean dirty;
 
     /**
      * Reconstitute stored data.
-     * 
-     * <p>The dirty bit is set based on the result. If successful, the bit is cleared,
-     * but if an error occurs, it will be set.</p>
-     * 
-     * @param raw serialized data to load
-     * @param src data source
      */
-    AbstractClientStorageServiceStore(@Nullable @NotEmpty final String raw, @Nonnull final ClientStorageSource src) {
-        source = Constraint.isNotNull(src, "ClientStorageSource cannot be null");
+    AbstractClientStorageServiceStore() {
         contextMap = new HashMap<>();
-        if (raw != null) {
-            try {
-                doLoad(raw);
-            } catch (final IOException e) {
-                contextMap.clear();
-                // Setting this should force corrupt data in the client to be overwritten.
-                setDirty(true);
-            }
-        }
     }
 
     /** {@inheritDoc} */
@@ -100,6 +84,24 @@ public abstract class AbstractClientStorageServiceStore implements ClientStorage
         return contextMap;
     }
     
+    /** {@inheritDoc} */
+    public void load(@Nullable @NotEmpty final String raw, @Nonnull final ClientStorageSource src) {
+        
+        contextMap.clear();
+        source = Constraint.isNotNull(src, "ClientStorageSource cannot be null");
+        
+        if (raw != null) {
+            try {
+                doLoad(raw);
+            } catch (final IOException e) {
+                contextMap.clear();
+                // Setting this should force corrupt data in the client to be overwritten.
+                setDirty(true);
+            }
+        }
+    }
+
+    
     /**
      * Reconstitute stored data.
      * 
diff --git a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/ClientStorageServiceStore.java b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/ClientStorageServiceStore.java
index 0fadd3b..6494519 100644
--- a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/ClientStorageServiceStore.java
+++ b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/ClientStorageServiceStore.java
@@ -66,6 +66,19 @@ public interface ClientStorageServiceStore {
     @Nonnull @NonnullElements @Live Map<String,Map<String,MutableStorageRecord<?>>> getContextMap();
     
     /**
+     * Reconstitute stored data.
+     * 
+     * <p>The dirty bit is set based on the result. If successful, the bit is cleared,
+     * but if an error occurs, it will be set.</p>
+     * 
+     * <p>By design this method should not throw under any non-catastrophic conditions.</p>
+     * 
+     * @param raw serialized data to load
+     * @param src storage source
+     */
+    void load(@Nullable @NotEmpty final String raw, @Nonnull final ClientStorageSource src);
+    
+    /**
      * Serialize current state of stored data into a storage operation.
      * 
      * @param storageService storage service
diff --git a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/JSONClientStorageServiceStore.java b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/JSONClientStorageServiceStore.java
index 0388f01..17c8282 100644
--- a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/JSONClientStorageServiceStore.java
+++ b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/JSONClientStorageServiceStore.java
@@ -50,21 +50,7 @@ public class JSONClientStorageServiceStore extends AbstractClientStorageServiceS
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(JSONClientStorageServiceStore.class);
-
-    /**
-     * Reconstitute stored data.
-     * 
-     * <p>The dirty bit is set based on the result. If successful, the bit is cleared,
-     * but if an error occurs, it will be set.</p>
-     * 
-     * @param raw serialized data to load
-     * @param src data source
-     */
-    public JSONClientStorageServiceStore(@Nullable @NotEmpty final String raw,
-            @Nonnull final ClientStorageSource src) {
-        super(raw, src);
-    }
-    
+        
     /** {@inheritDoc} */
     public void doLoad(@Nullable @NotEmpty final String raw) throws IOException {
         try {
@@ -162,12 +148,12 @@ public class JSONClientStorageServiceStore extends AbstractClientStorageServiceS
             
             final String raw = sink.toString();
             
-            log.trace("{} Size of data before encryption is {}", raw.length(), storageService.getLogPrefix());
-            log.trace("{} Data before encryption is {}", raw, storageService.getLogPrefix());
+            log.trace("{} Size of data before encryption is {}", storageService.getLogPrefix(), raw.length());
+            log.trace("{} Data before encryption is {}", storageService.getLogPrefix(), raw);
             try {
                 final String wrapped = storageService.getDataSealer().wrap(raw,
                         exp > 0 ? Instant.ofEpochMilli(exp) : Instant.now().plus(Duration.ofDays(1)));
-                log.trace("{} Size of data after encryption is {}", wrapped.length(), storageService.getLogPrefix());
+                log.trace("{} Size of data after encryption is {}", storageService.getLogPrefix(), wrapped.length());
                 setDirty(false);
                 return new ClientStorageServiceOperation(storageService.getId(), storageService.getStorageName(),
                         wrapped, getSource());
@@ -186,7 +172,9 @@ public class JSONClientStorageServiceStore extends AbstractClientStorageServiceS
         /** {@inheritDoc} */
         @Nonnull public ClientStorageServiceStore load(@Nullable @NotEmpty final String raw,
                 @Nonnull final ClientStorageSource src) {
-            return new JSONClientStorageServiceStore(raw, src);
+            final ClientStorageServiceStore store = new JSONClientStorageServiceStore();
+            store.load(raw, src);
+            return store;
         }
     }
 
diff --git a/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/XMLClientStorageServiceStore.java b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/XMLClientStorageServiceStore.java
new file mode 100644
index 0000000..1eedaf2
--- /dev/null
+++ b/opensaml-storage-impl/src/main/java/org/opensaml/storage/impl/client/XMLClientStorageServiceStore.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.storage.impl.client;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.storage.MutableStorageRecord;
+import org.opensaml.storage.impl.client.ClientStorageService.ClientStorageSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Strings;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.security.DataSealerException;
+import net.shibboleth.utilities.java.support.xml.BasicParserPool;
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+import net.shibboleth.utilities.java.support.xml.ParserPool;
+import net.shibboleth.utilities.java.support.xml.SerializeSupport;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
+
+/**
+ * XML-based storage for {@link ClientStorageService}.
+ */
+public class XMLClientStorageServiceStore extends AbstractClientStorageServiceStore {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(XMLClientStorageServiceStore.class);
+    
+    /** Parser machinery. */
+    @Nonnull private final ParserPool parserPool;
+
+    /**
+     * Constructor.
+     * 
+     * @param pool {@link ParserPool} to use
+     */
+    public XMLClientStorageServiceStore(@Nonnull final ParserPool pool) {
+        parserPool = Constraint.isNotNull(pool, "ParserPool cannot be null");
+    }
+
+    //Checkstyle: CyclomaticComplexity|MethodLength OFF
+    /** {@inheritDoc} */
+    public void doLoad(@Nullable @NotEmpty final String raw) throws IOException {
+        try {
+            final Document doc = parserPool.parse(new StringReader(raw));
+            final Element rootElement = doc != null ? doc.getDocumentElement() : null;
+            
+            if (rootElement == null || !"map".equals(rootElement.getNodeName())) {
+                throw new IOException("Found invalid data structure while parsing context map");
+            }
+            
+            Element contextElement = ElementSupport.getFirstChildElement(rootElement);
+            while (contextElement != null && "c".equals(contextElement.getNodeName())) {
+
+                final String contextId = contextElement.getAttribute("id");
+                if (!Strings.isNullOrEmpty(contextId)) {
+                    // Create new context if necessary.
+                    Map<String,MutableStorageRecord<?>> dataMap = getContextMap().get(contextId);
+                    if (dataMap == null) {
+                        dataMap = new HashMap<>();
+                        getContextMap().put(contextId, dataMap);
+                    }
+                    
+                    Element keyElement = ElementSupport.getFirstChildElement(contextElement);
+                    while (keyElement != null && "k".equals(keyElement.getNodeName())) {
+                        final String keyId = keyElement.getAttribute("id");
+                        if (!Strings.isNullOrEmpty(keyId)) {
+                            
+                            Long exp = null;
+                            if (keyElement.hasAttribute("x")) {
+                                exp = Long.valueOf(keyElement.getAttribute("x"));
+                            }
+                            
+                            dataMap.put(keyId, new MutableStorageRecord<>(keyElement.getTextContent(), exp));
+                        }
+                        
+                        keyElement = ElementSupport.getNextSiblingElement(keyElement);
+                    }
+                }
+                
+                contextElement = ElementSupport.getNextSiblingElement(contextElement);
+            }
+            setDirty(false);
+        } catch (final XMLParserException e) {
+            log.error("Found invalid data structure while parsing context map", e);
+            throw new IOException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public ClientStorageServiceOperation save(@Nonnull final ClientStorageService storageService)
+            throws IOException {
+        
+        if (!isDirty()) {
+            log.trace("{} Storage state has not been modified, save operation skipped", storageService.getLogPrefix());
+            return null;
+        }
+        
+        if (getContextMap().isEmpty()) {
+            log.trace("{} Data is empty", storageService.getLogPrefix());
+            return new ClientStorageServiceOperation(storageService.getId(), storageService.getStorageName(), null,
+                    getSource());
+        }
+
+        long exp = 0L;
+        final long now = System.currentTimeMillis();
+        boolean empty = true;
+
+        try {
+            final Document doc = parserPool.newDocument();
+            final Element rootElement = doc.createElement("map");
+            
+            for (final Map.Entry<String,Map<String, MutableStorageRecord<?>>> context
+                    : getContextMap().entrySet()) {
+                if (!context.getValue().isEmpty()) {
+                    final Element contextElement = doc.createElement("c");
+                    contextElement.setAttribute("id", context.getKey());
+                    
+                    for (final Map.Entry<String,MutableStorageRecord<?>> entry : context.getValue().entrySet()) {
+                        final MutableStorageRecord<?> record = entry.getValue();
+                        final Long recexp = record.getExpiration();
+                        if (recexp == null || recexp > now) {
+                            empty = false;
+                            final Element keyElement = doc.createElement("k");
+                            keyElement.setAttribute("id", entry.getKey());
+                            keyElement.setTextContent(record.getValue());
+                            
+                            if (recexp != null) {
+                                keyElement.setAttribute("x", recexp.toString());
+                                exp = Math.max(exp, recexp);
+                            }
+                            contextElement.appendChild(keyElement);
+                        }
+                    }
+                    
+                    rootElement.appendChild(contextElement);
+                }
+            }
+
+            if (empty) {
+                log.trace("{} Data is empty", storageService.getLogPrefix());
+                return new ClientStorageServiceOperation(storageService.getId(), storageService.getStorageName(), null,
+                        getSource());
+            }
+            
+            final String raw = SerializeSupport.nodeToString(rootElement);
+            
+            log.trace("{} Size of data before encryption is {}", storageService.getLogPrefix(), raw.length());
+            log.trace("{} Data before encryption is {}", storageService.getLogPrefix(), raw);
+            try {
+                final String wrapped = storageService.getDataSealer().wrap(raw,
+                        exp > 0 ? Instant.ofEpochMilli(exp) : Instant.now().plus(Duration.ofDays(1)));
+                log.trace("{} Size of data after encryption is {}", storageService.getLogPrefix(), wrapped.length());
+                setDirty(false);
+                return new ClientStorageServiceOperation(storageService.getId(), storageService.getStorageName(),
+                        wrapped, getSource());
+            } catch (final DataSealerException e) {
+                throw new IOException(e);
+            }
+        } catch (final XMLParserException e) {
+            throw new IOException(e);
+        }
+    }
+//Checkstyle: CyclomaticComplexity|MethodLength ON
+    
+    /** Factory for XML-backed store. */
+    public static class XMLClientStorageServiceStoreFactory extends AbstractInitializableComponent implements Factory {
+
+        /** ParserPool to pass into stores. */
+        @Nonnull private final ParserPool parserPool;
+        
+        /** Constructor. */
+        public XMLClientStorageServiceStoreFactory() {
+            parserPool = new BasicParserPool();
+        }
+        
+        /** {@inheritDoc} */
+        @Override
+        protected void doInitialize() throws ComponentInitializationException {
+            // TODO Auto-generated method stub
+            super.doInitialize();
+            
+            ((BasicParserPool) parserPool).setNamespaceAware(false);
+            ((BasicParserPool) parserPool).initialize();
+        }
+        
+        /** {@inheritDoc} */
+        @Override
+        protected void doDestroy() {
+            ((BasicParserPool) parserPool).destroy();
+        }
+
+        /** {@inheritDoc} */
+        @Nonnull public ClientStorageServiceStore load(@Nullable @NotEmpty final String raw,
+                @Nonnull final ClientStorageSource src) {
+            final ClientStorageServiceStore store = new XMLClientStorageServiceStore(parserPool);
+            store.load(raw, src);
+            return store;
+        }
+    }
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list