This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.jcr.contentloader-2.1.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-contentloader.git
commit 06c581e216bceffbea0143e859e8ffe80dae7894 Author: Eric Norman <[email protected]> AuthorDate: Sun Jun 27 19:36:49 2010 +0000 SLING-1172 Allow uploading JSON files to create content structures git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/jcr/contentloader@958411 13f79535-47bb-0310-9956-ffa450edef68 --- .../jcr/contentloader/ContentImportListener.java | 67 ++++++ .../sling/jcr/contentloader/ContentImporter.java | 53 +++++ .../ContentReader.java => ImportOptions.java} | 25 +-- .../contentloader/internal/BaseImportLoader.java | 54 +++++ .../internal/ContentLoaderService.java | 4 +- .../jcr/contentloader/internal/ContentReader.java | 9 + .../internal/DefaultContentCreator.java | 76 ++++++- .../internal/DefaultContentImporter.java | 238 +++++++++++++++++++++ .../{ContentReader.java => JcrContentHelper.java} | 31 +-- .../sling/jcr/contentloader/internal/Loader.java | 36 +--- .../jcr/contentloader/internal/PathEntry.java | 12 +- .../contentloader/internal/readers/JsonReader.java | 3 +- .../contentloader/internal/readers/XmlReader.java | 25 ++- .../contentloader/internal/readers/ZipReader.java | 16 +- 14 files changed, 569 insertions(+), 80 deletions(-) diff --git a/src/main/java/org/apache/sling/jcr/contentloader/ContentImportListener.java b/src/main/java/org/apache/sling/jcr/contentloader/ContentImportListener.java new file mode 100644 index 0000000..b27735c --- /dev/null +++ b/src/main/java/org/apache/sling/jcr/contentloader/ContentImportListener.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.sling.jcr.contentloader; + +/** + * Listener interface to provide callbacks for all imported updates + * for interested parties. This is primarily used to record + * the modifications during the "import" post operation. + */ +public interface ContentImportListener { + + /** + * Content has been updated. The source path provides the path of + * the modified Item. + */ + void onModify(String srcPath); + + /** + * An Item has been deleted. The source path provides the path of the + * deleted Item. + */ + void onDelete(String srcPath); + + /** + * An Item has been moved to a new location. The source provides the + * original path of the Item, the destination provides the new path of the + * Item. + */ + void onMove(String srcPath, String destPath); + + /** + * An Item has been copied to a new location. The source path provides the + * path of the copied Item, the destination path provides the path of the + * new Item. + */ + void onCopy(String srcPath, String destPath); + + /** + * A Node has been created. The source path provides the path of the newly + * created Node. + */ + void onCreate(String srcPath); + + /** + * A child Node has been reordered. The orderedPath provides the path of the + * node, which has been reordered. ThebeforeSibbling provides the name of + * the sibling node before which the source Node has been ordered. + */ + void onReorder(String orderedPath, String beforeSibbling); + +} diff --git a/src/main/java/org/apache/sling/jcr/contentloader/ContentImporter.java b/src/main/java/org/apache/sling/jcr/contentloader/ContentImporter.java new file mode 100644 index 0000000..e780324 --- /dev/null +++ b/src/main/java/org/apache/sling/jcr/contentloader/ContentImporter.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.sling.jcr.contentloader; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + + +/** + * The <code>ContentImporter</code> service + * <p> + * This interface is not intended to be implemented by bundles. It is + * implemented by this bundle and may be used by client bundles. + * </p> + */ +public interface ContentImporter { + + /** + * Import content into the repository by parsing the provided + * content stream. + * + * @param parent the root node for the imported content + * @param name the name of the imported content. The file extension determines the content type + * @param contentStream the content stream to be imported + * @param importOptions (optional) additional options to control the import + * @param importListener (optional) listener to receive callbacks for each change in the import + * @throws RepositoryException + * @throws IOException + */ + void importContent(Node parent, String name, + InputStream contentStream, ImportOptions importOptions, + ContentImportListener importListener) throws RepositoryException, IOException; + +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java b/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java similarity index 60% copy from src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java copy to src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java index 14bdbc0..fa3d53c 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java @@ -16,25 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sling.jcr.contentloader.internal; +package org.apache.sling.jcr.contentloader; -import java.io.IOException; -import java.net.URL; - -import javax.jcr.RepositoryException; /** - * The <code>ContentReader</code> - * A content reader is provided by an {@link ImportProvider}. + * Encapsulates the options for the content import. */ -public interface ContentReader { +public abstract class ImportOptions { + + public abstract boolean isOverwrite(); + + public abstract boolean isCheckin(); - /** - * Read the content from the URL and create the - * content using the provided content creator. - * @param url The input stream. - * @throws IOException - */ - void parse(URL url, ContentCreator creator) throws IOException, RepositoryException; + public abstract boolean isIgnoredImportProvider(String extension); -} +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/BaseImportLoader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/BaseImportLoader.java new file mode 100644 index 0000000..dd8c2f4 --- /dev/null +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/BaseImportLoader.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.sling.jcr.contentloader.internal; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.sling.jcr.contentloader.internal.readers.JsonReader; +import org.apache.sling.jcr.contentloader.internal.readers.XmlReader; +import org.apache.sling.jcr.contentloader.internal.readers.ZipReader; + +/** + * Base class that takes care of the details that are common to bundle content + * loader and the POST operation "import" loader. + */ +public abstract class BaseImportLoader { + public static final String EXT_XML = ".xml"; + public static final String EXT_JCR_XML = ".jcr.xml"; + public static final String EXT_JSON = ".json"; + public static final String EXT_JAR = ".jar"; + public static final String EXT_ZIP = ".zip"; + + /** All available import providers. */ + Map<String, ImportProvider> defaultImportProviders; + + public BaseImportLoader() { + defaultImportProviders = new LinkedHashMap<String, ImportProvider>(); + defaultImportProviders.put(EXT_JCR_XML, null); + defaultImportProviders.put(EXT_JSON, JsonReader.PROVIDER); + defaultImportProviders.put(EXT_XML, XmlReader.PROVIDER); + defaultImportProviders.put(EXT_JAR, ZipReader.JAR_PROVIDER); + defaultImportProviders.put(EXT_ZIP, ZipReader.ZIP_PROVIDER); + } + + public void dispose() { + defaultImportProviders = null; + } +} diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java index 2d68b2c..b023d68 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java @@ -58,7 +58,7 @@ import org.slf4j.LoggerFactory; * Content Loader Implementation" * @scr.property name="service.vendor" value="The Apache Software Foundation" */ -public class ContentLoaderService implements SynchronousBundleListener { +public class ContentLoaderService implements SynchronousBundleListener, JcrContentHelper { /** The manifest header to specify the workspace for initial content loading. */ public static final String CONTENT_WORKSPACE_HEADER = "Sling-Initial-Content-Workspace"; @@ -220,7 +220,7 @@ public class ContentLoaderService implements SynchronousBundleListener { * @return the digested value * @throws IllegalArgumentException */ - protected String digestPassword(String pwd) throws IllegalArgumentException { + public String digestPassword(String pwd) throws IllegalArgumentException { try { StringBuffer password = new StringBuffer(); password.append("{").append(passwordDigestAlgoritm).append("}"); diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java index 14bdbc0..a7b3dfc 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java @@ -19,6 +19,7 @@ package org.apache.sling.jcr.contentloader.internal; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import javax.jcr.RepositoryException; @@ -37,4 +38,12 @@ public interface ContentReader { */ void parse(URL url, ContentCreator creator) throws IOException, RepositoryException; + /** + * Read the content from the input stream and create the + * content using the provided content creator. + * @param ins the input stream. + * @throws IOException + */ + void parse(InputStream ins, ContentCreator contentCreator) throws IOException, RepositoryException; + } diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java index b1bc0e3..3c5565d 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java @@ -24,6 +24,10 @@ import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.sling.jcr.base.util.AccessControlUtil; +import org.apache.sling.jcr.contentloader.ContentImportListener; +import org.apache.sling.jcr.contentloader.ImportOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -59,7 +63,10 @@ import javax.jcr.ValueFactory; */ public class DefaultContentCreator implements ContentCreator { - private PathEntry configuration; + /** default log */ + final Logger log = LoggerFactory.getLogger(getClass()); + + private ImportOptions configuration; private final Pattern jsonDatePattern = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}[-+]{1}[0-9]{2}[:]{0,1}[0-9]{2}$"); private final SimpleDateFormat jsonDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); @@ -85,7 +92,7 @@ public class DefaultContentCreator implements ContentCreator { private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream"; /** Helper class to get the mime type of a file. */ - private final ContentLoaderService jcrContentHelper; + private final JcrContentHelper jcrContentHelper; /** List of active import providers mapped by extension. */ private Map<String, ImportProvider> importProviders; @@ -93,6 +100,9 @@ public class DefaultContentCreator implements ContentCreator { /** Optional list of created nodes (for uninstall) */ private List<String> createdNodes; + /** Optional listener to get notified about changes */ + private ContentImportListener importListener; + /** * A one time use seed to randomize the user location. */ @@ -107,7 +117,7 @@ public class DefaultContentCreator implements ContentCreator { * Constructor. * @param jcrContentHelper Helper class to get the mime type of a file */ - public DefaultContentCreator(ContentLoaderService jcrContentHelper) { + public DefaultContentCreator(JcrContentHelper jcrContentHelper) { this.jcrContentHelper = jcrContentHelper; } @@ -117,9 +127,10 @@ public class DefaultContentCreator implements ContentCreator { * @param defaultImportProviders List of all import providers. * @param createdNodes Optional list to store new nodes (for uninstall) */ - public void init(final PathEntry pathEntry, + public void init(final ImportOptions pathEntry, final Map<String, ImportProvider> defaultImportProviders, - final List<String> createdNodes) { + final List<String> createdNodes, + final ContentImportListener importListener) { this.configuration = pathEntry; // create list of allowed import providers this.importProviders = new HashMap<String, ImportProvider>(); @@ -131,6 +142,7 @@ public class DefaultContentCreator implements ContentCreator { } } this.createdNodes = createdNodes; + this.importListener = importListener; } /** @@ -254,7 +266,9 @@ public class DefaultContentCreator implements ContentCreator { if ( this.createdNodes != null ) { this.createdNodes.add(node.getPath()); } - + if ( this.importListener != null ) { + this.importListener.onCreate(node.getPath()); + } } else { // explicit primary node type @@ -262,6 +276,9 @@ public class DefaultContentCreator implements ContentCreator { if ( this.createdNodes != null ) { this.createdNodes.add(node.getPath()); } + if ( this.importListener != null ) { + this.importListener.onCreate(node.getPath()); + } } // ammend mixin node types @@ -305,6 +322,10 @@ public class DefaultContentCreator implements ContentCreator { String uuid = getUUID(node.getSession(), propPath, getAbsPath(node, value)); if (uuid != null) { node.setProperty(name, uuid, propertyType); + + if ( this.importListener != null ) { + this.importListener.onCreate(node.getProperty(name).getPath()); + } } } else if ("jcr:isCheckedOut".equals(name)) { @@ -325,12 +346,18 @@ public class DefaultContentCreator implements ContentCreator { // Fall back to default behaviour if this fails node.setProperty(name, value, propertyType); } + if ( this.importListener != null ) { + this.importListener.onCreate(node.getProperty(name).getPath()); + } } else { if (propertyType == PropertyType.UNDEFINED) { node.setProperty(name, value); } else { node.setProperty(name, value, propertyType); } + if ( this.importListener != null ) { + this.importListener.onCreate(node.getProperty(name).getPath()); + } } } @@ -358,6 +385,9 @@ public class DefaultContentCreator implements ContentCreator { } node.setProperty(name, uuids, propertyType); + if ( this.importListener != null ) { + this.importListener.onCreate(node.getProperty(name).getPath()); + } if (!hasAll) { delayedMultipleReferences.put(propPath, uuidOrPaths); @@ -376,16 +406,21 @@ public class DefaultContentCreator implements ContentCreator { } catch (ParseException e) { // If this failes, fallback to the default - jcrContentHelper.log.warn("Could not create dates for property, fallingback to defaults", e); + log.warn("Could not create dates for property, fallingback to defaults", e); node.setProperty(name, values, propertyType); } - + if ( this.importListener != null ) { + this.importListener.onCreate(node.getProperty(name).getPath()); + } } else { if (propertyType == PropertyType.UNDEFINED) { node.setProperty(name, values); } else { node.setProperty(name, values, propertyType); } + if ( this.importListener != null ) { + this.importListener.onCreate(node.getProperty(name).getPath()); + } } } @@ -515,12 +550,18 @@ public class DefaultContentCreator implements ContentCreator { } } parentNode.setProperty(name, uuids, PropertyType.REFERENCE); + if ( this.importListener != null ) { + this.importListener.onCreate(parentNode.getProperty(name).getPath()); + } if (hasAll) { delayedMultipleReferences.remove(property); } } else { parentNode.setProperty(name, uuid, PropertyType.REFERENCE); + if ( this.importListener != null ) { + this.importListener.onCreate(parentNode.getProperty(name).getPath()); + } } } } @@ -594,11 +635,18 @@ public class DefaultContentCreator implements ContentCreator { } if ( value == null ) { if ( node.hasProperty(name) ) { + String propPath = node.getProperty(name).getPath(); node.getProperty(name).remove(); + if ( this.importListener != null ) { + this.importListener.onDelete(propPath); + } } } else { final Value jcrValue = this.createValue(node.getSession().getValueFactory(), value); node.setProperty(name, jcrValue); + if ( this.importListener != null ) { + this.importListener.onModify(node.getProperty(name).getPath()); + } } } @@ -612,7 +660,11 @@ public class DefaultContentCreator implements ContentCreator { } if ( values == null || values.length == 0 ) { if ( node.hasProperty(name) ) { + String propPath = node.getProperty(name).getPath(); node.getProperty(name).remove(); + if ( this.importListener != null ) { + this.importListener.onDelete(propPath); + } } } else { final Value[] jcrValues = new Value[values.length]; @@ -620,6 +672,9 @@ public class DefaultContentCreator implements ContentCreator { jcrValues[i] = this.createValue(node.getSession().getValueFactory(), values[i]); } node.setProperty(name, jcrValues); + if ( this.importListener != null ) { + this.importListener.onModify(node.getProperty(name).getPath()); + } } } @@ -651,7 +706,7 @@ public class DefaultContentCreator implements ContentCreator { if (mimeType == null) { mimeType = jcrContentHelper.getMimeType(name); if (mimeType == null) { - jcrContentHelper.log.info( + log.info( "createFile: Cannot find content type for {}, using {}", name, DEFAULT_CONTENT_TYPE); mimeType = DEFAULT_CONTENT_TYPE; @@ -687,6 +742,9 @@ public class DefaultContentCreator implements ContentCreator { if ( this.createdNodes != null ) { this.createdNodes.add(n.getPath()); } + if ( this.importListener != null ) { + this.importListener.onCreate(node.getPath()); + } } node = node.getNode(token); } diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java new file mode 100644 index 0000000..c974fd7 --- /dev/null +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.sling.jcr.contentloader.internal; + +import static javax.jcr.ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; + +import javax.jcr.InvalidSerializedDataException; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.sling.commons.mime.MimeTypeService; +import org.apache.sling.jcr.contentloader.ContentImportListener; +import org.apache.sling.jcr.contentloader.ContentImporter; +import org.apache.sling.jcr.contentloader.ImportOptions; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The <code>DefaultContentImporter</code> is the default implementation of the + * ContentImporter service providing the following functionality: + * <ul> + * <li>Import content into the content repository. + * </ul> + * + * @scr.component immediate="false" label="%content.import.service.name" + * description="%content.import.service.description" + * @scr.property name="service.vendor" value="The Apache Software Foundation" + * @scr.property name="service.description" + * value="Apache Sling Contnet Importer Service" + * @scr.service interface="org.apache.sling.jcr.contentloader.ContentImporter" + */ +public class DefaultContentImporter extends BaseImportLoader implements JcrContentHelper, ContentImporter { + + /** default log */ + private final Logger log = LoggerFactory.getLogger(DefaultContentImporter.class); + + /** + * The MimeTypeService used by the initial content initialContentLoader to + * resolve MIME types for files to be installed. + * + * @scr.reference + */ + private MimeTypeService mimeTypeService; + + /** + * To be used for the encryption. E.g. for passwords in + * {@link javax.jcr.SimpleCredentials#getPassword()} SimpleCredentials} + * + * @scr.property valueRef="DEFAULT_PASSWORD_DIGEST_ALGORITHM" + */ + private static final String PROP_PASSWORD_DIGEST_ALGORITHM = "password.digest.algorithm"; + private static final String DEFAULT_PASSWORD_DIGEST_ALGORITHM = "sha1"; + private String passwordDigestAlgoritm = null; + + + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.ContentImporter#importContent(javax.jcr.Node, java.lang.String, java.io.InputStream, org.apache.sling.jcr.contentloader.ImportOptions, org.apache.sling.jcr.contentloader.ContentImportListener) + */ + public void importContent(Node parent, String name, + InputStream contentStream, ImportOptions importOptions, + ContentImportListener importListener) throws RepositoryException, IOException { + + // special treatment for system view imports + if (name.endsWith(EXT_JCR_XML)) { + Node node = importSystemView(parent, name, contentStream); + if (node != null) { + if (importListener != null) { + importListener.onCreate(node.getPath()); + } + return; + } + } + + DefaultContentCreator contentCreator = new DefaultContentCreator(this); + List<String> createdPaths = new ArrayList<String>(); + contentCreator.init(importOptions, this.defaultImportProviders, createdPaths, importListener); + + contentCreator.prepareParsing(parent, toPlainName(contentCreator, name)); + + final ImportProvider ip = contentCreator.getImportProvider(name); + ContentReader reader = ip.getReader(); + reader.parse(contentStream, contentCreator); + + //save changes + Session session = parent.getSession(); + session.save(); + + // finally checkin versionable nodes + for (final Node versionable : contentCreator.getVersionables()) { + versionable.checkin(); + } + } + + private String toPlainName(DefaultContentCreator contentCreator, String name) { + final String providerExt = contentCreator.getImportProviderExtension(name); + if (providerExt != null) { + return name.substring(0, name.length() - providerExt.length()); + } + return name; + } + + + /** + * Import the XML file as JCR system or document view import. If the XML + * file is not a valid system or document view export/import file, + * <code>false</code> is returned. + * + * @param parent The parent node below which to import + * @param name the name of the import resource + * @param contentStream The XML content to import + * @return <code>true</code> if the import succeeds, <code>false</code> + * if the import fails due to XML format errors. + * @throws IOException If an IO error occurrs reading the XML file. + */ + private Node importSystemView(Node parent, String name, InputStream contentStream) + throws IOException { + InputStream ins = null; + try { + + // check whether we have the content already, nothing to do then + if ( name.endsWith(EXT_JCR_XML) ) { + name = name.substring(0, name.length() - EXT_JCR_XML.length()); + } + if (parent.hasNode(name)) { + log.debug( + "importSystemView: Node {} for XML already exists, nothing to to", + name); + return parent.getNode(name); + } + + ins = contentStream; + Session session = parent.getSession(); + session.importXML(parent.getPath(), ins, IMPORT_UUID_CREATE_NEW); + + // additionally check whether the expected child node exists + return (parent.hasNode(name)) ? parent.getNode(name) : null; + } catch (InvalidSerializedDataException isde) { + + // the xml might not be System or Document View export, fall back + // to old-style XML reading + log.info( + "importSystemView: XML does not seem to be system view export, trying old style; cause: {}", + isde.toString()); + return null; + } catch (RepositoryException re) { + + // any other repository related issue... + log.info( + "importSystemView: Repository issue loading XML, trying old style; cause: {}", + re.toString()); + return null; + } finally { + if (ins != null) { + try { + ins.close(); + } catch (IOException ignore) { + // ignore + } + } + } + } + + + // ---------- JcrContentHelper implementation --------------------------------------------- + + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.internal.JcrContentHelper#digestPassword(java.lang.String) + */ + public String digestPassword(String pwd) throws IllegalArgumentException { + try { + StringBuffer password = new StringBuffer(); + password.append("{").append(passwordDigestAlgoritm).append("}"); + password.append(DefaultContentCreator.digest(passwordDigestAlgoritm, + pwd.getBytes("UTF-8"))); + return password.toString(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e.toString()); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.toString()); + } + } + + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.internal.JcrContentHelper#getMimeType(java.lang.String) + */ + public String getMimeType(String name) { + // local copy to not get NPE despite check for null due to concurrent + // unbind + MimeTypeService mts = mimeTypeService; + return (mts != null) ? mts.getMimeType(name) : null; + } + + + // ---------- SCR Integration --------------------------------------------- + + /** Activates this component, called by SCR before registering as a service */ + protected void activate(ComponentContext componentContext) { + Dictionary<?, ?> props = componentContext.getProperties(); + Object propValue = props.get(PROP_PASSWORD_DIGEST_ALGORITHM); + if (propValue instanceof String) { + passwordDigestAlgoritm = (String) propValue; + } else { + passwordDigestAlgoritm = DEFAULT_PASSWORD_DIGEST_ALGORITHM; + } + } + + /** Deativates this component, called by SCR to take out of service */ + protected void deactivate(ComponentContext componentContext) { + passwordDigestAlgoritm = null; + } + +} diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/JcrContentHelper.java similarity index 62% copy from src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java copy to src/main/java/org/apache/sling/jcr/contentloader/internal/JcrContentHelper.java index 14bdbc0..734bf59 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/JcrContentHelper.java @@ -18,23 +18,26 @@ */ package org.apache.sling.jcr.contentloader.internal; -import java.io.IOException; -import java.net.URL; - -import javax.jcr.RepositoryException; /** - * The <code>ContentReader</code> - * A content reader is provided by an {@link ImportProvider}. + * Interface to provide helpers for the ContentCreator + * */ -public interface ContentReader { +public interface JcrContentHelper { + + /** + * Returns the MIME type from the MimeTypeService for the given name + * @param name the name of the file to get the mimeType for + */ + String getMimeType(String name); - /** - * Read the content from the URL and create the - * content using the provided content creator. - * @param url The input stream. - * @throws IOException - */ - void parse(URL url, ContentCreator creator) throws IOException, RepositoryException; + /** + * Digest the given password using the configured digest algorithm + * + * @param pwd the password to digest + * @return digested password + * @throws IllegalArgumentException + */ + String digestPassword(String pwd) throws IllegalArgumentException; } diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/Loader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/Loader.java index b52fb82..d00bb9e 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/Loader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/Loader.java @@ -31,7 +31,6 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -44,9 +43,7 @@ import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; -import org.apache.sling.jcr.contentloader.internal.readers.JsonReader; -import org.apache.sling.jcr.contentloader.internal.readers.XmlReader; -import org.apache.sling.jcr.contentloader.internal.readers.ZipReader; +import org.apache.sling.jcr.contentloader.ImportOptions; import org.osgi.framework.Bundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,17 +51,7 @@ import org.slf4j.LoggerFactory; /** * The <code>Loader</code> loads initial content from the bundle. */ -public class Loader { - - public static final String EXT_XML = ".xml"; - - public static final String EXT_JCR_XML = ".jcr.xml"; - - public static final String EXT_JSON = ".json"; - - public static final String EXT_JAR = ".jar"; - - public static final String EXT_ZIP = ".zip"; +public class Loader extends BaseImportLoader { public static final String ROOT_DESCRIPTOR = "/ROOT"; @@ -73,25 +60,16 @@ public class Loader { private ContentLoaderService contentLoaderService; - /** All available import providers. */ - private Map<String, ImportProvider> defaultImportProviders; - private final DefaultContentCreator contentCreator; // bundles whose registration failed and should be retried private List<Bundle> delayedBundles; public Loader(ContentLoaderService contentLoaderService) { - this.contentLoaderService = contentLoaderService; + super(); + this.contentLoaderService = contentLoaderService; this.contentCreator = new DefaultContentCreator(contentLoaderService); this.delayedBundles = new LinkedList<Bundle>(); - - defaultImportProviders = new LinkedHashMap<String, ImportProvider>(); - defaultImportProviders.put(EXT_JCR_XML, null); - defaultImportProviders.put(EXT_JSON, JsonReader.PROVIDER); - defaultImportProviders.put(EXT_XML, XmlReader.PROVIDER); - defaultImportProviders.put(EXT_JAR, ZipReader.JAR_PROVIDER); - defaultImportProviders.put(EXT_ZIP, ZipReader.ZIP_PROVIDER); } public void dispose() { @@ -100,7 +78,7 @@ public class Loader { delayedBundles = null; } contentLoaderService = null; - defaultImportProviders = null; + super.dispose(); } /** @@ -369,7 +347,7 @@ public class Loader { final List<String> createdNodes) throws RepositoryException { // init content creator - this.contentCreator.init(configuration, this.defaultImportProviders, createdNodes); + this.contentCreator.init(configuration, this.defaultImportProviders, createdNodes, null); final Map<URL, Node> processedEntries = new HashMap<URL, Node>(); @@ -598,7 +576,7 @@ public class Loader { path = srcPath.substring(0, pos + 1) + name; } - this.contentCreator.init(configuration, defaultImportProviders, createdNodes); + this.contentCreator.init(configuration, defaultImportProviders, createdNodes, null); this.contentCreator.prepareParsing(parent, name); final URLConnection conn = source.openConnection(); final long lastModified = conn.getLastModified(); diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java index 2532ee1..aa003c4 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java @@ -24,12 +24,13 @@ import java.util.List; import java.util.StringTokenizer; import org.apache.sling.commons.osgi.ManifestHeader; +import org.apache.sling.jcr.contentloader.ImportOptions; import org.osgi.framework.Bundle; /** * A path entry from the manifest for initial content. */ -public class PathEntry { +public class PathEntry extends ImportOptions { /** The manifest header to specify initial content to be loaded. */ public static final String CONTENT_HEADER = "Sling-Initial-Content"; @@ -169,6 +170,9 @@ public class PathEntry { return this.path; } + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isOverwrite() + */ public boolean isOverwrite() { return this.overwrite; } @@ -177,10 +181,16 @@ public class PathEntry { return this.uninstall; } + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isCheckin() + */ public boolean isCheckin() { return this.checkin; } + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isIgnoredImportProvider(java.lang.String) + */ public boolean isIgnoredImportProvider(String extension) { if ( extension.startsWith(".") ) { extension = extension.substring(1); diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java index 2a8e599..4889357 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java @@ -156,7 +156,8 @@ public class JsonReader implements ContentReader { } JSONObject json = new JSONObject(jsonString); - this.createNode(null, json, contentCreator); + String optionalName = json.optString("name", null); + this.createNode(optionalName, json, contentCreator); } catch (JSONException je) { throw (IOException) new IOException(je.getMessage()).initCause(je); } diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java index 8cd74e1..5dfad35 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java @@ -172,14 +172,32 @@ public class XmlReader implements ContentReader { } } - private void parseInternal(final InputStream bufferedInput, + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.internal.ContentReader#parse(java.io.InputStream, org.apache.sling.jcr.contentloader.internal.ContentCreator) + */ + public void parse(InputStream ins, ContentCreator creator) + throws IOException, RepositoryException { + BufferedInputStream bufferedInput = null; + try { + // We need to buffer input, so that we can reset the stream if we encounter an XSL stylesheet reference + bufferedInput = new BufferedInputStream(ins); + URL xmlLocation = null; + parseInternal(bufferedInput, creator, xmlLocation); + } catch (XmlPullParserException xppe) { + throw (IOException) new IOException(xppe.getMessage()).initCause(xppe); + } finally { + closeStream(bufferedInput); + } + } + + private void parseInternal(final InputStream bufferedInput, final ContentCreator creator, final URL xmlLocation) throws XmlPullParserException, IOException, RepositoryException { final StringBuilder contentBuffer = new StringBuilder(); // Mark the beginning of the stream. We assume that if there's an XSL processing instruction, // it will occur in the first gulp - which makes sense, as processing instructions must be - // specified before the root elemeent of an XML file. + // specified before the root element of an XML file. bufferedInput.mark(bufferedInput.available()); // set the parser input, use null encoding to force detection with // <?xml?> @@ -594,7 +612,8 @@ public class XmlReader implements ContentReader { */ protected static class AttributeMap extends HashMap<String, String> { - private static final AttributeMap instance = new AttributeMap(); + private static final long serialVersionUID = -6304058237706001104L; + private static final AttributeMap instance = new AttributeMap(); public static AttributeMap getInstance() { return instance; diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java index e4debee..949f186 100644 --- a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java +++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java @@ -73,10 +73,16 @@ public class ZipReader implements ContentReader { * @see org.apache.sling.jcr.contentloader.internal.ContentReader#parse(java.net.URL, org.apache.sling.jcr.contentloader.internal.ContentCreator) */ public void parse(java.net.URL url, ContentCreator creator) - throws IOException, RepositoryException { - InputStream ins = null; + throws IOException, RepositoryException { + parse(url.openStream(), creator); + } + + /** + * @see org.apache.sling.jcr.contentloader.internal.ContentReader#parse(java.io.InputStream, org.apache.sling.jcr.contentloader.internal.ContentCreator) + */ + public void parse(InputStream ins, ContentCreator creator) + throws IOException, RepositoryException { try { - ins = url.openStream(); creator.createNode(null, NT_FOLDER, null); final ZipInputStream zis = new ZipInputStream(ins); ZipEntry entry; @@ -109,6 +115,6 @@ public class ZipReader implements ContentReader { } } } - } - + } + } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
