Return-Path: Delivered-To: apmail-sling-commits-archive@www.apache.org Received: (qmail 29112 invoked from network); 27 Oct 2009 09:44:20 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 27 Oct 2009 09:44:20 -0000 Received: (qmail 62936 invoked by uid 500); 27 Oct 2009 09:44:20 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 62881 invoked by uid 500); 27 Oct 2009 09:44:20 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 62872 invoked by uid 99); 27 Oct 2009 09:44:20 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 27 Oct 2009 09:44:20 +0000 X-ASF-Spam-Status: No, hits=-2.6 required=5.0 tests=BAYES_00 X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 27 Oct 2009 09:44:17 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 7308D2388998; Tue, 27 Oct 2009 09:43:57 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r830106 - in /sling/trunk/bundles/jcr/contentloader/src: main/java/org/apache/sling/jcr/contentloader/internal/readers/ test/java/org/apache/sling/jcr/contentloader/internal/readers/ test/resources/reader/ Date: Tue, 27 Oct 2009 09:43:57 -0000 To: commits@sling.apache.org From: vramdal@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20091027094357.7308D2388998@eris.apache.org> Author: vramdal Date: Tue Oct 27 09:43:56 2009 New Revision: 830106 URL: http://svn.apache.org/viewvc?rev=830106&view=rev Log: SLING-1161 implemented. Now supports including external files with nt:file in content loader XML Added: sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/filesample.xml sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/testfile.txt Modified: sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java Modified: sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java?rev=830106&r1=830105&r2=830106&view=diff ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java (original) +++ sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java Tue Oct 27 09:43:56 2009 @@ -25,12 +25,18 @@ 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 @@ /** * This reader reads an xml file defining the content. The xml format should have this * format: - * + * *
  * <node>
  *   <name>the name of the node</name>
@@ -81,6 +87,9 @@
  *   </nodes>
  * </node>
  * 
+ * + * 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 <nt:file>} element. */ public class XmlReader implements ContentReader { @@ -113,6 +122,9 @@ 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 @@ 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 @@ NodeDescription.SHARED.clear(); PropertyDescription.SHARED.clear(); + FileDescription.SHARED.clear(); NodeDescription currentNode = null; PropertyDescription currentProperty = null; @@ -200,6 +219,22 @@ } 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 @@ } currentNode.addMixinType(content); } - } else if (eventType == XmlPullParser.TEXT || eventType == XmlPullParser.CDSECT) { contentBuffer.append(this.xmlParser.getText()); } @@ -455,4 +489,103 @@ } } + + /** + * Represents a reference to a file that is to be loaded into the repository. The file is referenced by an + * XML element named <nt:file>, with the attributes src, + * mimeType and lastModified.

Example: + *
+     * <nt:file src="../../image.png" mimeType="image/png" lastModified="1977-06-01T07:00:00+0100" />
+     * 
+ * The date format for lastModified is yyyy-MM-dd'T'HH:mm:ssZ. + * The lastModified 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 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 { + + private static final AttributeMap instance = new AttributeMap(); + + public static AttributeMap getInstance() { + return instance; + } + + /** + * Puts values in an AttributeMap by extracting attributes from the xmlParser. + * @param xmlParser xmlParser 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)); + } + } + } } Modified: sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java?rev=830106&r1=830105&r2=830106&view=diff ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java (original) +++ sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java Tue Oct 27 09:43:56 2009 @@ -22,29 +22,82 @@ 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 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 filesCreated = new ArrayList(); + public MockContentCreator() { } @@ -68,6 +121,11 @@ } 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 { Added: sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/filesample.xml URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/filesample.xml?rev=830106&view=auto ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/filesample.xml (added) +++ sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/filesample.xml Tue Oct 27 09:43:56 2009 @@ -0,0 +1,21 @@ + + nodeName + type + + mixtype1 + mixtype2 + + + + propName + propValue + String + + + + + + + + + Added: sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/testfile.txt URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/testfile.txt?rev=830106&view=auto ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/testfile.txt (added) +++ sling/trunk/bundles/jcr/contentloader/src/test/resources/reader/testfile.txt Tue Oct 27 09:43:56 2009 @@ -0,0 +1 @@ +This is a test file.