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 11a204acc52e69f22b55b9d085695eb4276d32b5 Author: Carsten Ziegeler <[email protected]> AuthorDate: Fri Sep 19 08:07:31 2014 +0000 Add slingstart model git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/tooling/support/slingstart-model@1626138 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/sling/slingstart/model/SSMArtifact.java | 144 ++++++++++ .../sling/slingstart/model/SSMConfiguration.java | 67 +++++ .../sling/slingstart/model/SSMConstants.java | 27 ++ .../apache/sling/slingstart/model/SSMRunMode.java | 228 +++++++++++++++ .../apache/sling/slingstart/model/SSMSettings.java | 53 ++++ .../sling/slingstart/model/SSMStartLevel.java | 79 ++++++ .../sling/slingstart/model/SSMSubsystem.java | 167 +++++++++++ .../sling/slingstart/model/package-info.java | 24 ++ .../slingstart/model/xml/XMLSSMModelReader.java | 309 +++++++++++++++++++++ .../slingstart/model/xml/XMLSSMModelWriter.java | 194 +++++++++++++ .../sling/slingstart/model/xml/package-info.java | 24 ++ 11 files changed, 1316 insertions(+) diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java b/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java new file mode 100644 index 0000000..02b7cc1 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMArtifact.java @@ -0,0 +1,144 @@ +/* + * 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; + +/** + * Description of an artifact. + */ +public class SSMArtifact { + + public String groupId; + public String artifactId; + public String version; + public String classifier; + public String type; + + public String getRepositoryPath() { + final StringBuilder sb = new StringBuilder(); + sb.append(groupId.replace('.', '/')); + sb.append('/'); + sb.append(artifactId); + sb.append('/'); + sb.append(version); + sb.append('/'); + sb.append(artifactId); + sb.append('-'); + sb.append(version); + if ( classifier != null ) { + sb.append('-'); + sb.append(classifier); + } + sb.append('.'); + sb.append(type); + 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() { + // trim values first + if ( groupId != null ) groupId = groupId.trim(); + if ( artifactId != null ) artifactId = artifactId.trim(); + if ( version != null ) version = version.trim(); + if ( type != null ) type = type.trim(); + if ( classifier != null ) classifier = classifier.trim(); + + // 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 ( "bundle".equals(type) || type == null || type.isEmpty() ) { + type = "jar"; + } + if ( type == null || type.isEmpty() ) { + throw new IllegalStateException(this + " : type"); + } + if ( classifier != null && classifier.isEmpty() ) { + classifier = null; + } + } + + public static SSMArtifact fromMvnUrl(final String url) { + // 'mvn:' [ repository-url '!' ] group-id '/' artifact-id [ '/' [version] [ '/' [type] [ '/' classifier ] ] ] ] + final String content = url.substring(4); + // ignore repository url + int pos = content.indexOf('!'); + final String coordinates = (pos == -1 ? content : content.substring(pos + 1)); + final SSMArtifact ad = new SSMArtifact(); + int part = 0; + String value = coordinates; + while ( value != null ) { + pos = value.indexOf('/'); + final String current; + if ( pos == -1 ) { + current = value; + value = null; + } else { + if ( pos == 0 ) { + current = null; + } else { + current = value.substring(0, pos); + } + value = value.substring(pos + 1); + } + if ( current != null ) { + if ( part == 0 ) { + ad.groupId = current; + } else if ( part == 1 ) { + ad.artifactId = current; + } else if ( part == 2 ) { + ad.version = current; + } else if ( part == 3 ) { + ad.type = current; + } else if ( part == 4 ) { + ad.classifier = current; + } + } + part++; + } + if ( ad.version == null ) { + ad.version = "LATEST"; + } + if ( ad.type == null || ad.type.length() == 0 || ad.type.equals("bundle") ) { + ad.type = "jar"; + } + return ad; + } + + @Override + public String toString() { + return "CSArtifact [groupId=" + groupId + ", artifactId=" + artifactId + + ", version=" + version + ", classifier=" + classifier + + ", type=" + type + "]"; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java b/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java new file mode 100644 index 0000000..89ab2fe --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMConfiguration.java @@ -0,0 +1,67 @@ +/* + * 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; + + +/** + * Configuration + */ +public class SSMConfiguration { + + public String pid; + + public String factoryPid; + + public String properties; + + /** + * validates the object and throws an IllegalStateException + * This object needs: + * - pid + * - properties + * - factoryPid is optional + * + * @throws IllegalStateException + */ + public void validate() { + // trim values first + if ( pid != null ) pid = pid.trim(); + if ( factoryPid != null ) factoryPid = factoryPid.trim(); + if ( properties != null ) properties = properties.trim(); + + // check/correct values + if ( pid == null || pid.isEmpty() ) { + throw new IllegalStateException("pid"); + } + if ( properties == null || properties.isEmpty() ) { + throw new IllegalStateException("properties"); + } + } + + public boolean isSpecial() { + if ( pid != null && pid.startsWith(":") ) { + return true; + } + return false; + } + + @Override + public String toString() { + return "CSConfiguration [pid=" + pid + ", factoryPid=" + factoryPid + + ", properties=" + properties + "]"; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java b/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java new file mode 100644 index 0000000..ca91467 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMConstants.java @@ -0,0 +1,27 @@ +/* + * 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; + + +public abstract class SSMConstants { + + /** Name of the configuration containing the web.xml. */ + public static final String CFG_WEB_XML = ":web.xml"; + + /** Name of the configuration for the bootstrap contents. */ + public static final String CFG_BOOTSTRAP = ":bootstrap"; +} diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMRunMode.java b/src/main/java/org/apache/sling/slingstart/model/SSMRunMode.java new file mode 100644 index 0000000..3479210 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMRunMode.java @@ -0,0 +1,228 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +/** + * A run mode is a collection of + * - artifacts (through start levels) + * - configurations + * - settings + */ +public class SSMRunMode { + + 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 String[] runModes; + + public final List<SSMStartLevel> startLevels = new ArrayList<SSMStartLevel>(); + + public final List<SSMConfiguration> configurations = new ArrayList<SSMConfiguration>(); + + public SSMSettings settings; + + /** + * validates the object and throws an IllegalStateException + * + * @throws IllegalStateException + */ + public void validate() { + if ( this.runModes != null ) { + boolean hasSpecial = false; + final List<String> modes = new ArrayList<String>(); + for(String m : this.runModes) { + if ( m != null ) m = m.trim(); + if ( m != null && !m.isEmpty()) { + modes.add(m); + if ( m.startsWith(":") ) { + if ( hasSpecial ) { + throw new IllegalStateException("Invalid modes " + Arrays.toString(this.runModes)); + } + hasSpecial = true; + } + } + } + if ( modes.size() == 0 ) { + this.runModes = null; + } else { + this.runModes = modes.toArray(new String[modes.size()]); + } + } + for(final SSMStartLevel sl : this.startLevels) { + sl.validate(); + } + for(final SSMConfiguration c : this.configurations) { + c.validate(); + } + if( settings != null ) { + if (!this.isSpecial() ) { + throw new IllegalStateException("Settings not allowed for custom run modes"); + } + settings.validate(); + } + } + + /** + * Check if this run mode is active wrt the given set of active run modes. + */ + public boolean isActive(final Set<String> activeRunModes) { + boolean active = true; + if ( runModes != null ) { + for(final String mode : runModes) { + if ( !activeRunModes.contains(mode) ) { + active = false; + break; + } + } + } + return active; + } + + /** + * Check whether this run mode is a special run mode + */ + public boolean isSpecial() { + if ( runModes != null && runModes.length == 1 && runModes[0].startsWith(":") ) { + return true; + } + return false; + } + + /** + * Check if this run mode is a specific, single run mode. + */ + public boolean isRunMode(final String mode) { + if ( mode == null && this.runModes == null ) { + return true; + } + if ( mode != null + && this.runModes != null + && this.runModes.length == 1 + && this.runModes[0].equals(mode) ) { + return true; + } + return false; + } + + /** + * Get or create a start level + */ + public SSMStartLevel getOrCreateStartLevel(final int startLevel) { + for(final SSMStartLevel sl : this.startLevels) { + if ( sl.level == startLevel ) { + return sl; + } + } + final SSMStartLevel sl = new SSMStartLevel(); + sl.level = startLevel; + this.startLevels.add(sl); + Collections.sort(this.startLevels, new Comparator<SSMStartLevel>() { + + @Override + public int compare(SSMStartLevel o1, SSMStartLevel o2) { + if ( o1.level < o2.level ) { + return -1; + } else if ( o1.level > o2.level ) { + return 1; + } + return 0; + } + }); + return sl; + } + + /** + * Merge another run mode with this one. + */ + public void merge(final SSMRunMode mode) { + for(final SSMStartLevel sl : mode.startLevels) { + // search for duplicates in other start levels + for(final SSMArtifact artifact : sl.artifacts) { + for(final SSMStartLevel mySL : this.startLevels) { + if ( mySL.level == sl.level ) { + continue; + } + final SSMArtifact myArtifact = mySL.search(artifact); + if ( myArtifact != null ) { + mySL.artifacts.remove(myArtifact); + } + } + } + + final SSMStartLevel mergeSL = this.getOrCreateStartLevel(sl.level); + mergeSL.merge(sl); + } + for(final SSMConfiguration config : mode.configurations) { + SSMConfiguration found = null; + for(final SSMConfiguration current : this.configurations) { + if ( config.factoryPid == null ) { + if ( current.factoryPid == null && current.pid.equals(config.pid) ) { + found = current; + break; + } + } else { + if ( config.factoryPid.equals(current.factoryPid) && current.pid.equals(config.pid) ) { + found = current; + break; + } + } + } + if ( found != null ) { + found.properties = config.properties; + } else { + this.configurations.add(config); + } + } + if ( this.settings == null && mode.settings != null ) { + this.settings = new SSMSettings(); + } + if ( mode.settings != null ) { + this.settings.merge(mode.settings); + } + } + + /** + * Search a configuration with a pid + */ + public SSMConfiguration getConfiguration(final String pid) { + for(final SSMConfiguration c : this.configurations) { + if ( pid.equals(c.pid) ) { + return c; + } + } + return null; + } + + @Override + public String toString() { + return "CSRunMode [runModes=" + Arrays.toString(runModes) + + ", startLevels=" + startLevels + ", configurations=" + + configurations + ", settings=" + settings + "]"; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMSettings.java b/src/main/java/org/apache/sling/slingstart/model/SSMSettings.java new file mode 100644 index 0000000..5b69dfc --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMSettings.java @@ -0,0 +1,53 @@ +/* + * 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; + + +/** + * The settings of a subsystem. + */ +public class SSMSettings { + + public String properties; + + /** + * validates the object and throws an IllegalStateException + * This object needs: + * - properties (non empty) + * + * @throws IllegalStateException + */ + public void validate() { + // check/correct values + if ( properties == null || properties.isEmpty() ) { + throw new IllegalStateException("settings"); + } + } + + public void merge(final SSMSettings other) { + if ( this.properties == null ) { + this.properties = other.properties; + } else { + this.properties = this.properties + "\n" + other.properties; + } + } + + @Override + public String toString() { + return "CSSettings [properties=" + properties + "]"; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java b/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java new file mode 100644 index 0000000..ba60950 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMStartLevel.java @@ -0,0 +1,79 @@ +/* + * 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.ArrayList; +import java.util.List; + +/** + * A start level holds a set of bundles. + */ +public class SSMStartLevel { + + public int level; + + public final List<SSMArtifact> artifacts = new ArrayList<SSMArtifact>(); + + /** + * 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. + */ + public SSMArtifact search(final SSMArtifact template) { + SSMArtifact found = null; + for(final SSMArtifact current : this.artifacts) { + if ( current.groupId.equals(template.groupId) + && current.artifactId.equals(template.artifactId) + && current.classifier.equals(template.classifier) + && current.type.equals(template.type) ) { + found = current; + break; + } + } + return found; + } + + public void merge(final SSMStartLevel other) { + for(final SSMArtifact a : other.artifacts) { + final SSMArtifact found = this.search(a); + if ( found != null ) { + found.version = a.version; + } else { + this.artifacts.add(a); + } + } + } + + @Override + public String toString() { + return "CSStartLevel [level=" + level + ", artifacts=" + artifacts + + "]"; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/SSMSubsystem.java b/src/main/java/org/apache/sling/slingstart/model/SSMSubsystem.java new file mode 100644 index 0000000..bacb0cd --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/SSMSubsystem.java @@ -0,0 +1,167 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A subsystem is a set of run modes and properties. + * The properties can be used for specifying artifact versions. + * At least it has a "global" run mode which describes the common subsystem. + */ +public class SSMSubsystem { + + public final List<SSMRunMode> runModes = new ArrayList<SSMRunMode>(); + + public Map<String, String> properties = new HashMap<String, String>(); + + public SSMSubsystem() { + this.runModes.add(new SSMRunMode()); // global run mode + } + + /** + * Find the run mode if available + * @param runModes + * @return The run mode or null. + */ + private SSMRunMode findRunMode(final String[] runModes) { + SSMRunMode result = null; + for(final SSMRunMode current : this.runModes) { + if ( runModes == null && current.runModes == null ) { + result = current; + break; + } + if ( runModes != null && current.runModes != null ) { + final List<String> a = new ArrayList<String>(Arrays.asList(runModes)); + final List<String> b = new ArrayList<String>(Arrays.asList(current.runModes)); + Collections.sort(a); + Collections.sort(b); + if ( a.equals(b) ) { + result = current; + break; + } + } + } + return result; + } + + /** + * Get the run mode if available + * @return The run mode or null + */ + public SSMRunMode getRunMode(final String runMode) { + return findRunMode(new String[] {runMode}); + } + + /** + * Get or create the run mode. + */ + public SSMRunMode getOrCreateRunMode(final String[] runModes) { + SSMRunMode result = findRunMode(runModes); + if ( result == null ) { + result = new SSMRunMode(); + result.runModes = runModes; + this.runModes.add(result); + Collections.sort(this.runModes, new Comparator<SSMRunMode>() { + + @Override + public int compare(final SSMRunMode o1, final SSMRunMode o2) { + if ( o1.runModes == null ) { + if ( o2.runModes == null ) { + return 0; + } + return -1; + } + if ( o2.runModes == null ) { + return 1; + } + final List<String> a = new ArrayList<String>(Arrays.asList(o1.runModes)); + final List<String> b = new ArrayList<String>(Arrays.asList(o2.runModes)); + Collections.sort(a); + Collections.sort(b); + + return a.toString().compareTo(b.toString()); + } + }); + } + return result; + } + + /** + * validates the object and throws an IllegalStateException + * + * @throws IllegalStateException + */ + public void validate() { + for(final SSMRunMode runMode : this.runModes) { + runMode.validate(); + } + } + + /** + * Replace properties in the string. + * + * @throws IllegalArgumentException + */ + public String getValue(final String v) { + String msg = v; + // check for variables + int pos = -1; + int start = 0; + while ( ( pos = msg.indexOf('$', start) ) != -1 ) { + if ( msg.length() > pos && msg.charAt(pos + 1) == '{' ) { + final int endPos = msg.indexOf('}', pos); + if ( endPos == -1 ) { + start = pos + 1; + } else { + final String name = msg.substring(pos + 2, endPos); + final String value = this.properties.get(name); + if ( value == null ) { + throw new IllegalArgumentException("Unknown variable: " + name); + } + msg = msg.substring(0, pos) + value + msg.substring(endPos + 1); + } + } else { + start = pos + 1; + } + } + return msg; + } + + /** + * Merge two subsystems. + */ + public void merge(final SSMSubsystem other) { + for(final SSMRunMode mode : other.runModes) { + final SSMRunMode mergeRunMode = this.getOrCreateRunMode(mode.runModes); + mergeRunMode.merge(mode); + } + this.properties.putAll(other.properties); + } + + @Override + public String toString() { + return "SSMSubsystem [runModes=" + runModes + ", properties=" + + properties + "]"; + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/package-info.java b/src/main/java/org/apache/sling/slingstart/model/package-info.java new file mode 100644 index 0000000..fc31712 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@Version("1.0") +package org.apache.sling.slingstart.model; + +import aQute.bnd.annotation.Version; + 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 new file mode 100644 index 0000000..48b5554 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelReader.java @@ -0,0 +1,309 @@ +/* + * 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.xml; + +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.Reader; +import java.io.StringReader; +import java.util.Stack; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.sling.slingstart.model.SSMArtifact; +import org.apache.sling.slingstart.model.SSMConfiguration; +import org.apache.sling.slingstart.model.SSMRunMode; +import org.apache.sling.slingstart.model.SSMSettings; +import org.apache.sling.slingstart.model.SSMSubsystem; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * Simple XML parser for the model. + * It ignores all elements in a different namespace than the root element + */ +public class XMLSSMModelReader { + + public enum MODE { + INIT(null, null), + SUBSYSTEM(INIT, "subsystem"), + + PROPERTIES(SUBSYSTEM, "properties"), + + STARTLEVEL(SUBSYSTEM, "startLevel"), + ARTIFACT(SUBSYSTEM, "artifact"), + + STARTLEVEL_ARTIFACT(STARTLEVEL, "artifact"), + + CONFIGURATION(SUBSYSTEM, "configuration"), + SETTINGS(SUBSYSTEM, "settings"), + + RUNMODE(SUBSYSTEM, "runMode"), + RUNMODE_STARTLEVEL(RUNMODE, "startLevel"), + RUNMODE_ARTIFACT(RUNMODE, "artifact"), + RUNMODE_STARTLEVEL_ARTIFACT(RUNMODE_STARTLEVEL, "artifact"), + + RUNMODE_CONFIGURATION(RUNMODE, "configuration"), + RUNMODE_SETTINGS(RUNMODE, "settings"); + + public final MODE fromMode; + public final String elementName; + + MODE(final MODE fm, final String en) { + this.fromMode = fm; + this.elementName = en; + } + } + + /** + * Reads the properties file + * The reader is not closed. + * @throws IOException + */ + public static SSMSubsystem read(final Reader reader) + throws IOException { + try { + final SSMSubsystem result = new SSMSubsystem(); + + final SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + SAXParser saxParser = spf.newSAXParser(); + XMLReader xmlReader = saxParser.getXMLReader(); + xmlReader.setContentHandler(new ContentHandler() { + + private final Stack<String> elementsStack = new Stack<String>(); + + /** The current parsing mode. */ + private MODE mode = MODE.INIT; + + /** String builder to get text from the document. */ + private StringBuilder text; + + /** The namespace for the read xml elements. */ + private String namespace; + + /** The run mode */ + private SSMRunMode runMode; + + /** Current startlevel */ + private int startLevel; + + /** Current configuration. */ + private SSMConfiguration configuration; + + @Override + public void startElement(final String uri, + final String localName, + final String qName, + final Attributes atts) + throws SAXException { + if ( this.mode == MODE.INIT ) { + if ( MODE.SUBSYSTEM.elementName.equals(localName) ) { + this.namespace = uri; + this.mode = MODE.SUBSYSTEM; + this.runMode = result.getOrCreateRunMode(null); + this.startLevel = 0; + } else { + throw new SAXException("Unknown root element (" + localName + "). Document must start with " + MODE.SUBSYSTEM.elementName); + } + } else { + if ( (uri == null && this.namespace == null) || (uri != null && uri.equals(this.namespace)) ) { + boolean found = false; + for(final MODE m : MODE.values()) { + if ( this.mode == m.fromMode && localName.equals(m.elementName) ) { + this.mode = m; + found = true; + break; + } + } + if ( !found ) { + throw new SAXException("Unknown element " + localName); + } + + if ( this.mode == MODE.STARTLEVEL || this.mode == MODE.RUNMODE_STARTLEVEL) { + int level = 0; + final String levelVal = atts.getValue("level"); + if ( levelVal != null ) { + level = Integer.valueOf(levelVal); + } + this.startLevel = level; + } else if ( this.mode == MODE.ARTIFACT || this.mode == MODE.RUNMODE_ARTIFACT || this.mode == MODE.STARTLEVEL_ARTIFACT || this.mode == MODE.RUNMODE_STARTLEVEL_ARTIFACT) { + final SSMArtifact artifact = new SSMArtifact(); + this.runMode.getOrCreateStartLevel(this.startLevel).artifacts.add(artifact); + artifact.groupId = atts.getValue("groupId"); + artifact.artifactId = atts.getValue("artifactId"); + artifact.version = atts.getValue("version"); + artifact.type = atts.getValue("type"); + artifact.classifier = atts.getValue("classifier"); + } else if ( this.mode == MODE.CONFIGURATION || this.mode == MODE.RUNMODE_CONFIGURATION) { + this.configuration = new SSMConfiguration(); + this.configuration.pid = atts.getValue("pid"); + this.configuration.factoryPid = atts.getValue("factory"); + this.runMode.configurations.add(this.configuration); + this.text = new StringBuilder(); + } else if ( this.mode == MODE.SETTINGS || this.mode == MODE.RUNMODE_SETTINGS) { + if ( this.runMode.settings != null ) { + throw new SAXException("Duplicate settings section"); + } + this.runMode.settings = new SSMSettings(); + this.text = new StringBuilder(); + + } else if ( this.mode == MODE.RUNMODE ) { + final String runMode = atts.getValue("modes"); + if ( runMode == null || runMode.trim().length() == 0 ) { + throw new SAXException("Required attribute runModes missing for runMode element"); + } + this.runMode = result.getOrCreateRunMode(runMode.split(",")); + this.startLevel = 0; + + } else { + this.text = new StringBuilder(); + } + } + } + elementsStack.push(localName); + } + + @Override + public void endElement(final String uri, final String localName, final String qName) + throws SAXException { + final String openElement = this.elementsStack.pop(); + if ( !openElement.equals(localName) ) { + throw new SAXException("Invalid document - expected closing " + openElement + " but received " + localName); + } + if ( (uri == null && this.namespace == null) || (uri != null && uri.equals(this.namespace)) ) { + String textValue = (text != null ? text.toString() : null); + if ( textValue != null ) { + textValue = textValue.trim(); + if ( textValue.length() == 0 ) { + textValue = null; + } + } + text = null; + boolean found = false; + final MODE prevMode = this.mode; + for(final MODE m : MODE.values()) { + if ( this.mode == m && localName.equals(m.elementName) ) { + this.mode = m.fromMode; + found = true; + break; + } + } + if ( !found ) { + throw new SAXException("Unknown element " + localName); + } + if ( prevMode == MODE.STARTLEVEL || prevMode == MODE.RUNMODE_STARTLEVEL ) { + this.startLevel = 0; + } else if ( prevMode == MODE.CONFIGURATION || prevMode == MODE.RUNMODE_CONFIGURATION ) { + this.configuration.properties = textValue; + this.configuration = null; + } else if ( prevMode == MODE.SETTINGS || prevMode == MODE.RUNMODE_SETTINGS) { + this.runMode.settings.properties = textValue; + } else if ( prevMode == MODE.RUNMODE ) { + this.runMode = result.getOrCreateRunMode(null); + this.startLevel = 0; + } else if ( prevMode == MODE.PROPERTIES ) { + final LineNumberReader reader = new LineNumberReader(new StringReader(textValue)); + String line = null; + try { + while ( (line = reader.readLine()) != null ) { + final int pos = line.indexOf("="); + if ( pos == -1 || line.indexOf("=", pos + 1 ) != -1 ) { + throw new SAXException("Invalid property definition: " + line); + } + result.properties.put(line.substring(0, pos), line.substring(pos + 1)); + } + } catch (final IOException io) { + throw new SAXException(io); + } + } + } + } + + @Override + public void characters(final char[] ch, final int start, final int length) + throws SAXException { + if ( text != null ) { + text.append(ch, start, length); + } + } + + @Override + public void startDocument() throws SAXException { + // nothing to do + } + + @Override + public void skippedEntity(final String name) throws SAXException { + // nothing to do + } + + @Override + public void setDocumentLocator(final Locator locator) { + // nothing to do + } + + @Override + public void processingInstruction(final String target, final String data) + throws SAXException { + // nothing to do + } + + @Override + public void ignorableWhitespace(final char[] ch, final int start,final int length) + throws SAXException { + // nothing to do + } + + @Override + public void startPrefixMapping(final String prefix, final String uri) + throws SAXException { + // nothing to do + } + + @Override + public void endPrefixMapping(final String prefix) throws SAXException { + // nothing to do + } + + @Override + public void endDocument() throws SAXException { + // nothing to do + } + + }); + xmlReader.parse(new InputSource(reader)); + + try { + result.validate(); + } catch ( final IllegalStateException ise) { + throw (IOException)new IOException("Invalid subsystem definition: " + ise.getMessage()).initCause(ise); + } + return result; + } catch ( final SAXException se) { + throw new IOException(se); + } catch ( final ParserConfigurationException pce ) { + throw new IOException(pce); + } + } +} diff --git a/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java new file mode 100644 index 0000000..acced8d --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/xml/XMLSSMModelWriter.java @@ -0,0 +1,194 @@ +/* + * 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.xml; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.Map; + +import org.apache.sling.slingstart.model.SSMArtifact; +import org.apache.sling.slingstart.model.SSMConfiguration; +import org.apache.sling.slingstart.model.SSMRunMode; +import org.apache.sling.slingstart.model.SSMStartLevel; +import org.apache.sling.slingstart.model.SSMSubsystem; + +/** + * Simple writer for the a model + */ +public class XMLSSMModelWriter { + + private static void printRunModeAttribute(final PrintWriter pw, final SSMRunMode rmd) { + if ( rmd.runModes != null && rmd.runModes.length > 0 ) { + pw.print(" modes=\""); + boolean first = true; + for(final String mode : rmd.runModes) { + if ( first ) { + first = false; + } else { + pw.print(","); + } + pw.print(escapeXml(mode)); + } + pw.print("\""); + } + + } + + private static String INDENT = " "; + + + /** + * 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 SSMSubsystem subsystem) + throws IOException { + final PrintWriter pw = new PrintWriter(writer); + pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + pw.println("<subsystem>"); + + // properties + if ( subsystem.properties.size() > 0 ) { + pw.print(INDENT); + pw.println("<properties><![CDATA["); + for(final Map.Entry<String, String> entry : subsystem.properties.entrySet()) { + pw.print(INDENT); + pw.print(INDENT); + pw.print(entry.getKey()); + pw.print("="); + pw.println(entry.getValue()); + } + pw.print(INDENT); + pw.println("]]></properties>"); + } + for(final SSMRunMode runMode : subsystem.runModes) { + // TODO - don't write out empty run modes + String indent = INDENT; + if ( runMode.runModes != null ) { + pw.print(indent); + pw.print("<runMode"); + printRunModeAttribute(pw, runMode); + pw.println(">"); + indent = indent + INDENT; + } + + for(final SSMStartLevel startLevel : runMode.startLevels) { + if ( startLevel.artifacts.size() == 0 ) { + continue; + } + if ( startLevel.level != 0 ) { + pw.print(indent); + pw.print("<startLevel"); + pw.print(" level=\""); + pw.print(String.valueOf(startLevel.level)); + pw.print("\""); + pw.println(">"); + indent += INDENT; + } + for(final SSMArtifact ad : startLevel.artifacts) { + pw.print(indent); + pw.print("<artifact groupId=\""); + pw.print(escapeXml(ad.groupId)); + pw.print("\" artifactId=\""); + pw.print(escapeXml(ad.artifactId)); + pw.print("\" version=\""); + pw.print(escapeXml(ad.version)); + pw.print("\""); + if ( !"jar".equals(ad.type) ) { + pw.print(" type=\""); + pw.print(escapeXml(ad.type)); + pw.print("\""); + } + if ( ad.classifier != null ) { + pw.print(" classifier=\""); + pw.print(escapeXml(ad.classifier)); + pw.print("\""); + } + pw.println("/>"); + } + if ( startLevel.level != 0 ) { + indent = indent.substring(0, indent.length() - INDENT.length()); + pw.print(indent); + pw.println("</startLevel>"); + } + } + + for(final SSMConfiguration config : runMode.configurations) { + pw.print(indent); + pw.print("<configuration "); + if ( config.factoryPid != null ) { + pw.print("factory=\""); + pw.print(escapeXml(config.factoryPid)); + pw.print("\" "); + } + pw.print("pid=\""); + pw.print(escapeXml(config.pid)); + pw.println("\"><![CDATA["); + pw.println(config.properties); + pw.print(indent); + pw.println("]]></configuration>"); + } + + if ( runMode.settings != null ) { + pw.print(indent); + pw.println("<settings><![CDATA["); + pw.println(runMode.settings.properties); + pw.print(indent); + pw.println("]]></settings>"); + } + + if ( runMode.runModes != null ) { + indent = indent.substring(0, indent.length() - INDENT.length()); + pw.print(indent); + pw.println("</runMode>"); + } + } + + pw.println("</subsystem>"); + } + + /** Escape xml text */ + private static String escapeXml(final String input) { + if (input == null) { + return null; + } + + final StringBuilder b = new StringBuilder(input.length()); + for(int i = 0;i < input.length(); i++) { + final char c = input.charAt(i); + if(c == '&') { + b.append("&"); + } else if(c == '<') { + b.append("<"); + } else if(c == '>') { + b.append(">"); + } else if(c == '"') { + b.append("""); + } else if(c == '\'') { + b.append("'"); + } else { + b.append(c); + } + } + return b.toString(); + } + +} diff --git a/src/main/java/org/apache/sling/slingstart/model/xml/package-info.java b/src/main/java/org/apache/sling/slingstart/model/xml/package-info.java new file mode 100644 index 0000000..ee86132 --- /dev/null +++ b/src/main/java/org/apache/sling/slingstart/model/xml/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@Version("1.0") +package org.apache.sling.slingstart.model.xml; + +import aQute.bnd.annotation.Version; + -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
