This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.provisioning.model-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-provisioning-model.git
commit 1a36bd0e2702a0cc2ec385039b46d94bb5481028 Author: Carsten Ziegeler <[email protected]> AuthorDate: Fri Sep 26 15:25:23 2014 +0000 Implement txt format for reading and writing git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/slingstart-model@1627807 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/sling/slingstart/model/SSMArtifact.java | 61 +++-- .../sling/slingstart/model/SSMConfiguration.java | 28 +-- .../sling/slingstart/model/SSMConstants.java | 13 + .../sling/slingstart/model/SSMDeliverable.java | 19 +- .../apache/sling/slingstart/model/SSMFeature.java | 44 +--- .../sling/slingstart/model/SSMStartLevel.java | 21 +- .../model/{SSMConstants.java => SSMTraceable.java} | 30 ++- .../sling/slingstart/model/SSMValidator.java | 94 ++++++++ .../slingstart/model/txt/TXTSSMModelReader.java | 265 +++++++++++++-------- .../slingstart/model/txt/TXTSSMModelWriter.java | 191 +++++++++++++++ .../slingstart/model/xml/XMLSSMModelReader.java | 71 ++++-- 11 files changed, 590 insertions(+), 247 deletions(-) diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java b/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java index d05e8ff..7cc9b3b 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java @@ -25,7 +25,7 @@ import java.util.Map; * In addition, the classifier and type can be specified as well. * An artifact can have any metadata. */ -public class SSMArtifact { +public class SSMArtifact extends SSMTraceable { private final String groupId; private final String artifactId; @@ -126,6 +126,27 @@ public class SSMArtifact { } /** + * Return a mvn url + * @return A mvn url + * @see #fromMvnUrl(String) + */ + public String toMvnUrl() { + final StringBuilder sb = new StringBuilder("mvn:"); + sb.append(this.groupId); + sb.append('/'); + sb.append(this.artifactId); + sb.append('/'); + sb.append(this.version); + sb.append('/'); + sb.append(this.type); + if ( this.classifier != null ) { + sb.append('/'); + sb.append(this.classifier); + } + return sb.toString(); + } + + /** * Return the group id. * @return The group id. */ @@ -196,38 +217,14 @@ public class SSMArtifact { return sb.toString(); } - /** - * Validates the object and throws an IllegalStateException - * This object needs: - * - groupId - * - artifactId - * - version - * If type is null, it's set to "jar" - * If type is "bundle", it's set to "jar" - * - classifier is optional - * - * @throws IllegalStateException - */ - public void validate() { - // check/correct values - if ( groupId == null || groupId.isEmpty() ) { - throw new IllegalStateException(this + " : groupId"); - } - if ( artifactId == null || artifactId.isEmpty() ) { - throw new IllegalStateException(this + " : artifactId"); - } - if ( version == null || version.isEmpty() ) { - throw new IllegalStateException(this + " : version"); - } - if ( type == null || type.isEmpty() ) { - throw new IllegalStateException(this + " : type"); - } - } - @Override public String toString() { - return "SSMArtifact [groupId=" + groupId + ", artifactId=" + artifactId - + ", version=" + version + ", classifier=" + classifier - + ", type=" + type + "]"; + return "SSMArtifact [groupId=" + groupId + + ", artifactId=" + artifactId + + ", version=" + version + + ", classifier=" + classifier + + ", type=" + type + + ( this.getLocation() != null ? ", location=" + this.getLocation() : "") + + "]"; } } diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java b/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java index 3a3f0a0..f2342e2 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java @@ -23,7 +23,7 @@ import java.util.Hashtable; /** * Configuration */ -public class SSMConfiguration { +public class SSMConfiguration extends SSMTraceable { private final String pid; @@ -54,25 +54,6 @@ public class SSMConfiguration { } /** - * validates the object and throws an IllegalStateException - * This object needs: - * - pid - * - properties - * - factoryPid is optional - * - * @throws IllegalStateException - */ - public void validate() { - // check/correct values - if ( pid == null || pid.isEmpty() ) { - throw new IllegalStateException("pid"); - } - if ( properties == null || properties.isEmpty() ) { - throw new IllegalStateException("properties"); - } - } - - /** * Is this a special configuration? * @return Special config */ @@ -93,7 +74,10 @@ public class SSMConfiguration { @Override public String toString() { - return "SSMConfiguration [pid=" + pid + ", factoryPid=" + factoryPid - + ", properties=" + properties + "]"; + return "SSMConfiguration [pid=" + pid + + ", factoryPid=" + factoryPid + + ", properties=" + properties + + ( this.getLocation() != null ? ", location=" + this.getLocation() : "") + + "]"; } } diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java b/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java index ca91467..3b0fa18 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java @@ -24,4 +24,17 @@ public abstract class SSMConstants { /** Name of the configuration for the bootstrap contents. */ public static final String CFG_BOOTSTRAP = ":bootstrap"; + + /** Name of the base run mode for the Sling launchpad. */ + public static final String RUN_MODE_BASE = ":base"; + + /** Name of the boot run mode. */ + public static final String RUN_MODE_BOOT = ":boot"; + + /** Name of the webapp run mode. */ + public static final String RUN_MODE_WEBAPP = ":webapp"; + + /** Name of the standalone run mode. */ + public static final String RUN_MODE_STANDALONE = ":standalone"; + } diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java b/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java index 1960d24..a931981 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMDeliverable.java @@ -31,7 +31,7 @@ import java.util.Map; * * At least it has a "global" feature which contains artifacts that are always installed.. */ -public class SSMDeliverable { +public class SSMDeliverable extends SSMTraceable { private final List<SSMFeature> features = new ArrayList<SSMFeature>(); @@ -84,17 +84,6 @@ public class SSMDeliverable { } /** - * validates the object and throws an IllegalStateException - * - * @throws IllegalStateException - */ - public void validate() { - for(final SSMFeature f : this.features) { - f.validate(); - } - } - - /** * Replace properties in the string. * * @throws IllegalArgumentException @@ -145,7 +134,9 @@ public class SSMDeliverable { @Override public String toString() { - return "SSMDeliverable [features=" + features + ", variables=" - + variables + "]"; + return "SSMDeliverable [features=" + features + + ", variables=" + variables + + ( this.getLocation() != null ? ", location=" + this.getLocation() : "") + + "]"; } } diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java b/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java index 86f7a81..0a8e175 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMFeature.java @@ -36,15 +36,9 @@ import java.util.Set; * In addition to custom, user defined run modes, special run modes exists. * A special run mode name starts with a colon. */ -public class SSMFeature implements Comparable<SSMFeature> { - - public static final String RUN_MODE_BASE = ":base"; - - public static final String RUN_MODE_BOOT = ":boot"; - - public static final String RUN_MODE_WEBAPP = ":webapp"; - - public static final String RUN_MODE_STANDALONE = ":standalone"; +public class SSMFeature + extends SSMTraceable + implements Comparable<SSMFeature> { private final String[] runModes; @@ -82,31 +76,6 @@ public class SSMFeature implements Comparable<SSMFeature> { } /** - * validates the object and throws an IllegalStateException - * - * @throws IllegalStateException - */ - public void validate() { - if ( this.runModes != null ) { - boolean hasSpecial = false; - for(String m : this.runModes) { - if ( m.startsWith(":") ) { - if ( hasSpecial ) { - throw new IllegalStateException("Invalid modes " + Arrays.toString(this.runModes)); - } - hasSpecial = true; - } - } - } - for(final SSMStartLevel sl : this.startLevels) { - sl.validate(); - } - for(final SSMConfiguration c : this.configurations) { - c.validate(); - } - } - - /** * Check if this feature is active wrt the given set of active run modes. */ public boolean isActive(final Set<String> activeRunModes) { @@ -261,7 +230,10 @@ public class SSMFeature implements Comparable<SSMFeature> { @Override public String toString() { return "SSMFeature [runModes=" + Arrays.toString(runModes) - + ", startLevels=" + startLevels + ", configurations=" - + configurations + ", settings=" + settings + "]"; + + ", startLevels=" + startLevels + + ", configurations=" + configurations + + ", settings=" + settings + + ( this.getLocation() != null ? ", location=" + this.getLocation() : "") + + "]"; } } diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java b/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java index ce2e08b..572bab0 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java @@ -23,7 +23,8 @@ import java.util.List; * A start level holds a set of artifacts. * A valid start level is positive, start level 0 means the default OSGi start level. */ -public class SSMStartLevel implements Comparable<SSMStartLevel> { +public class SSMStartLevel extends SSMTraceable + implements Comparable<SSMStartLevel> { private final int level; @@ -42,20 +43,6 @@ public class SSMStartLevel implements Comparable<SSMStartLevel> { } /** - * validates the object and throws an IllegalStateException - * - * @throws IllegalStateException - */ - public void validate() { - for(final SSMArtifact sl : this.artifacts) { - sl.validate(); - } - if ( level < 0 ) { - throw new IllegalStateException("level"); - } - } - - /** * Search an artifact with the same groupId, artifactId, version, type and classifier. * Version is not considered. */ @@ -95,7 +82,9 @@ public class SSMStartLevel implements Comparable<SSMStartLevel> { @Override public String toString() { - return "SSMStartLevel [level=" + level + ", artifacts=" + artifacts + return "SSMStartLevel [level=" + level + + ", artifacts=" + artifacts + + ( this.getLocation() != null ? ", location=" + this.getLocation() : "") + "]"; } } diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java b/src/main/java/org/apache/sling/slingstart/model/SSMTraceable.java similarity index 60% copy from src/main/java/org/apache/sling/slingstart/model/SSMConstants.java copy to src/main/java/org/apache/sling/slingstart/model/SSMTraceable.java index ca91467..78be78c 100644 --- a/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java +++ b/src/main/java/org/apache/sling/slingstart/model/SSMTraceable.java @@ -16,12 +16,32 @@ */ package org.apache.sling.slingstart.model; +public abstract class SSMTraceable { -public abstract class SSMConstants { + private String location; - /** Name of the configuration containing the web.xml. */ - public static final String CFG_WEB_XML = ":web.xml"; + private String comment; - /** Name of the configuration for the bootstrap contents. */ - public static final String CFG_BOOTSTRAP = ":bootstrap"; + public String getLocation() { + return this.location; + } + + public void setLocation(final String value) { + this.location = value; + } + + public String getComment() { + return this.comment; + } + + public void setComment(final String value) { + this.comment = value; + } + + @Override + public String toString() { + return "SSMTraceable [location=" + location + ", comment=" + comment + + "]"; + } } + diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMValidator.java b/src/main/java/org/apache/sling/slingstart/model/SSMValidator.java new file mode 100644 index 0000000..1ccb94f --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMValidator.java @@ -0,0 +1,94 @@ +/* + * 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.slingstart.model; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Validate a complete model. + */ +public class SSMValidator { + + /** + * Validates the model. + * @param model + * @return + */ + public Map<SSMTraceable, String> validate(final SSMDeliverable model) { + final Map<SSMTraceable, String> errors = new HashMap<SSMTraceable, String>(); + + for(final SSMFeature feature : model.getFeatures() ) { + final String[] rm = feature.getRunModes(); + if ( rm != null ) { + boolean hasSpecial = false; + for(final String m : rm) { + if ( m.startsWith(":") ) { + if ( hasSpecial ) { + errors.put(feature, "Invalid modes " + Arrays.toString(rm)); + break; + } + hasSpecial = true; + } + } + } + for(final SSMStartLevel sl : feature.getStartLevels()) { + if ( sl.getLevel() < 0 ) { + errors.put(sl, "Invalid start level " + sl.getLevel()); + } + for(final SSMArtifact a : sl.getArtifacts()) { + String error = null; + if ( a.getGroupId() == null || a.getGroupId().isEmpty() ) { + error = "groupId missing"; + } + if ( a.getArtifactId() == null || a.getArtifactId().isEmpty() ) { + error = (error != null ? error + ", " : "") + "artifactId missing"; + } + if ( a.getVersion() == null || a.getVersion().isEmpty() ) { + error = (error != null ? error + ", " : "") + "version missing"; + } + if ( a.getType() == null || a.getType().isEmpty() ) { + error = (error != null ? error + ", " : "") + "type missing"; + } + if (error != null) { + errors.put(a, error); + } + } + } + for(final SSMConfiguration c : feature.getConfigurations()) { + String error = null; + if ( c.getPid() == null || c.getPid().isEmpty() ) { + error = "pid missing"; + } + if ( c.isSpecial() && c.getFactoryPid() != null ) { + error = (error != null ? error + ", " : "") + "factory pid not allowed for special configuration"; + } + if ( c.getProperties().isEmpty() ) { + error = (error != null ? error + ", " : "") + "configuration properties missing"; + } + if (error != null) { + errors.put(c, error); + } + } + } + if ( errors.size() == 0 ) { + return null; + } + return errors; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java b/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java index 9c2f9c0..c41262d 100644 --- a/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java +++ b/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelReader.java @@ -20,118 +20,211 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.LineNumberReader; import java.io.Reader; -import java.io.StringReader; import java.util.Dictionary; import java.util.Enumeration; -import java.util.UUID; import org.apache.felix.cm.file.ConfigurationHandler; import org.apache.sling.slingstart.model.SSMArtifact; import org.apache.sling.slingstart.model.SSMConfiguration; import org.apache.sling.slingstart.model.SSMDeliverable; import org.apache.sling.slingstart.model.SSMFeature; +import org.apache.sling.slingstart.model.SSMStartLevel; +import org.apache.sling.slingstart.model.SSMTraceable; public class TXTSSMModelReader { public static final String FELIX_FORMAT_SUFFIX = "FORMAT:felix.config"; + private enum MODE { + NONE, + VARS, + FEATURE, + START_LEVEL, + CONFIGURATION, + SETTINGS, + ARTIFACT + } + /** * Reads the deliverable file * The reader is not closed. * @throws IOException */ - public static SSMDeliverable read(final Reader reader) + public static SSMDeliverable read(final Reader reader, final String location) + throws IOException { + final TXTSSMModelReader mr = new TXTSSMModelReader(location); + return mr.readModel(reader); + } + + private MODE mode = MODE.NONE; + + private final SSMDeliverable model = new SSMDeliverable(); + + private SSMFeature feature = null; + private SSMStartLevel startLevel = null; + private SSMConfiguration config = null; + private SSMArtifact artifact = null; + + private String comment = null; + + private StringBuilder configBuilder = null; + private boolean configFelixFormat = false; + + private LineNumberReader lineNumberReader; + + private TXTSSMModelReader(final String location) { + this.model.setLocation(location); + } + + private SSMDeliverable readModel(final Reader reader) throws IOException { - final SSMDeliverable model = new SSMDeliverable(); - final LineNumberReader lnr = new LineNumberReader(reader); + + boolean global = true; + + lineNumberReader = new LineNumberReader(reader); String line; - while ( (line = lnr.readLine()) != null ) { - if ( ignore(line) ) { + while ( (line = lineNumberReader.readLine()) != null ) { + // ignore empty line + if ( line.trim().isEmpty() ) { + continue; + } + // comment? + if ( line.startsWith("#") ) { + checkConfig(); + mode = MODE.NONE; + final String c = line.substring(1).trim(); + if ( comment == null ) { + comment = c; + } else { + comment = comment + "\n" + c; + } continue; } - // Command must start with a verb, optionally followed - // by properties - if (!isVerb(line)) { - throw new IOException("Expecting verb, current line is " + line); + if ( global ) { + global = false; + model.setComment(comment); + comment = null; } - // Parse verb and qualifier from first line - final String [] firstLine= line.split(" "); - final String verb = firstLine[0]; - final StringBuilder builder = new StringBuilder(); - for(int i=1; i < firstLine.length; i++) { - if (builder.length() > 0) { - builder.append(' '); + final String trimmedLine = line.trim(); + final int pos = line.indexOf(':'); + final String params = (pos != -1 ? line.substring(pos + 1).trim() : null); + + if ( trimmedLine.startsWith("feature:") ) { + checkConfig(); + + mode = MODE.FEATURE; + feature = model.getOrCreateFeature(params.split(",")); + this.init(feature); + startLevel = feature.getOrCreateStartLevel(0); + + } else if ( trimmedLine.startsWith("variables:") ) { + checkConfig(); + + if ( comment != null ) { + throw new IOException("comment not allowed for variables in line " + this.lineNumberReader.getLineNumber()); } - builder.append(firstLine[i]); - } - final String qualifier = builder.toString(); - - // Parse properties from optional indented lines - // that follow verb line - StringBuilder props = null; - do { - line = lnr.readLine(); - if (line != null && !isVerb(line)) { - if (props == null) { - props = new StringBuilder(); - } - addProperty(props, line); + mode = MODE.VARS; + + } else if ( trimmedLine.startsWith("startLevel:") ) { + checkConfig(); + + if ( feature == null ) { + throw new IOException("startlevel outside of feature in line " + this.lineNumberReader.getLineNumber()); } - } while ( line != null && !isVerb(line)); - - if ( "classpath".equals("verb") ) { - final SSMFeature boot = model.getOrCreateFeature(new String[] {SSMFeature.RUN_MODE_BOOT}); - final SSMArtifact artifact = SSMArtifact.fromMvnUrl(qualifier); - boot.getOrCreateStartLevel(0).getArtifacts().add(artifact); - } else if ( "bundle".equals(verb) ) { - final SSMFeature feature = model.getOrCreateFeature(null); - final SSMArtifact artifact = SSMArtifact.fromMvnUrl(qualifier); - feature.getOrCreateStartLevel(0).getArtifacts().add(artifact); - } else if ( "config".equals(verb) ) { - final SSMFeature feature = model.getOrCreateFeature(null); - boolean felixFormat = false; - final String pid; - if (qualifier.endsWith(FELIX_FORMAT_SUFFIX)) { - felixFormat = true; - pid = qualifier.split(" ")[0].trim(); + int level = (params.length() == 0 ? level = 0 : Integer.valueOf(params)); + startLevel = feature.getOrCreateStartLevel(level); + this.init(startLevel); + mode = MODE.START_LEVEL; + + } else if ( trimmedLine.startsWith("config:FELIX ") || trimmedLine.startsWith("config: ") ) { + checkConfig(); + + mode = MODE.CONFIGURATION; + final int factoryPos = params.indexOf('-'); + if ( factoryPos == -1 ) { + config = new SSMConfiguration(params, null); } else { - pid = qualifier; + config = new SSMConfiguration(params.substring(pos + 1), params.substring(0, pos)); } - final SSMConfiguration config = feature.getOrCreateConfiguration(pid, null); - if ( props != null ) { - processConfigurationProperties(config, props.toString(), felixFormat); + this.init(config); + configBuilder = new StringBuilder(); + configFelixFormat = trimmedLine.startsWith("config:FELIX "); + + } else if ( trimmedLine.startsWith("settings:") ) { + checkConfig(); + + if ( comment != null ) { + throw new IOException("comment not allowed for settings in line " + this.lineNumberReader.getLineNumber()); } - } else if ( "config.factory".equals(verb) ) { - final SSMFeature feature = model.getOrCreateFeature(null); - boolean felixFormat = false; - final String factoryPid; - if (qualifier.endsWith(FELIX_FORMAT_SUFFIX)) { - felixFormat = true; - factoryPid = qualifier.split(" ")[0].trim(); - } else { - factoryPid = qualifier; + if ( startLevel == null ) { + throw new IOException("settings outside of feature/startlevel in line " + this.lineNumberReader.getLineNumber()); } - // create unique alias - final SSMConfiguration config = feature.getOrCreateConfiguration(UUID.randomUUID().toString(), factoryPid); - if ( props != null ) { - processConfigurationProperties(config, props.toString(), felixFormat); + mode = MODE.SETTINGS; + + } else if ( trimmedLine.startsWith("artifact:") ) { + checkConfig(); + + if ( startLevel == null ) { + throw new IOException("artifact outside of feature/startlevel in line " + this.lineNumberReader.getLineNumber()); + } + + mode = MODE.ARTIFACT; + try { + artifact = SSMArtifact.fromMvnUrl("mvn:" + params); + } catch ( final IllegalArgumentException iae) { + throw new IOException(iae.getMessage() + " in line " + this.lineNumberReader.getLineNumber(), iae); + } + this.init(artifact); + startLevel.getArtifacts().add(artifact); + + } else { + switch ( mode ) { + case NONE: throw new IOException("No global contents allowed in line " + this.lineNumberReader.getLineNumber()); + case ARTIFACT : final String[] metadata = parseProperty(trimmedLine); + artifact.getMetadata().put(metadata[0], metadata[1]); + break; + case VARS : final String[] vars = parseProperty(trimmedLine); + model.getVariables().put(vars[0], vars[1]); + break; + case SETTINGS : final String[] settings = parseProperty(trimmedLine); + feature.getSettings().put(settings[0], settings[1]); + break; + case FEATURE: throw new IOException("No contents allowed for feature in line " + this.lineNumberReader.getLineNumber()); + case START_LEVEL: throw new IOException("No contents allowed for feature in line " + this.lineNumberReader.getLineNumber()); + case CONFIGURATION: configBuilder.append(trimmedLine); + configBuilder.append('\n'); + break; } } } + checkConfig(); + if ( comment != null ) { + throw new IOException("Comment not allowed at the end of file"); + } return model; } - private static void processConfigurationProperties(final SSMConfiguration config, final String textValue, - final boolean felixFormat) + private void init(final SSMTraceable traceable) { + traceable.setComment(this.comment); + this.comment = null; + final String number = String.valueOf(this.lineNumberReader.getLineNumber()); + if ( model.getLocation() != null ) { + traceable.setLocation(model.getLocation() + ":" + number); + } else { + traceable.setLocation(number); + } + } + + private void checkConfig() throws IOException { - if ( felixFormat ) { + if ( config != null ) { ByteArrayInputStream bais = null; try { - bais = new ByteArrayInputStream(textValue.getBytes("UTF-8")); + bais = new ByteArrayInputStream(configBuilder.toString().getBytes("UTF-8")); @SuppressWarnings("unchecked") final Dictionary<String, Object> props = ConfigurationHandler.read(bais); final Enumeration<String> e = props.keys(); @@ -148,39 +241,19 @@ public class TXTSSMModelReader { } } } - } else if ( config.isSpecial() ) { - config.getProperties().put(config.getPid(), textValue); - } else { - final LineNumberReader lnr = new LineNumberReader(new StringReader(textValue)); - String line; - while ( (line = lnr.readLine()) != null ) { - final int pos = line.indexOf('='); - config.getProperties().put(line.substring(0, pos), line.substring(pos + 1)); - } } + config = null; + configBuilder = null; } - private static boolean ignore(final String line) { - return line.trim().length() == 0 || line.startsWith("#"); - } - - private static boolean isVerb(final String line) { - return line.length() > 0 && !Character.isWhitespace(line.charAt(0)); - } - - private static void addProperty(final StringBuilder builder, final String line) - throws IOException { + private String[] parseProperty(final String line) throws IOException { final int equalsPos = line.indexOf('='); final String key = line.substring(0, equalsPos).trim(); final String value = line.substring(equalsPos + 1).trim(); - if (key.trim().isEmpty() || value.trim().isEmpty() ) { - throw new IOException("Invalid property line [" + line + "]"); + if (key.isEmpty() || value.isEmpty() ) { + throw new IOException("Invalid property; " + line + " in line " + this.lineNumberReader.getLineNumber()); } - - builder.append(key); - builder.append('='); - builder.append(value); - builder.append('\n'); + return new String[] {key, value}; } } diff --git a/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelWriter.java b/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelWriter.java new file mode 100644 index 0000000..0df8848 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/txt/TXTSSMModelWriter.java @@ -0,0 +1,191 @@ +/* + * 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.slingstart.model.txt; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.Writer; +import java.util.Map; + +import org.apache.felix.cm.file.ConfigurationHandler; +import org.apache.sling.slingstart.model.SSMArtifact; +import org.apache.sling.slingstart.model.SSMConfiguration; +import org.apache.sling.slingstart.model.SSMDeliverable; +import org.apache.sling.slingstart.model.SSMFeature; +import org.apache.sling.slingstart.model.SSMStartLevel; +import org.apache.sling.slingstart.model.SSMTraceable; + +/** + * Simple writer for the a model + */ +public class TXTSSMModelWriter { + + private static void writeComment(final PrintWriter pw, final SSMTraceable traceable) + throws IOException { + if ( traceable.getComment() != null ) { + final LineNumberReader lnr = new LineNumberReader(new StringReader(traceable.getComment())); + try { + String line = null; + while ( (line = lnr.readLine()) != null ) { + pw.print("# "); + pw.println(line); + } + } finally { + lnr.close(); + } + } + } + + /** + * Writes the model to the writer. + * The writer is not closed. + * @param writer + * @param subystem + * @throws IOException + */ + public static void write(final Writer writer, final SSMDeliverable model) + throws IOException { + final PrintWriter pw = new PrintWriter(writer); + + writeComment(pw, model); + // variables + if ( model.getVariables().size() > 0 ) { + pw.println("variables:"); + for(final Map.Entry<String, String> entry : model.getVariables().entrySet()) { + pw.print(" "); + pw.print(entry.getKey()); + pw.print("="); + pw.println(entry.getValue()); + } + pw.println(); + } + + // features + for(final SSMFeature feature : model.getFeatures()) { + // skip empty feature + if ( feature.getConfigurations().isEmpty() && feature.getSettings().isEmpty() ) { + boolean hasArtifacts = false; + for(final SSMStartLevel sl : feature.getStartLevels()) { + if ( !sl.getArtifacts().isEmpty() ) { + hasArtifacts = true; + break; + } + } + if ( !hasArtifacts ) { + continue; + } + } + writeComment(pw, feature); + pw.print("feature:"); + final String[] runModes = feature.getRunModes(); + if ( runModes != null && runModes.length > 0 ) { + pw.print(" "); + boolean first = true; + for(final String mode : runModes) { + if ( first ) { + first = false; + } else { + pw.print(","); + } + pw.print(mode); + } + } + pw.println(); + + // settings + if ( feature.getSettings().size() > 0 ) { + pw.println(" settings:"); + + for(final Map.Entry<String, String> entry :feature.getSettings().entrySet()) { + pw.print(" "); + pw.print(entry.getKey()); + pw.print("="); + pw.println(entry.getValue()); + } + pw.println(); + } + + // start level + for(final SSMStartLevel startLevel : feature.getStartLevels()) { + // skip empty levels + if ( startLevel.getArtifacts().size() == 0 ) { + continue; + } + writeComment(pw, startLevel); + pw.print(" startLevel: "); + pw.print(String.valueOf(startLevel.getLevel())); + pw.println(); + pw.println(); + + // artifacts + for(final SSMArtifact ad : startLevel.getArtifacts()) { + writeComment(pw, ad); + pw.print(" "); + pw.print("artifact: "); + pw.print(ad.toMvnUrl().substring(4)); + pw.println(); + if ( ad.getMetadata().size() > 0 ) { + for(final Map.Entry<String, String> entry : ad.getMetadata().entrySet()) { + pw.print(" "); + pw.print(entry.getKey()); + pw.print("="); + pw.println(entry.getValue()); + } + } + } + if ( startLevel.getArtifacts().size() > 0 ) { + pw.println(); + } + } + + // configurations + for(final SSMConfiguration config : feature.getConfigurations()) { + writeComment(pw, config); + pw.print(" config: "); + if ( config.getFactoryPid() != null ) { + pw.print(config.getFactoryPid()); + pw.print("-"); + } + pw.print(config.getPid()); + pw.println(); + final String configString; + if ( config.isSpecial() ) { + configString = config.getProperties().get(config.getPid()).toString(); + } else { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + ConfigurationHandler.write(os , config.getProperties()); + } finally { + os.close(); + } + configString = new String(os.toByteArray(), "UTF-8"); + } + // we have to read the configuration line by line to properly indent + final LineNumberReader lnr = new LineNumberReader(new StringReader(configString)); + String line = null; + while ((line = lnr.readLine()) != null ) { + pw.print(" "); + pw.println(line); + } + pw.println(); + } + } + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java index 08b975a..bec89cc 100644 --- a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java +++ b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java @@ -23,6 +23,8 @@ import java.io.Reader; import java.io.StringReader; import java.util.Dictionary; import java.util.Enumeration; +import java.util.Map; +import java.util.Properties; import java.util.Stack; import javax.xml.parsers.ParserConfigurationException; @@ -34,6 +36,8 @@ import org.apache.sling.slingstart.model.SSMArtifact; import org.apache.sling.slingstart.model.SSMConfiguration; import org.apache.sling.slingstart.model.SSMDeliverable; import org.apache.sling.slingstart.model.SSMFeature; +import org.apache.sling.slingstart.model.SSMTraceable; +import org.apache.sling.slingstart.model.SSMValidator; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; @@ -114,6 +118,9 @@ public class XMLSSMModelReader { /** Current configuration. */ private SSMConfiguration configuration; + /** Felix config format (default) */ + private boolean isFelixConfigurationFormat = true; + @Override public void startElement(final String uri, final String localName, @@ -159,15 +166,13 @@ public class XMLSSMModelReader { this.feature.getOrCreateStartLevel(this.startLevel).getArtifacts().add(artifact); } else if ( this.mode == MODE.CONFIGURATION || this.mode == MODE.FEATURE_CONFIGURATION) { this.configuration = this.feature.getOrCreateConfiguration(atts.getValue("pid"), atts.getValue("factory")); + this.isFelixConfigurationFormat = !"true".equals(atts.getValue("props")); this.text = new StringBuilder(); } else if ( this.mode == MODE.SETTINGS || this.mode == MODE.FEATURE_SETTINGS) { - if ( this.feature.getSettings() != null ) { - throw new SAXException("Duplicate settings section"); - } this.text = new StringBuilder(); } else if ( this.mode == MODE.FEATURE ) { - final String runMode = atts.getValue("modes"); + final String runMode = atts.getValue("runModes"); if ( runMode == null || runMode.trim().length() == 0 ) { throw new SAXException("Required attribute runModes missing for runMode element"); } @@ -216,26 +221,38 @@ public class XMLSSMModelReader { if ( this.configuration.isSpecial() ) { this.configuration.getProperties().put(this.configuration.getPid(), textValue); } else { - ByteArrayInputStream bais = null; - try { - bais = new ByteArrayInputStream(textValue.getBytes("UTF-8")); - @SuppressWarnings("unchecked") - final Dictionary<String, Object> props = ConfigurationHandler.read(bais); - final Enumeration<String> e = props.keys(); - while ( e.hasMoreElements() ) { - final String key = e.nextElement(); - this.configuration.getProperties().put(key, props.get(key)); - } - } catch ( final IOException ioe ) { - throw new SAXException(ioe); - } finally { - if ( bais != null ) { - try { - bais.close(); - } catch ( final IOException ignore ) { - // ignore + if ( this.isFelixConfigurationFormat ) { + ByteArrayInputStream bais = null; + try { + bais = new ByteArrayInputStream(textValue.getBytes("UTF-8")); + @SuppressWarnings("unchecked") + final Dictionary<String, Object> props = ConfigurationHandler.read(bais); + final Enumeration<String> e = props.keys(); + while ( e.hasMoreElements() ) { + final String key = e.nextElement(); + this.configuration.getProperties().put(key, props.get(key)); + } + } catch ( final IOException ioe ) { + throw new SAXException(ioe); + } finally { + if ( bais != null ) { + try { + bais.close(); + } catch ( final IOException ignore ) { + // ignore + } } } + } else { + final Properties props = new Properties(); + try { + props.load(new StringReader(textValue)); + } catch ( final IOException ioe ) { + throw new SAXException(ioe); + } + for(final Map.Entry<Object, Object> entry : props.entrySet()) { + this.configuration.getProperties().put((String)entry.getKey(), (String)entry.getValue()); + } } } this.configuration = null; @@ -244,6 +261,9 @@ public class XMLSSMModelReader { String line = null; try { while ( (line = reader.readLine()) != null ) { + if ( line.startsWith("#") || line.trim().isEmpty()) { + continue; + } final int pos = line.indexOf("="); if ( pos == -1 || line.indexOf("=", pos + 1 ) != -1 ) { throw new SAXException("Invalid property definition: " + line); @@ -328,10 +348,9 @@ public class XMLSSMModelReader { }); xmlReader.parse(new InputSource(reader)); - try { - result.validate(); - } catch ( final IllegalStateException ise) { - throw (IOException)new IOException("Invalid subsystem definition: " + ise.getMessage()).initCause(ise); + final Map<SSMTraceable, String> errors = new SSMValidator().validate(result); + if ( errors != null ) { + throw new IOException("Invalid model definition: " + errors); } return result; } catch ( final SAXException se) { -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
