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:57:06 UTC
[sling-org-apache-sling-provisioning-model] 10/34: Implement txt
format for reading and writing
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 <cz...@apache.org>
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
"commits@sling.apache.org" <co...@sling.apache.org>.