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.0.6 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-contentloader.git
commit 50de0c92a5eb0a8d10e18818070d1d19cc31417a Author: Vidar Skauge Ramdal <[email protected]> AuthorDate: Tue Oct 27 09:43:56 2009 +0000 SLING-1161 implemented. Now supports including external files with nt:file in content loader XML git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/jcr/contentloader@830106 13f79535-47bb-0310-9956-ffa450edef68 --- .../contentloader/internal/readers/XmlReader.java | 137 ++++++++++++++++++++- .../internal/readers/XmlReaderTest.java | 68 +++++++++- src/test/resources/reader/filesample.xml | 21 ++++ src/test/resources/reader/testfile.txt | 1 + 4 files changed, 220 insertions(+), 7 deletions(-) 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 39a69a8..a4a525a 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 @@ -25,12 +25,18 @@ import java.io.InputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.URL; +import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.text.DateFormat; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; @@ -52,7 +58,7 @@ import org.xmlpull.v1.XmlPullParserException; /** * This reader reads an xml file defining the content. The xml format should have this * format: - * + * * <pre> * <node> * <name>the name of the node</name> @@ -81,6 +87,9 @@ import org.xmlpull.v1.XmlPullParserException; * </nodes> * </node> * </pre> + * + * If you want to include a binary file in your loaded content, you may specify it using a + * {@link org.apache.sling.jcr.contentloader.internal.readers.XmlReader.FileDescription <code><nt:file></code>} element. */ public class XmlReader implements ContentReader { @@ -113,6 +122,9 @@ public class XmlReader implements ContentReader { private static final String HREF_ATTRIBUTE = "href"; + private static final String ELEM_FILE_NAMESPACE = "http://www.jcp.org/jcr/nt/1.0"; + private static final String ELEM_FILE_NAME = "file"; + public static final ImportProvider PROVIDER = new ImportProvider() { private XmlReader xmlReader; @@ -131,6 +143,12 @@ public class XmlReader implements ContentReader { XmlReader() { this.xmlParser = new KXmlParser(); + try { + // Make namespace-aware + this.xmlParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } } // ---------- XML content access ------------------------------------------- @@ -165,6 +183,7 @@ public class XmlReader implements ContentReader { NodeDescription.SHARED.clear(); PropertyDescription.SHARED.clear(); + FileDescription.SHARED.clear(); NodeDescription currentNode = null; PropertyDescription currentProperty = null; @@ -200,6 +219,22 @@ public class XmlReader implements ContentReader { } else if (ELEM_NODE.equals(currentElement)) { currentNode = NodeDescription.create(currentNode, creator); currentNode = NodeDescription.SHARED; + } else if (ELEM_FILE_NAME.equals(currentElement) && ELEM_FILE_NAMESPACE.equals(this.xmlParser.getNamespace())) { + int attributeCount = this.xmlParser.getAttributeCount(); + if (attributeCount < 2 || attributeCount > 3) { + throw new IOException("File element must have these attributes: url, mimeType and lastModified"); + } + try { + AttributeMap attributes = AttributeMap.getInstance(); + attributes.setValues(xmlParser); + FileDescription.SHARED.setBaseLocation(xmlLocation); + FileDescription.SHARED.setValues(attributes); + attributes.clear(); + } catch (ParseException e) { + throw new IOException("Error parsing file description", e); + } + FileDescription.SHARED.create(creator); + FileDescription.SHARED.clear(); } } else if (eventType == XmlPullParser.END_TAG) { @@ -243,7 +278,6 @@ public class XmlReader implements ContentReader { } currentNode.addMixinType(content); } - } else if (eventType == XmlPullParser.TEXT || eventType == XmlPullParser.CDSECT) { contentBuffer.append(this.xmlParser.getText()); } @@ -455,4 +489,103 @@ public class XmlReader implements ContentReader { } } + + /** + * Represents a reference to a file that is to be loaded into the repository. The file is referenced by an + * XML element named <code><nt:file></code>, with the attributes <code>src</code>, + * <code>mimeType</code> and <code>lastModified</code>. <br/><br/>Example: + * <pre> + * <nt:file src="../../image.png" mimeType="image/png" lastModified="1977-06-01T07:00:00+0100" /> + * </pre> + * The date format for <code>lastModified</code> is <code>yyyy-MM-dd'T'HH:mm:ssZ</code>. + * The <code>lastModified</code> attribute is optional. If missing, it will be set to the current time. + */ + protected static final class FileDescription { + + private URL url; + private String mimeType; + private URL baseLocation; + private Long lastModified; + + public static FileDescription SHARED = new FileDescription(); + private static final String SRC_ATTRIBUTE = "src"; + private static final String MIME_TYPE_ATTRIBUTE = "mimeType"; + private static final String LAST_MODIFIED_ATTRIBUTE = "lastModified"; + public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + + static { + DATE_FORMAT.setLenient(true); + } + + public void setValues(AttributeMap attributes) throws MalformedURLException, ParseException { + Set<String> attributeNames = attributes.keySet(); + for (String name : attributeNames) { + String value = attributes.get(name); + if (name.equals(SRC_ATTRIBUTE)) { + url = new URL(baseLocation, value); + } else if (name.equals(MIME_TYPE_ATTRIBUTE)) { + mimeType = value; + } else if (name.equals(LAST_MODIFIED_ATTRIBUTE)) { + lastModified = DATE_FORMAT.parse(value).getTime(); + } + } + } + + public void create(ContentCreator creator) throws RepositoryException, IOException { + String[] parts = url.getPath().split("/"); + String name = parts[parts.length - 1]; + InputStream stream = url.openStream(); + creator.createFileAndResourceNode(name, stream, mimeType, lastModified != null ? lastModified : Calendar.getInstance().getTimeInMillis()); + closeStream(stream); + creator.finishNode(); + creator.finishNode(); + this.clear(); + } + + public URL getUrl() { + return url; + } + + public String getMimeType() { + return mimeType; + } + + public Long getLastModified() { + return lastModified; + } + + public void clear() { + this.url = null; + this.mimeType = null; + this.lastModified = null; + } + + public void setBaseLocation(URL xmlLocation) { + this.baseLocation = xmlLocation; + } + } + + /** + * Utility class for dealing with attributes from KXmlParser. + */ + protected static class AttributeMap extends HashMap<String, String> { + + private static final AttributeMap instance = new AttributeMap(); + + public static AttributeMap getInstance() { + return instance; + } + + /** + * Puts values in an <code>AttributeMap</code> by extracting attributes from the <code>xmlParser</code>. + * @param xmlParser <code>xmlParser</code> to extract attributes from. The parser must be + * in {@link org.xmlpull.v1.XmlPullParser#START_TAG} state. + */ + public void setValues(KXmlParser xmlParser) { + final int count = xmlParser.getAttributeCount(); + for (int i = 0; i < count; i++) { + this.put(xmlParser.getAttributeName(i), xmlParser.getAttributeValue(i)); + } + } + } } diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java index 46119dd..2bc5ab2 100644 --- a/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java +++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java @@ -22,29 +22,82 @@ import junit.framework.TestCase; import org.apache.sling.jcr.contentloader.internal.ContentCreator; import javax.jcr.RepositoryException; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; +import java.util.Date; +import java.util.List; import java.util.Map; -/** - * Test the XmlReader with an XSLT transform - */ public class XmlReaderTest extends TestCase { + private XmlReader reader; + private MockContentCreator creator; + + /** + * Test the XmlReader with an XSLT transform. + */ public void testXmlReader() throws Exception { - XmlReader reader = new XmlReader(); File file = new File("src/test/resources/reader/sample.xml"); final URL testdata = file.toURI().toURL(); - final MockContentCreator creator = new MockContentCreator(); reader.parse(testdata, creator); assertEquals("Did not create expected number of nodes", 1, creator.size()); } + /** + * Test inclusion of binary files. + */ + public void testCreateFile() throws Exception { + File input = new File("src/test/resources/reader/filesample.xml"); + final URL testdata = input.toURI().toURL(); + reader.parse(testdata, creator); + assertEquals("Did not create expected number of files", 2, creator.filesCreated.size()); + MockContentCreator.FileDescription file = creator.filesCreated.get(0); + try { + file.data.available(); + TestCase.fail("Did not close inputstream"); + } catch (IOException ignore) { + // Expected + } + assertEquals("mimeType mismatch", "application/test", file.mimeType); + assertEquals("lastModified mismatch", XmlReader.FileDescription.DATE_FORMAT.parse("1977-06-01T07:00:00+0100"), new Date(file.lastModified)); + assertEquals("Could not read file", "This is a test file.", file.content); + + } + + protected void setUp() throws Exception { + super.setUp(); + reader = new XmlReader(); + creator = new MockContentCreator(); + } + @SuppressWarnings("serial") private static class MockContentCreator extends ArrayList<String> implements ContentCreator { + public static class FileDescription { + public String name; + public InputStream data; + public String mimeType; + public long lastModified; + public String content; + + public FileDescription(String name, InputStream data, String mimeType, long lastModified) throws IOException { + this.name = name; + this.data = data; + this.mimeType = mimeType; + this.lastModified = lastModified; + BufferedReader reader = new BufferedReader(new InputStreamReader(data)); + this.content = reader.readLine(); + reader.close(); + } + } + + public List<FileDescription> filesCreated = new ArrayList<FileDescription>(); + public MockContentCreator() { } @@ -68,6 +121,11 @@ public class XmlReaderTest extends TestCase { } public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException { + try { + this.filesCreated.add(new FileDescription(name, data, mimeType, lastModified)); + } catch (IOException e) { + throw new RuntimeException(e); + } } public boolean switchCurrentNode(String subPath, String newNodeType) throws RepositoryException { diff --git a/src/test/resources/reader/filesample.xml b/src/test/resources/reader/filesample.xml new file mode 100644 index 0000000..ce4607b --- /dev/null +++ b/src/test/resources/reader/filesample.xml @@ -0,0 +1,21 @@ +<node xmlns:nt="http://www.jcp.org/jcr/nt/1.0"> + <name>nodeName</name> + <primaryNodeType>type</primaryNodeType> + <mixinNodeTypes> + <mixinNodeType>mixtype1</mixinNodeType> + <mixinNodeType>mixtype2</mixinNodeType> + </mixinNodeTypes> + <properties> + <property> + <name>propName</name> + <value>propValue</value> + <type>String</type> + </property> + <!-- more properties --> + </properties> + <nodes> + <!-- child nodes --> + <nt:file src="testfile.txt" mimeType="application/test" lastModified="1977-06-01T07:00:00+0100"/> + <nt:file src="testfile.txt" mimeType="application/test" lastModified="1977-06-01T07:00:00+0100"/> + </nodes> +</node> diff --git a/src/test/resources/reader/testfile.txt b/src/test/resources/reader/testfile.txt new file mode 100644 index 0000000..6de7b8c --- /dev/null +++ b/src/test/resources/reader/testfile.txt @@ -0,0 +1 @@ +This is a test file. -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
