You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:49:41 UTC

[sling-org-apache-sling-jcr-repoinit] 14/17: SLING-5943 - support multiple model/raw references in RepositoryInitializer configuration

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.jcr.repoinit-1.0.2
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git

commit 3fa0bab78247d746e44f8ccbc081732e13e764ad
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Tue Aug 16 14:28:28 2016 +0000

    SLING-5943 - support multiple model/raw references in RepositoryInitializer configuration
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/jcr/repoinit@1756518 13f79535-47bb-0310-9956-ffa450edef68
---
 .../jcr/repoinit/impl/RepoinitTextProvider.java    | 141 +++++++++++++++++
 .../jcr/repoinit/impl/RepositoryInitializer.java   | 170 +++++----------------
 .../jcr/repoinit/RepositoryInitializerTest.java    |  38 +++--
 .../jcr/repoinit/impl/RepoinitReferenceTest.java   |  91 +++++++++++
 4 files changed, 294 insertions(+), 146 deletions(-)

diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/RepoinitTextProvider.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepoinitTextProvider.java
new file mode 100644
index 0000000..7b07636
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepoinitTextProvider.java
@@ -0,0 +1,141 @@
+/*
+ * 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.repoinit.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.provisioning.model.Feature;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.Section;
+import org.apache.sling.provisioning.model.io.ModelReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Retrieves repoinit statements from URLs that return either
+ *  raw repoinit text or Sling provisioning models that are parsed
+ *  to extract the repoinit text.
+ *  
+ *  Uses references like
+ *  
+ *  <code>model@repoinit:context:/resources/provisioning/model</code>,
+ *  
+ *  meaning that the supplied context:/ URI returns a provisioning model 
+ *  containing repoinit statements in its "repoinit" additional section, or
+ *    
+ *
+ *  <code>raw:classpath://com.example.sling.repoinit/repoinit.txt</code>
+ *  
+ *  meaning that the supplied classpath: URI returns raw repoinit statements.
+ */
+public class RepoinitTextProvider {
+    public static enum TextFormat { raw, model };
+    private static final String DEFAULT_MODEL_SECTION = "repoinit";
+    
+    public static final Pattern REF_PATTERN = Pattern.compile("([a-z]+)(@([a-zA-Z0-9_-]+))?:(.*)");
+    
+    private Logger log = LoggerFactory.getLogger(getClass());
+    
+    static class Reference {
+        final TextFormat format;
+        final String modelSection;
+        final String url;
+        
+        Reference(String ref) {
+            if(ref == null) {
+                throw new IllegalArgumentException("Null reference");
+            }
+            final Matcher m = REF_PATTERN.matcher(ref);
+            if(!m.matches()) {
+                throw new IllegalArgumentException("Invalid reference '" + ref + "', should match " + REF_PATTERN);
+            }
+            format = TextFormat.valueOf(m.group(1));
+            if(format.equals(TextFormat.raw)) {
+                modelSection = null;
+            } else if(format.equals(TextFormat.model) && m.group(3) == null) {
+                modelSection = DEFAULT_MODEL_SECTION;
+            } else {
+                modelSection = m.group(3);
+            }
+            url = m.group(4);
+        }
+        
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(getClass().getSimpleName()).append(":");
+            sb.append("format=").append(format);
+            if(modelSection != null) {
+                sb.append(", model section=").append(modelSection);
+            }
+            sb.append(", URL=").append(url);
+            return sb.toString();
+        }
+    }
+    
+    public String getRepoinitText(String referenceString) throws IOException {
+        final Reference ref = new Reference(referenceString);
+        log.info("Reading repoinit statements from {}", ref);
+        final String rawText = getRawText(ref.url);
+        log.debug("Raw text from {}: \n{}", ref.url, rawText);
+        if(TextFormat.model.equals(ref.format)) {
+            log.debug("Extracting provisioning model section {}", ref.modelSection);
+            return extractFromModel(ref.url, rawText, ref.modelSection); 
+        } else {
+            return rawText;
+        }
+    }
+    
+    private String extractFromModel(String sourceInfo, String rawText, String modelSection) throws IOException {
+        final StringReader reader = new StringReader(rawText);
+        final Model model = ModelReader.read(reader, sourceInfo);
+        final StringBuilder sb = new StringBuilder();
+        if(modelSection == null) {
+            throw new IllegalStateException("Model section name is null, cannot read model");
+        }
+        for (final Feature feature : model.getFeatures()) {
+            for (final Section section : feature.getAdditionalSections(modelSection)) {
+                sb.append("# ").append(modelSection).append(" from ").append(feature.getName()).append("\n");
+                sb.append("# ").append(section.getComment()).append("\n");
+                sb.append(section.getContents()).append("\n");
+            }
+        }
+        return sb.toString();
+    }
+    
+    private String getRawText(String urlString) throws IOException {
+        String result = "";
+        final URL url = new URL(urlString);
+        final URLConnection c = url.openConnection();
+        final InputStream is = c.getInputStream();
+        if(is == null) {
+            log.warn("Cannot get InputStream for {}", url);
+        } else {
+            final StringWriter w = new StringWriter();
+            IOUtils.copy(is, w, "UTF-8");
+            result = w.toString();
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializer.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializer.java
index 9b5c686..9dbab2c 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializer.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializer.java
@@ -16,34 +16,23 @@
  */
 package org.apache.sling.jcr.repoinit.impl;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.io.StringReader;
-import java.io.StringWriter;
-import java.net.URL;
-import java.net.URLConnection;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
 import javax.jcr.Session;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.PropertyOption;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.api.SlingRepositoryInitializer;
 import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor;
-import org.apache.sling.provisioning.model.Feature;
-import org.apache.sling.provisioning.model.Model;
-import org.apache.sling.provisioning.model.Section;
-import org.apache.sling.provisioning.model.io.ModelReader;
 import org.apache.sling.repoinit.parser.RepoInitParser;
 import org.apache.sling.repoinit.parser.operations.Operation;
 import org.osgi.framework.Constants;
@@ -66,40 +55,18 @@ import org.slf4j.LoggerFactory;
 public class RepositoryInitializer implements SlingRepositoryInitializer {
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    public static final String DEFAULT_TEXT_URL = "context:/resources/provisioning/model.txt";
+    public static final String DEFAULT_REFERENCE = "model@repoinit:context:/resources/provisioning/model.txt";
     
     @Property(
-            label="Text URL", 
-            description="URL of the source text that provides repoinit statements.",
-            value=DEFAULT_TEXT_URL)
-    public static final String PROP_TEXT_URL = "text.url";
-    private String textURL;
-    
-    public static final String DEFAULT_MODEL_SECTION_NAME = "repoinit";
-    
-    @Property(
-            label="Model section name", 
-            description=
-                "If using the provisioning model format, this specifies the additional section name (without leading colon) used to extract"
-                + " repoinit statements from the raw text provided by the configured source text URL.",
-            value=DEFAULT_MODEL_SECTION_NAME)
-    public static final String PROP_MODEL_SECTION_NAME = "model.section.name";
-    private String modelSectionName;
-    
-    @Property(
-            label="Text format", 
+            label="Repoinit references", 
             description=
-                "The format to use to interpret the text provided by the configured source text URL. "
-                + "That text can be either a Sling provisioning model with repoinit statements embedded in additional sections,"
-                + " or raw repoinit statements",
-            options = {
-                    @PropertyOption(name = "MODEL", value = "Provisioning Model (MODEL)"),
-                    @PropertyOption(name = "RAW", value = "Raw Repoinit statements (RAW)")
-                },                
-            value="MODEL")
-    public static final String PROP_TEXT_FORMAT = "text.format";
-    public static enum TextFormat { RAW, MODEL };
-    private TextFormat textFormat;
+                 "References to the source text that provides repoinit statements."
+                + " format is either model@repoinit:<provisioning model URL> or raw:<raw URL>"
+            ,
+            cardinality=Integer.MAX_VALUE,
+            value={ DEFAULT_REFERENCE })
+    public static final String PROP_REFERENCES = "references";
+    private String [] references;
     
     @Reference
     private RepoInitParser parser;
@@ -109,106 +76,47 @@ public class RepositoryInitializer implements SlingRepositoryInitializer {
     
     @Activate
     public void activate(Map<String, Object> config) {
-        textURL = PropertiesUtil.toString(config.get(PROP_TEXT_URL), DEFAULT_TEXT_URL);
-        
-        final String fmt = PropertiesUtil.toString(config.get(PROP_TEXT_FORMAT), TextFormat.MODEL.toString());
-        try {
-            textFormat = TextFormat.valueOf(fmt);
-        } catch(Exception e) {
-            throw new IllegalArgumentException("Invalid text format '" + fmt + "',"
-                    + " valid values are " + Arrays.asList(TextFormat.values()));
-        }
-        
-        modelSectionName = PropertiesUtil.toString(config.get(PROP_MODEL_SECTION_NAME), DEFAULT_MODEL_SECTION_NAME);
-        
+        warnForOldConfigParameters(config);
+        references = PropertiesUtil.toStringArray(config.get(PROP_REFERENCES));
         log.debug("Activated: {}", this.toString());
     }
     
+    /** Some config parameters are not used anymore as of V1.0.2, this logs 
+     *  warnings if they are still used.
+     */
+    private void warnForOldConfigParameters(Map<String, Object> config) {
+        final String [] names = {
+                "text.url",
+                "text.format",
+                "model.section.name"
+        };
+        for(String name : names) {
+            if(config.containsKey(name)) {
+                log.warn("Configuration parameter '{}' is not used anymore, will be ignored", name);
+            }
+        }
+        }
+    
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append(getClass().getSimpleName()).append(": ");
-        sb.append(PROP_TEXT_URL).append("=").append(textURL).append(", ");
-        sb.append(PROP_TEXT_FORMAT).append("=").append(textFormat).append(", ");
-        sb.append(PROP_MODEL_SECTION_NAME).append("=").append(modelSectionName);
-        return sb.toString();
+        return getClass().getSimpleName() + ", references=" + Arrays.asList(references);
     }
-    
+
     @Override
     public void processRepository(SlingRepository repo) throws Exception {
-        final String repoinit = getRepoInitText();
-        
-        if(TextFormat.MODEL.equals(textFormat)) {
-            if(modelSectionName == null) {
-                throw new IllegalStateException("Section name is null, cannot read model");
-            }
-            if(modelSectionName.trim().length() == 0) {
-                throw new IllegalStateException("Empty " + PROP_MODEL_SECTION_NAME + " is not supported anymore, please use " 
-                        + PROP_TEXT_FORMAT + " to specify the input text format");
-            }
-        }
-        
         // loginAdministrative is ok here, definitely an admin operation
         final Session s = repo.loginAdministrative(null);
         try {
-            final List<Operation> ops = parser.parse(new StringReader(repoinit));
-            log.info("Executing {} repoinit operations", ops.size());
-            processor.apply(s, ops);
-            s.save();
+            final RepoinitTextProvider p = new RepoinitTextProvider();
+            for(String reference : references) {
+                final String repoinitText = p.getRepoinitText(reference);
+                final List<Operation> ops = parser.parse(new StringReader(repoinitText));
+                log.info("Executing {} repoinit operations", ops.size());
+                processor.apply(s, ops);
+                s.save();
+            }
         } finally {
             s.logout();
         }
-    }
-    
-    /** Get the repoinit statements to execute */
-    private String getRawRepoInitText() {
-        String result = "";
-        try {
-            final URL url = new URL(textURL);
-            final URLConnection c = url.openConnection();
-            final InputStream is = c.getInputStream();
-            if(is == null) {
-                log.warn("Cannot get InputStream for {}", url);
-            } else {
-                final StringWriter w = new StringWriter();
-                IOUtils.copy(is, w, "UTF-8");
-                result = w.toString();
-            }
-        } catch(Exception e) {
-            log.warn("Error reading repoinit statements from " + textURL, e);
-        }
-        return result;
-    }
-    
-    private String getRepoInitText() {
-        final String rawText = getRawRepoInitText();
-        log.debug("Raw text from {}: \n{}", textURL, rawText);
-        log.info("Got {} characters from {}", rawText.length(), textURL);
-        if(TextFormat.RAW.equals(textFormat)) {
-            log.info("Parsing raw repoinit statements from {}", textURL);
-            return rawText;
-        } else {
-            log.info("Extracting repoinit statements from section ':{}' of provisioning model {}", modelSectionName, textURL);
-            final StringReader reader = new StringReader(rawText);
-            try {
-                final Model model = ModelReader.read(reader, textURL);
-                final StringBuilder sb = new StringBuilder();
-                if(modelSectionName == null) {
-                    throw new IllegalStateException("Model section name is null, cannot read model");
-                }
-                for (final Feature feature : model.getFeatures()) {
-                    for (final Section section : feature.getAdditionalSections(modelSectionName)) {
-                        sb.append("# ").append(modelSectionName).append(" from ").append(feature.getName()).append("\n");
-                        sb.append("# ").append(section.getComment()).append("\n");
-                        sb.append(section.getContents()).append("\n");
-                    }
-                }
-                return sb.toString();
-            } catch (IOException e) {
-                log.warn("Error parsing provisioning model from " + textURL, e);
-                return "";
-            }
-        }
-    }
-
-}
+    }   
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/RepositoryInitializerTest.java b/src/test/java/org/apache/sling/jcr/repoinit/RepositoryInitializerTest.java
index 4081050..7167385 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/RepositoryInitializerTest.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/RepositoryInitializerTest.java
@@ -31,9 +31,10 @@ import java.util.UUID;
 
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.repoinit.impl.JcrRepoInitOpsProcessorImpl;
+import org.apache.sling.jcr.repoinit.impl.RepoinitTextProvider.TextFormat;
 import org.apache.sling.jcr.repoinit.impl.RepositoryInitializer;
