Store data model CND files
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 10 Dec 2012 12:27:11 +0000 (12:27 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 10 Dec 2012 12:27:11 +0000 (12:27 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@5942 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

base/runtime/org.argeo.util/src/main/java/org/argeo/util/security/DigestUtils.java
server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml
server/modules/org.argeo.node.repo.jackrabbit/noderepo.properties
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java

index ade5b1e0112bd0afc8295bc147d86fbde7907662..b6aae6fd4988925b62da9d060bc9958f93d91c3e 100644 (file)
@@ -33,6 +33,19 @@ public class DigestUtils {
        // TODO: make it writable
        private final static Integer byteBufferCapacity = 100 * 1024;// 100 KB
 
+       public static String digest(String algorithm, byte[] bytes) {
+               try {
+                       MessageDigest digest = MessageDigest.getInstance(algorithm);
+                       digest.update(bytes);
+                       byte[] checksum = digest.digest();
+                       String res = StreamUtils.encodeHexString(checksum);
+                       return res;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot digest with algorithm "
+                                       + algorithm, e);
+               }
+       }
+
        public static String digest(String algorithm, InputStream in) {
                try {
                        MessageDigest digest = MessageDigest.getInstance(algorithm);
index b21d705ca916db10ef5d7817dba265f0659a00fe..6648c155045a5ad129f1b8fb46b625267ebd3479 100644 (file)
@@ -16,6 +16,7 @@
                <property name="homeDirectory" value="${argeo.node.repo.home}" />
                <property name="configuration" value="${argeo.node.repo.configuration}" />
                <property name="variables" value="osgibundle:/noderepo.properties" />
+               <property name="forceCndImport" value="${argeo.node.repo.forceCndImport}" />
        </bean>
 
        <bean id="repositoryFactory" class="org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory">
index 7f01f8a9de86503e0e23cc4a31199cb75a1dc548..c91d51d08ca77c723c44987d563de711b4b478e0 100644 (file)
@@ -1,6 +1,7 @@
 # Workspace used by the node session
 argeo.node.repo.defaultWorkspace=main
 #argeo.node.repo.securityWorkspace=security
+argeo.node.repo.forceCndImport=true
 
 # Repository base directory
 argeo.node.repo.home=${osgi.instance.area}/node
index 87d62872a30ff30b70cd5fcd33aabe7da5afae26..9060b585a59bee58a8615b910a11cdfd9934e5cf 100644 (file)
@@ -52,7 +52,7 @@ import org.xml.sax.InputSource;
  */
 public class JackrabbitContainer extends JackrabbitWrapper implements
                MaintainedRepository {
-       private Log log = LogFactory.getLog(JackrabbitContainer.class);
+       private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
 
        // local
        private Resource configuration;
index 669da15a12cb3c7631b892cbb3c464fa7a9d37b3..1505b1cfe9763d50f25c69e7cdda849fe3b1e9f9 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.argeo.jackrabbit;
 
+import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -29,6 +30,7 @@ import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.Repository;
 import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
 
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
@@ -42,6 +44,7 @@ import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.ArgeoTypes;
 import org.argeo.jcr.JcrRepositoryWrapper;
 import org.argeo.jcr.JcrUtils;
+import org.argeo.util.security.DigestUtils;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -57,7 +60,8 @@ import org.springframework.core.io.ResourceLoader;
  */
 public class JackrabbitWrapper extends JcrRepositoryWrapper implements
                ResourceLoaderAware {
-       private Log log = LogFactory.getLog(JackrabbitWrapper.class);
+       private final static Log log = LogFactory.getLog(JackrabbitWrapper.class);
+       private final static String DIGEST_ALGORITHM = "MD5";
 
        // local
        private ResourceLoader resourceLoader;
@@ -69,7 +73,7 @@ public class JackrabbitWrapper extends JcrRepositoryWrapper implements
         * Always import CNDs. Useful during development of new data models. In
         * production, explicit migration processes should be used.
         */
-       private Boolean forceCndImport = false;
+       private Boolean forceCndImport = true;
 
        /** Namespaces to register: key is prefix, value namespace */
        private Map<String, String> namespaces = new HashMap<String, String>();
@@ -120,152 +124,172 @@ public class JackrabbitWrapper extends JcrRepositoryWrapper implements
 
                        // load CND files from classpath or as URL
                        for (String resUrl : cndFiles) {
-                               URL url;
-                               Bundle dataModelBundle = null;
-
-                               {// TODO put this in a separate method
-                                       boolean classpath;
-                                       // normalize URL
-                                       if (bundleContext != null
-                                                       && resUrl.startsWith("classpath:")) {
-                                               resUrl = resUrl.substring("classpath:".length());
-                                               classpath = true;
-                                       } else if (resUrl.indexOf(':') < 0) {
-                                               if (!resUrl.startsWith("/")) {
-                                                       resUrl = "/" + resUrl;
-                                                       log.warn("Classpath should start with '/'");
-                                               }
-                                               // resUrl = "classpath:" + resUrl;
-                                               classpath = true;
-                                       } else {
-                                               classpath = false;
-                                       }
+                               processCndFile(session, resUrl);
+                       }
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot import node type definitions "
+                                       + cndFiles, e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
 
-                                       if (classpath) {
-                                               if (bundleContext != null) {
-                                                       Bundle currentBundle = bundleContext.getBundle();
-                                                       url = currentBundle.getResource(resUrl);
-                                                       if (url != null) {// found
-                                                               dataModelBundle = findDataModelBundle(resUrl);
-                                                       }
-                                               } else {
-                                                       resUrl = "classpath:" + resUrl;
-                                                       url = null;
-                                                       // url =
-                                                       // getClass().getClassLoader().getResource(resUrl);
-                                                       // if (url == null)
-                                                       // url = Thread.currentThread()
-                                                       // .getContextClassLoader()
-                                                       // .getResource(resUrl);
-                                               }
-                                       } else if (!resUrl.startsWith("classpath:")) {
-                                               url = new URL(resUrl);
-                                       } else {
-                                               url = null;
-                                       }
-                               }
-                               // check existing data model nodes
-                               new NamespaceHelper(session).registerNamespace(
-                                               ArgeoNames.ARGEO, ArgeoNames.ARGEO_NAMESPACE);
-                               if (!session
-                                               .itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH))
-                                       JcrUtils.mkdirs(session,
-                                                       ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
-                               Node dataModels = session
-                                               .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
-                               NodeIterator it = dataModels.getNodes();
-                               Node dataModel = null;
-                               while (it.hasNext()) {
-                                       Node node = it.nextNode();
-                                       if (node.getProperty(ArgeoNames.ARGEO_URI).getString()
-                                                       .equals(resUrl)) {
-                                               dataModel = node;
-                                               break;
-                                       }
+       }
+
+       protected void processCndFile(Session session, String resUrl) {
+               Reader reader = null;
+               try {
+                       // check existing data model nodes
+                       new NamespaceHelper(session).registerNamespace(ArgeoNames.ARGEO,
+                                       ArgeoNames.ARGEO_NAMESPACE);
+                       if (!session.itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH))
+                               JcrUtils.mkdirs(session,
+                                               ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
+                       Node dataModels = session
+                                       .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH);
+                       NodeIterator it = dataModels.getNodes();
+                       Node dataModel = null;
+                       while (it.hasNext()) {
+                               Node node = it.nextNode();
+                               if (node.getProperty(ArgeoNames.ARGEO_URI).getString()
+                                               .equals(resUrl)) {
+                                       dataModel = node;
+                                       break;
                                }
+                       }
 
-                               // does nothing if data model already registered
-                               if (dataModel != null && !forceCndImport) {
-                                       if (dataModelBundle != null) {
-                                               String version = dataModel.getProperty(
-                                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION)
-                                                               .getString();
-                                               String dataModelBundleVersion = dataModelBundle
-                                                               .getVersion().toString();
-                                               if (!version.equals(dataModelBundleVersion)) {
-                                                       log.warn("Data model with version "
-                                                                       + dataModelBundleVersion
-                                                                       + " available, current version is "
-                                                                       + version);
-                                               }
+                       byte[] cndContent = readCndContent(resUrl);
+                       String newDigest = DigestUtils.digest(DIGEST_ALGORITHM, cndContent);
+                       Bundle bundle = findDataModelBundle(resUrl);
+
+                       String currentVersion = null;
+                       if (dataModel != null) {
+                               currentVersion = dataModel.getProperty(
+                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString();
+                               if (dataModel.hasNode(Node.JCR_CONTENT)) {
+                                       String oldDigest = JcrUtils.checksumFile(dataModel,
+                                                       DIGEST_ALGORITHM);
+                                       if (oldDigest.equals(newDigest)) {
+                                               if (log.isDebugEnabled())
+                                                       log.debug("Data model " + resUrl
+                                                                       + " hasn't changed, keeping version "
+                                                                       + currentVersion);
+                                               return;
                                        }
-                                       // do not implicitly update
-                                       return;
                                }
+                       }
 
-                               InputStream in = null;
-                               Reader reader = null;
-                               try {
-                                       if (url != null) {
-                                               in = url.openStream();
-                                       } else if (resourceLoader != null) {
-                                               Resource res = resourceLoader.getResource(resUrl);
-                                               in = res.getInputStream();
-                                               url = res.getURL();
-                                       } else {
-                                               throw new ArgeoException("No " + resUrl
-                                                               + " in the classpath,"
-                                                               + " make sure the containing"
-                                                               + " package is visible.");
-                                       }
+                       if (dataModel != null && !forceCndImport) {
+                               log.info("Data model "
+                                               + resUrl
+                                               + " has changed since version "
+                                               + currentVersion
+                                               + (bundle != null ? ": version " + bundle.getVersion()
+                                                               + ", bundle " + bundle.getSymbolicName() : ""));
+                               return;
+                       }
 
-                                       reader = new InputStreamReader(in);
-                                       // actually imports the CND
-                                       CndImporter.registerNodeTypes(reader, session, true);
-
-                                       // FIXME: what if argeo.cnd would not be the first called on
-                                       // a new repo? argeo:dataModel would not be found
-                                       String fileName = FilenameUtils.getName(url.getPath());
-                                       if (dataModel == null) {
-                                               dataModel = dataModels.addNode(fileName);
-                                               dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL);
-                                               dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl);
-                                       } else {
-                                               session.getWorkspace().getVersionManager()
-                                                               .checkout(dataModel.getPath());
-                                       }
-                                       if (dataModelBundle != null)
-                                               dataModel.setProperty(
-                                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION,
-                                                               dataModelBundle.getVersion().toString());
-                                       else
-                                               dataModel.setProperty(
-                                                               ArgeoNames.ARGEO_DATA_MODEL_VERSION, "0.0.0");
-                                       JcrUtils.updateLastModified(dataModel);
-                                       session.save();
-                                       session.getWorkspace().getVersionManager()
-                                                       .checkin(dataModel.getPath());
-                               } finally {
-                                       IOUtils.closeQuietly(in);
-                                       IOUtils.closeQuietly(reader);
-                               }
+                       reader = new InputStreamReader(new ByteArrayInputStream(cndContent));
+                       // actually imports the CND
+                       CndImporter.registerNodeTypes(reader, session, true);
+
+                       if (dataModel != null & !dataModel.isNodeType(NodeType.NT_FILE)) {
+                               dataModel.remove();
+                               dataModel = null;
+                       }
 
-                               if (log.isDebugEnabled())
-                                       log.debug("Data model "
-                                                       + resUrl
-                                                       + (dataModelBundle != null ? ", version "
-                                                                       + dataModelBundle.getVersion()
-                                                                       + ", bundle "
-                                                                       + dataModelBundle.getSymbolicName() : ""));
+                       // FIXME: what if argeo.cnd would not be the first called on
+                       // a new repo? argeo:dataModel would not be found
+                       String fileName = FilenameUtils.getName(resUrl);
+                       if (dataModel == null) {
+                               dataModel = dataModels.addNode(fileName, NodeType.NT_FILE);
+                               dataModel.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+                               dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL);
+                               dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl);
+                       } else {
+                               session.getWorkspace().getVersionManager()
+                                               .checkout(dataModel.getPath());
                        }
+                       if (bundle != null)
+                               dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+                                               bundle.getVersion().toString());
+                       else
+                               dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+                                               "0.0.0");
+                       JcrUtils.copyBytesAsFile(dataModel.getParent(), fileName,
+                                       cndContent);
+                       JcrUtils.updateLastModified(dataModel);
+                       session.save();
+                       session.getWorkspace().getVersionManager()
+                                       .checkin(dataModel.getPath());
+
+                       if (currentVersion == null)
+                               log.info("Data model "
+                                               + resUrl
+                                               + (bundle != null ? ", version " + bundle.getVersion()
+                                                               + ", bundle " + bundle.getSymbolicName() : ""));
+                       else
+                               log.info("Data model "
+                                               + resUrl
+                                               + " updated from version "
+                                               + currentVersion
+                                               + (bundle != null ? ", version " + bundle.getVersion()
+                                                               + ", bundle " + bundle.getSymbolicName() : ""));
                } catch (Exception e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoException("Cannot import node type definitions "
-                                       + cndFiles, e);
+                       throw new ArgeoException("Cannot process data model " + resUrl, e);
                } finally {
-                       JcrUtils.logoutQuietly(session);
+                       IOUtils.closeQuietly(reader);
                }
+       }
+
+       protected byte[] readCndContent(String resUrl) {
+               InputStream in = null;
+               try {
+                       boolean classpath;
+                       // normalize URL
+                       if (bundleContext != null && resUrl.startsWith("classpath:")) {
+                               resUrl = resUrl.substring("classpath:".length());
+                               classpath = true;
+                       } else if (resUrl.indexOf(':') < 0) {
+                               if (!resUrl.startsWith("/")) {
+                                       resUrl = "/" + resUrl;
+                                       log.warn("Classpath should start with '/'");
+                               }
+                               classpath = true;
+                       } else {
+                               classpath = false;
+                       }
+
+                       URL url = null;
+                       if (classpath) {
+                               if (bundleContext != null) {
+                                       Bundle currentBundle = bundleContext.getBundle();
+                                       url = currentBundle.getResource(resUrl);
+                               } else {
+                                       resUrl = "classpath:" + resUrl;
+                                       url = null;
+                               }
+                       } else if (!resUrl.startsWith("classpath:")) {
+                               url = new URL(resUrl);
+                       }
 
+                       if (url != null) {
+                               in = url.openStream();
+                       } else if (resourceLoader != null) {
+                               Resource res = resourceLoader.getResource(resUrl);
+                               in = res.getInputStream();
+                               url = res.getURL();
+                       } else {
+                               throw new ArgeoException("No " + resUrl + " in the classpath,"
+                                               + " make sure the containing" + " package is visible.");
+                       }
+
+                       return IOUtils.toByteArray(in);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot read CND from " + resUrl, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
        }
 
        /*