-import org.apache.sling.jcr.repoinit.impl.RepositoryInitializer.TextFormat;
 import org.apache.sling.jcr.repoinit.impl.TestUtil;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
 import org.apache.sling.repoinit.parser.impl.RepoInitParserService;
 import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
@@ -70,18 +71,19 @@ public class RepositoryInitializerTest {
         final List<Object []> result = new ArrayList<Object[]>();
         
         // Realistic cases
-        result.add(new Object[] { "Using provisioning model", "SECTION_" + UUID.randomUUID(), TextFormat.MODEL.toString(), true, true, null }); 
-        result.add(new Object[] { "Default value of model section config", null, TextFormat.MODEL.toString(), true, true, null });
-        result.add(new Object[] { "Raw repoinit/empty section", "", TextFormat.RAW.toString(), false, true, null }); 
-        result.add(new Object[] { "Raw repoinit/ignored section name", "IGNORED_SectionName", TextFormat.RAW.toString(), false, true, null }); 
+        result.add(new Object[] { "Using provisioning model", "SECTION_" + UUID.randomUUID(), TextFormat.model.toString(), true, true, null }); 
+        result.add(new Object[] { "Default value of model section config", null, TextFormat.model.toString(), true, true, null });
+        result.add(new Object[] { "Raw repoinit/empty section", "", TextFormat.raw.toString(), false, true, null }); 
+        result.add(new Object[] { "Raw repoinit/ignored section name", "IGNORED_SectionName", TextFormat.raw.toString(), false, true, null }); 
         
         // Edge and failure cases 
-        result.add(new Object[] { "All empty, just setup + parsing", "", TextFormat.RAW.toString(), false, false, null });
-        result.add(new Object[] { "Raw repoinit/null format", null, null, true, false, RuntimeException.class });
+        result.add(new Object[] { "All empty, just setup + parsing", "", TextFormat.raw.toString(), false, false, null });
+        result.add(new Object[] { "Raw repoinit/null format", null, null, true, false, RepoInitParsingException.class });
         result.add(new Object[] { "With model/null format", null, null, false, false, RuntimeException.class });
         result.add(new Object[] { "Invalid format", null, "invalidFormat", false, false, RuntimeException.class }); 
-        result.add(new Object[] { "Empty model section", "", TextFormat.MODEL.toString(), false, false, IllegalStateException.class }); 
-        result.add(new Object[] { "Null model section", null, TextFormat.MODEL.toString(), false, false, IllegalStateException.class }); 
+        result.add(new Object[] { "Empty model section", "", TextFormat.model.toString(), false, false, IllegalArgumentException.class }); 
+        result.add(new Object[] { "Null model section", null, TextFormat.model.toString(), false, false, IOException.class });
+        
         return result;
     }
     
@@ -109,14 +111,20 @@ public class RepositoryInitializerTest {
 
         initializer = new RepositoryInitializer();
         config = new HashMap<String, Object>();
-        config.put(RepositoryInitializer.PROP_TEXT_URL, url);
-        if(modelSection != null) {
-            config.put(RepositoryInitializer.PROP_MODEL_SECTION_NAME, modelSection);
-        }
-        if(textFormat != null) {
-            config.put(RepositoryInitializer.PROP_TEXT_FORMAT, textFormat);
+        
+        String ref = null;
+        if(TextFormat.model.toString().equals(textFormat)) {
+            if(modelSection != null) {
+                ref = "model@" + modelSection + ":" + url;
+            } else {
+                ref = "model:" + url;
+            }
+        } else {
+            ref = "raw:" + url;
         }
         
+        config.put(RepositoryInitializer.PROP_REFERENCES, new String[] { ref });
+        
         context.registerInjectActivateService(new RepoInitParserService());
         context.registerInjectActivateService(new JcrRepoInitOpsProcessorImpl());
         
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/impl/RepoinitReferenceTest.java b/src/test/java/org/apache/sling/jcr/repoinit/impl/RepoinitReferenceTest.java
new file mode 100644
index 0000000..5b6b775
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/repoinit/impl/RepoinitReferenceTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.repoinit.impl;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sling.jcr.repoinit.impl.RepoinitTextProvider.Reference;
+import org.apache.sling.jcr.repoinit.impl.RepoinitTextProvider.TextFormat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Test the RepoinitTextProvider references parsing */
+@RunWith(Parameterized.class)
+public class RepoinitReferenceTest {
+    
+    @Parameters(name="{0}")
+    public static Collection<Object[]> data() {
+        final List<Object []> result = new ArrayList<Object[]>();
+        
+        // Valid references
+        result.add(new Object[] { "model@foo:uri:1234", TextFormat.model, "foo", "uri:1234", null }); 
+        result.add(new Object[] { "model:uri:2345", TextFormat.model, "repoinit", "uri:2345", null }); 
+        result.add(new Object[] { "raw:uri:4567", TextFormat.raw , null, "uri:4567", null }); 
+        result.add(new Object[] { "raw:uri@5678", TextFormat.raw, null, "uri@5678", null });
+
+        // Invalid references
+        result.add(new Object[] { "model@foo", null, null, null, IllegalArgumentException.class });
+        result.add(new Object[] { "model#foo:url", TextFormat.model, "repoinit", "url", IllegalArgumentException.class });
+        result.add(new Object[] { "", null, null, null, IllegalArgumentException.class });
+        result.add(new Object[] { null, null, null, null, IllegalArgumentException.class });
+        result.add(new Object[] { "foo:url", null, null, null, IllegalArgumentException.class });
+        
+        // foo is ignored, by design
+        result.add(new Object[] { "raw@foo:url", TextFormat.raw, null, "url", null });
+        
+        return result;
+    }
+    
+    private final String input;
+    private final RepoinitTextProvider.TextFormat format;
+    private final String modelSection;
+    private final String url;
+    private final Class<?> expectedException;
+    
+    public RepoinitReferenceTest(String input, TextFormat format, String modelSection, String url, Class<? >expectedException) {
+        this.input = input;
+        this.format = format;
+        this.modelSection = modelSection;
+        this.url = url;
+        this.expectedException = expectedException;
+    }
+    
+    @Test
+    public void testParsing() {
+        try {
+            final Reference ref = new Reference(input);
+            if(expectedException != null) {
+                fail("Expected a " + expectedException.getName());
+            }
+            assertEquals(format, ref.format);
+            assertEquals(modelSection, ref.modelSection);
+            assertEquals(url, ref.url);
+        } catch(Exception e) {
+            if(expectedException != null) {
+                assertEquals(expectedException, e.getClass());
+            } else {
+                fail("Unexpected " + e);
+            }
+        }
+    }
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.