You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/04/11 19:20:48 UTC
[26/33] Revert "[KARAF-2852] Merge features/core and features/command"
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java
new file mode 100644
index 0000000..56e5102
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java
@@ -0,0 +1,102 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.URI;
+
+import org.apache.karaf.features.Repository;
+import org.apache.karaf.features.internal.model.Features;
+import org.apache.karaf.features.internal.model.JaxbUtil;
+
+/**
+ * The repository implementation.
+ */
+public class RepositoryImpl implements Repository {
+
+ private final URI uri;
+ private Features features;
+
+ public RepositoryImpl(URI uri) {
+ this.uri = uri;
+ }
+
+ public URI getURI() {
+ return uri;
+ }
+
+ public String getName() throws IOException {
+ load();
+ return features.getName();
+ }
+
+ public URI[] getRepositories() throws Exception {
+ load();
+ URI[] result = new URI[features.getRepository().size()];
+ for (int i = 0; i < features.getRepository().size(); i++) {
+ String uri = features.getRepository().get(i);
+ uri = uri.trim();
+ result[i] = URI.create(uri);
+ }
+ return result;
+ }
+
+ public org.apache.karaf.features.Feature[] getFeatures() throws Exception {
+ load();
+ return features.getFeature().toArray(new org.apache.karaf.features.Feature[features.getFeature().size()]);
+ }
+
+
+ public void load() throws IOException {
+ load(false);
+ }
+
+ public void load(boolean validate) throws IOException {
+ if (features == null) {
+ try {
+ InputStream inputStream = uri.toURL().openStream();
+ inputStream = new FilterInputStream(inputStream) {
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedIOException();
+ }
+ return super.read(b, off, len);
+ }
+ };
+ try {
+ features = JaxbUtil.unmarshal(uri.toASCIIString(), inputStream, validate);
+ } finally {
+ inputStream.close();
+ }
+ } catch (IllegalArgumentException e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ } catch (Exception e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ }
+ }
+ }
+
+ @Override
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java
new file mode 100644
index 0000000..96b99ee
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java
@@ -0,0 +1,84 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.karaf.features.internal.resolver.CapabilitySet;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class RequirementSort<T extends Resource> {
+
+ /**
+ * Sorts {@link Resource} based on their {@link Requirement}s and {@link Capability}s.
+ */
+ public static <T extends Resource> Collection<T> sort(Collection<T> resources) {
+ Set<String> namespaces = new HashSet<String>();
+ for (Resource r : resources) {
+ for (Capability cap : r.getCapabilities(null)) {
+ namespaces.add(cap.getNamespace());
+ }
+ }
+ CapabilitySet capSet = new CapabilitySet(new ArrayList<String>(namespaces));
+ for (Resource r : resources) {
+ for (Capability cap : r.getCapabilities(null)) {
+ capSet.addCapability(cap);
+ }
+ }
+ Set<T> sorted = new LinkedHashSet<T>();
+ Set<T> visited = new LinkedHashSet<T>();
+ for (T r : resources) {
+ visit(r, visited, sorted, capSet);
+ }
+ return sorted;
+ }
+
+
+ private static <T extends Resource> void visit(T resource, Set<T> visited, Set<T> sorted, CapabilitySet capSet) {
+ if (!visited.add(resource)) {
+ return;
+ }
+ for (T r : collectDependencies(resource, capSet)) {
+ visit(r, visited, sorted, capSet);
+ }
+ sorted.add(resource);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Resource> Set<T> collectDependencies(T resource, CapabilitySet capSet) {
+ Set<T> result = new LinkedHashSet<T>();
+ for (Requirement requirement : resource.getRequirements(null)) {
+ String filter = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+ SimpleFilter sf = (filter != null)
+ ? SimpleFilter.parse(filter)
+ : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ for (Capability cap : capSet.match(sf, true)) {
+ result.add((T) cap.getResource());
+ }
+ }
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java
new file mode 100644
index 0000000..d1f16b9
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java
@@ -0,0 +1,51 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.karaf.features.internal.deployment.Downloader;
+import org.apache.karaf.features.internal.deployment.StreamProvider;
+import org.apache.karaf.features.internal.util.MultiException;
+
+public class SimpleDownloader implements Downloader {
+
+ private final MultiException exception = new MultiException("Error");
+
+ @Override
+ public void await() throws InterruptedException, MultiException {
+ exception.throwIfExceptions();
+ }
+
+ @Override
+ public void download(final String location, final DownloadCallback downloadCallback) throws MalformedURLException {
+ final URL url = new URL(location);
+ try {
+ downloadCallback.downloaded(new StreamProvider() {
+ @Override
+ public InputStream open() throws IOException {
+ return url.openStream();
+ }
+ });
+ } catch (Exception e) {
+ exception.addException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java
new file mode 100644
index 0000000..c84f4e0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java
@@ -0,0 +1,34 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class State {
+
+ public final AtomicBoolean bootDone = new AtomicBoolean();
+ public final Set<String> repositories = new TreeSet<String>();
+ public final Set<String> features = new TreeSet<String>();
+ public final Set<String> installedFeatures = new TreeSet<String>();
+ public final Set<Long> managedBundles = new TreeSet<Long>();
+ public final Map<String, Long> bundleChecksums = new HashMap<String, Long>();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java
new file mode 100644
index 0000000..ac54ab0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java
@@ -0,0 +1,175 @@
+/*
+ * 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.karaf.features.internal.service;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.karaf.features.Feature;
+
+public abstract class StateStorage {
+
+ public void load(State state) throws IOException {
+ state.repositories.clear();
+ state.features.clear();
+ state.installedFeatures.clear();
+ state.managedBundles.clear();
+ InputStream is = getInputStream();
+ if (is != null) {
+ try {
+ Properties props = new Properties();
+ props.load(is);
+ state.bootDone.set(loadBool(props, "bootDone"));
+ state.repositories.addAll(loadSet(props, "repositories."));
+ state.features.addAll(loadSet(props, "features."));
+ state.installedFeatures.addAll(loadSet(props, "installed."));
+ state.managedBundles.addAll(toLongSet(loadSet(props, "managed.")));
+ state.bundleChecksums.putAll(toStringLongMap(loadMap(props, "checksums.")));
+ } finally {
+ close(is);
+ }
+ }
+ }
+
+ public void save(State state) throws IOException {
+ OutputStream os = getOutputStream();
+ if (os != null) {
+ try {
+ Properties props = new Properties();
+ saveBool(props, "bootDone", state.bootDone.get());
+ saveSet(props, "repositories.", state.repositories);
+ saveSet(props, "features.", state.features);
+ saveSet(props, "installed.", state.installedFeatures);
+ saveSet(props, "managed.", toStringSet(state.managedBundles));
+ saveMap(props, "checksums.", toStringStringMap(state.bundleChecksums));
+ props.store(os, "FeaturesService State");
+ } finally {
+ close(os);
+ }
+ }
+ }
+
+ protected abstract InputStream getInputStream() throws IOException;
+ protected abstract OutputStream getOutputStream() throws IOException;
+
+ protected boolean loadBool(Properties props, String key) {
+ return Boolean.parseBoolean(props.getProperty(key));
+ }
+
+ protected void saveBool(Properties props, String key, boolean val) {
+ props.setProperty(key, Boolean.toString(val));
+ }
+
+ protected Set<String> toStringSet(Set<Long> set) {
+ Set<String> ns = new TreeSet<String>();
+ for (long l : set) {
+ ns.add(Long.toString(l));
+ }
+ return ns;
+ }
+
+ protected Set<Long> toLongSet(Set<String> set) {
+ Set<Long> ns = new TreeSet<Long>();
+ for (String s : set) {
+ ns.add(Long.parseLong(s));
+ }
+ return ns;
+ }
+
+ protected void saveSet(Properties props, String prefix, Set<String> set) {
+ List<String> l = new ArrayList<String>(set);
+ props.put(prefix + "count", Integer.toString(l.size()));
+ for (int i = 0; i < l.size(); i++) {
+ props.put(prefix + "item." + i, l.get(i));
+ }
+ }
+
+ protected Set<String> loadSet(Properties props, String prefix) {
+ Set<String> l = new HashSet<String>();
+ String countStr = (String) props.get(prefix + "count");
+ if (countStr != null) {
+ int count = Integer.parseInt(countStr);
+ for (int i = 0; i < count; i++) {
+ l.add((String) props.get(prefix + "item." + i));
+ }
+ }
+ return l;
+ }
+
+ protected Map<String, String> toStringStringMap(Map<String, Long> map) {
+ Map<String, String> nm = new HashMap<String, String>();
+ for (Map.Entry<String, Long> entry : map.entrySet()) {
+ nm.put(entry.getKey(), Long.toString(entry.getValue()));
+ }
+ return nm;
+ }
+
+ protected Map<String, Long> toStringLongMap(Map<String, String> map) {
+ Map<String, Long> nm = new HashMap<String, Long>();
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ nm.put(entry.getKey(), Long.parseLong(entry.getValue()));
+ }
+ return nm;
+ }
+
+
+ protected void saveMap(Properties props, String prefix, Map<String, String> map) {
+ List<Map.Entry<String, String>> l = new ArrayList<Map.Entry<String, String>>(map.entrySet());
+ props.put(prefix + "count", Integer.toString(l.size()));
+ for (int i = 0; i < l.size(); i++) {
+ props.put(prefix + "key." + i, l.get(i).getKey());
+ props.put(prefix + "val." + i, l.get(i).getValue());
+ }
+ }
+
+ protected Map<String, String> loadMap(Properties props, String prefix) {
+ Map<String, String> l = new HashMap<String, String>();
+ String countStr = (String) props.get(prefix + "count");
+ if (countStr != null) {
+ int count = Integer.parseInt(countStr);
+ for (int i = 0; i < count; i++) {
+ String key = (String) props.get(prefix + "key." + i);
+ String val = (String) props.get(prefix + "val." + i);
+ l.put(key, val);
+ }
+ }
+ return l;
+ }
+
+
+ protected void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java
new file mode 100644
index 0000000..19fc706
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.karaf.features.internal.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.CRC32;
+
+public class ChecksumUtils {
+
+ private ChecksumUtils() {
+ }
+
+ /**
+ * Compute a cheksum for the file or directory that consists of the name, length and the last modified date
+ * for a file and its children in case of a directory
+ *
+ * @param is the input stream
+ * @return a checksum identifying any change
+ */
+ public static long checksum(InputStream is) throws IOException
+ {
+ try {
+ CRC32 crc = new CRC32();
+ byte[] buffer = new byte[8192];
+ int l;
+ while ((l = is.read(buffer)) > 0) {
+ crc.update(buffer, 0, l);
+ }
+ return crc.getValue();
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java
new file mode 100644
index 0000000..a53d8a5
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java
@@ -0,0 +1,349 @@
+/*
+ * 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.karaf.features.internal.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ */
+public class JsonReader {
+
+ public static Object read(Reader reader) throws IOException {
+ return new JsonReader(reader).parse();
+ }
+
+ public static Object read(InputStream is) throws IOException {
+ return new JsonReader(new InputStreamReader(is)).parse();
+ }
+
+ //
+ // Implementation
+ //
+
+ private final Reader reader;
+ private final StringBuilder recorder;
+ private int current;
+ private int line = 1;
+ private int column = 0;
+
+ JsonReader(Reader reader) {
+ this.reader = reader;
+ recorder = new StringBuilder();
+ }
+
+ public Object parse() throws IOException {
+ read();
+ skipWhiteSpace();
+ Object result = readValue();
+ skipWhiteSpace();
+ if (!endOfText()) {
+ throw error("Unexpected character");
+ }
+ return result;
+ }
+
+ private Object readValue() throws IOException {
+ switch (current) {
+ case 'n':
+ return readNull();
+ case 't':
+ return readTrue();
+ case 'f':
+ return readFalse();
+ case '"':
+ return readString();
+ case '[':
+ return readArray();
+ case '{':
+ return readObject();
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return readNumber();
+ default:
+ throw expected("value");
+ }
+ }
+
+ private Collection<?> readArray() throws IOException {
+ read();
+ Collection<Object> array = new ArrayList<Object>();
+ skipWhiteSpace();
+ if (readChar(']')) {
+ return array;
+ }
+ do {
+ skipWhiteSpace();
+ array.add(readValue());
+ skipWhiteSpace();
+ } while (readChar(','));
+ if (!readChar(']')) {
+ throw expected("',' or ']'");
+ }
+ return array;
+ }
+
+ private Map<String, Object> readObject() throws IOException {
+ read();
+ Map<String, Object> object = new HashMap<String, Object>();
+ skipWhiteSpace();
+ if (readChar('}')) {
+ return object;
+ }
+ do {
+ skipWhiteSpace();
+ String name = readName();
+ skipWhiteSpace();
+ if (!readChar(':')) {
+ throw expected("':'");
+ }
+ skipWhiteSpace();
+ object.put(name, readValue());
+ skipWhiteSpace();
+ } while (readChar(','));
+ if (!readChar('}')) {
+ throw expected("',' or '}'");
+ }
+ return object;
+ }
+
+ private Object readNull() throws IOException {
+ read();
+ readRequiredChar('u');
+ readRequiredChar('l');
+ readRequiredChar('l');
+ return null;
+ }
+
+ private Boolean readTrue() throws IOException {
+ read();
+ readRequiredChar('r');
+ readRequiredChar('u');
+ readRequiredChar('e');
+ return Boolean.TRUE;
+ }
+
+ private Boolean readFalse() throws IOException {
+ read();
+ readRequiredChar('a');
+ readRequiredChar('l');
+ readRequiredChar('s');
+ readRequiredChar('e');
+ return Boolean.FALSE;
+ }
+
+ private void readRequiredChar(char ch) throws IOException {
+ if (!readChar(ch)) {
+ throw expected("'" + ch + "'");
+ }
+ }
+
+ private String readString() throws IOException {
+ read();
+ recorder.setLength(0);
+ while (current != '"') {
+ if (current == '\\') {
+ readEscape();
+ } else if (current < 0x20) {
+ throw expected("valid string character");
+ } else {
+ recorder.append((char) current);
+ read();
+ }
+ }
+ read();
+ return recorder.toString();
+ }
+
+ private void readEscape() throws IOException {
+ read();
+ switch (current) {
+ case '"':
+ case '/':
+ case '\\':
+ recorder.append((char) current);
+ break;
+ case 'b':
+ recorder.append('\b');
+ break;
+ case 'f':
+ recorder.append('\f');
+ break;
+ case 'n':
+ recorder.append('\n');
+ break;
+ case 'r':
+ recorder.append('\r');
+ break;
+ case 't':
+ recorder.append('\t');
+ break;
+ case 'u':
+ char[] hexChars = new char[4];
+ for (int i = 0; i < 4; i++) {
+ read();
+ if (!isHexDigit(current)) {
+ throw expected("hexadecimal digit");
+ }
+ hexChars[i] = (char) current;
+ }
+ recorder.append((char) Integer.parseInt(String.valueOf(hexChars), 16));
+ break;
+ default:
+ throw expected("valid escape sequence");
+ }
+ read();
+ }
+
+ private Number readNumber() throws IOException {
+ recorder.setLength(0);
+ readAndAppendChar('-');
+ int firstDigit = current;
+ if (!readAndAppendDigit()) {
+ throw expected("digit");
+ }
+ if (firstDigit != '0') {
+ while (readAndAppendDigit()) {
+ }
+ }
+ readFraction();
+ readExponent();
+ return Double.parseDouble(recorder.toString());
+ }
+
+ private boolean readFraction() throws IOException {
+ if (!readAndAppendChar('.')) {
+ return false;
+ }
+ if (!readAndAppendDigit()) {
+ throw expected("digit");
+ }
+ while (readAndAppendDigit()) {
+ }
+ return true;
+ }
+
+ private boolean readExponent() throws IOException {
+ if (!readAndAppendChar('e') && !readAndAppendChar('E')) {
+ return false;
+ }
+ if (!readAndAppendChar('+')) {
+ readAndAppendChar('-');
+ }
+ if (!readAndAppendDigit()) {
+ throw expected("digit");
+ }
+ while (readAndAppendDigit()) {
+ }
+ return true;
+ }
+
+ private String readName() throws IOException {
+ if (current != '"') {
+ throw expected("name");
+ }
+ readString();
+ return recorder.toString();
+ }
+
+ private boolean readAndAppendChar(char ch) throws IOException {
+ if (current != ch) {
+ return false;
+ }
+ recorder.append(ch);
+ read();
+ return true;
+ }
+
+ private boolean readChar(char ch) throws IOException {
+ if (current != ch) {
+ return false;
+ }
+ read();
+ return true;
+ }
+
+ private boolean readAndAppendDigit() throws IOException {
+ if (!isDigit(current)) {
+ return false;
+ }
+ recorder.append((char) current);
+ read();
+ return true;
+ }
+
+ private void skipWhiteSpace() throws IOException {
+ while (isWhiteSpace(current) && !endOfText()) {
+ read();
+ }
+ }
+
+ private void read() throws IOException {
+ if (endOfText()) {
+ throw error("Unexpected end of input");
+ }
+ column++;
+ if (current == '\n') {
+ line++;
+ column = 0;
+ }
+ current = reader.read();
+ }
+
+ private boolean endOfText() {
+ return current == -1;
+ }
+
+ private IOException expected(String expected) {
+ if (endOfText()) {
+ return error("Unexpected end of input");
+ }
+ return error("Expected " + expected);
+ }
+
+ private IOException error(String message) {
+ return new IOException(message + " at " + line + ":" + column);
+ }
+
+ private static boolean isWhiteSpace(int ch) {
+ return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
+ }
+
+ private static boolean isDigit(int ch) {
+ return ch >= '0' && ch <= '9';
+ }
+
+ private static boolean isHexDigit(int ch) {
+ return ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F';
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
new file mode 100644
index 0000000..cba27fb
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
@@ -0,0 +1,120 @@
+/*
+ * 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.karaf.features.internal.util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ */
+public class JsonWriter {
+
+ public static void write(Writer writer, Object value) throws IOException {
+ if (value instanceof Map) {
+ writeObject(writer, (Map) value);
+ } else if (value instanceof Collection) {
+ writeArray(writer, (Collection) value);
+ } else if (value instanceof Number) {
+ writeNumber(writer, (Number) value);
+ } else if (value instanceof String) {
+ writeString(writer, (String) value);
+ } else if (value instanceof Boolean) {
+ writeBoolean(writer, (Boolean) value);
+ } else if (value == null) {
+ writeNull(writer);
+ } else {
+ throw new IllegalArgumentException("Unsupported value: " + value);
+ }
+ }
+
+ private static void writeObject(Writer writer, Map<?, ?> value) throws IOException {
+ writer.append('{');
+ boolean first = true;
+ for (Map.Entry entry : value.entrySet()) {
+ if (!first) {
+ writer.append(',');
+ } else {
+ first = false;
+ }
+ writeString(writer, (String) entry.getKey());
+ writer.append(':');
+ write(writer, entry.getValue());
+ }
+ writer.append('}');
+ }
+
+ private static void writeString(Writer writer, String value) throws IOException {
+ writer.append('"');
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ writer.append('\\');
+ writer.append(c);
+ break;
+ default:
+ if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+ String s = Integer.toHexString(c);
+ writer.append('\\');
+ writer.append('u');
+ for (int j = s.length(); j < 4; j++) {
+ writer.append('0');
+ }
+ writer.append(s);
+ } else {
+ writer.append(c);
+ }
+ break;
+ }
+ }
+ writer.append('"');
+ }
+
+ private static void writeNumber(Writer writer, Number value) throws IOException {
+ writer.append(value.toString());
+ }
+
+ private static void writeBoolean(Writer writer, Boolean value) throws IOException {
+ writer.append(Boolean.toString(value));
+ }
+
+ private static void writeArray(Writer writer, Collection<?> value) throws IOException {
+ writer.append('[');
+ boolean first = true;
+ for (Object obj : value) {
+ if (!first) {
+ writer.append(',');
+ } else {
+ first = false;
+ }
+ write(writer, obj);
+ }
+ writer.append(']');
+ }
+
+ private static void writeNull(Writer writer) throws IOException {
+ writer.append("null");
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java
new file mode 100644
index 0000000..d30b7b5
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java
@@ -0,0 +1,142 @@
+/*
+ * 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.karaf.features.internal.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.utils.version.VersionTable;
+import org.osgi.framework.Version;
+
+public class Macro {
+
+ public static String transform(String macro, String value) {
+ if (macro.startsWith("${") && macro.endsWith("}")) {
+ String[] args = macro.substring(2, macro.length() - 1).split(";");
+ if ("version".equals(args[0])) {
+ if (args.length != 2) {
+ throw new IllegalArgumentException("Invalid syntax for macro: " + macro);
+ }
+ return version(args[1], VersionTable.getVersion(value));
+ } else if ("range".equals(args[0])) {
+ if (args.length != 2) {
+ throw new IllegalArgumentException("Invalid syntax for macro: " + macro);
+ }
+ return range(args[1], VersionTable.getVersion(value));
+ } else {
+ throw new IllegalArgumentException("Unknown macro: " + macro);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Modify a version to set a version policy. Thed policy is a mask that is
+ * mapped to a version.
+ *
+ * <pre>
+ * + increment
+ * - decrement
+ * = maintain
+ * ˜ discard
+ *
+ * ==+ = maintain major, minor, increment micro, discard qualifier
+ * ˜˜˜= = just get the qualifier
+ * version="[${version;==;${@}},${version;=+;${@}})"
+ * </pre>
+ *
+ * @param args
+ * @return
+ */
+ final static String MASK_STRING = "[\\-+=~0123456789]{0,3}[=~]?";
+
+ static String version(String mask, Version version) {
+ StringBuilder sb = new StringBuilder();
+ String del = "";
+
+ for (int i = 0; i < mask.length(); i++) {
+ char c = mask.charAt(i);
+ String result = null;
+ if (c != '~') {
+ if (i > 3) {
+ throw new IllegalArgumentException("Version mask can only specify 3 digits");
+ } else if (i == 3) {
+ result = version.getQualifier();
+ if (result.isEmpty()) {
+ result = null;
+ }
+ } else if (Character.isDigit(c)) {
+ // Handle masks like +00, =+0
+ result = String.valueOf(c);
+ } else {
+ int x = 0;
+ switch (i) {
+ case 0: x = version.getMajor(); break;
+ case 1: x = version.getMinor(); break;
+ case 2: x = version.getMicro(); break;
+ }
+ switch (c) {
+ case '+' :
+ x++;
+ break;
+ case '-' :
+ x--;
+ break;
+ case '=' :
+ break;
+ }
+ result = Integer.toString(x);
+ }
+ if (result != null) {
+ sb.append(del);
+ del = ".";
+ sb.append(result);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Schortcut for version policy
+ *
+ * <pre>
+ * -provide-policy : ${policy;[==,=+)}
+ * -consume-policy : ${policy;[==,+)}
+ * </pre>
+ *
+ * @param args
+ * @return
+ */
+
+ static Pattern RANGE_MASK = Pattern.compile("(\\[|\\()(" + MASK_STRING + "),(" + MASK_STRING + ")(\\]|\\))");
+
+ static String range(String spec, Version version) {
+ Matcher m = RANGE_MASK.matcher(spec);
+ m.matches();
+ String floor = m.group(1);
+ String floorMask = m.group(2);
+ String ceilingMask = m.group(3);
+ String ceiling = m.group(4);
+
+ String left = version(floorMask, version);
+ String right = version(ceilingMask, version);
+
+ return floor + left + "," + right + ceiling;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java
new file mode 100644
index 0000000..36af452
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java
@@ -0,0 +1,95 @@
+/*
+ * 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.karaf.features.internal.util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class MultiException extends Exception {
+
+ private List<Exception> exceptions = new ArrayList<Exception>();
+
+ public MultiException(String message) {
+ super(message);
+ }
+
+ public MultiException(String message, List<Exception> exceptions) {
+ super(message);
+ this.exceptions = exceptions;
+ }
+
+ public void addException(Exception e) {
+ exceptions.add(e);
+ }
+
+ public void throwIfExceptions() throws MultiException {
+ if (!exceptions.isEmpty()) {
+ throw this;
+ }
+ }
+
+ public Throwable[] getCauses() {
+ return exceptions.toArray(new Throwable[exceptions.size()]);
+ }
+
+ @Override
+ public void printStackTrace()
+ {
+ super.printStackTrace();
+ for (Exception e : exceptions) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /* ------------------------------------------------------------------------------- */
+ /**
+ * @see Throwable#printStackTrace(java.io.PrintStream)
+ */
+ @Override
+ public void printStackTrace(PrintStream out)
+ {
+ super.printStackTrace(out);
+ for (Exception e : exceptions) {
+ e.printStackTrace(out);
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintWriter out)
+ {
+ super.printStackTrace(out);
+ for (Exception e : exceptions) {
+ e.printStackTrace(out);
+ }
+ }
+
+ public static void throwIf(String message, List<Exception> exceptions) throws MultiException {
+ if (exceptions != null && !exceptions.isEmpty()) {
+ StringBuilder sb = new StringBuilder(message);
+ sb.append(":");
+ for (Exception e : exceptions) {
+ sb.append("\n\t");
+ sb.append(e.getMessage());
+ }
+ throw new MultiException(sb.toString(), exceptions);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java b/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
new file mode 100644
index 0000000..6afbbed
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
@@ -0,0 +1,142 @@
+/*
+ * 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.karaf.features.management;
+
+import javax.management.openmbean.TabularData;
+
+public interface FeaturesServiceMBean {
+
+ TabularData getFeatures() throws Exception;
+
+ TabularData getRepositories() throws Exception;
+
+ void addRepository(String url) throws Exception;
+
+ void addRepository(String url, boolean install) throws Exception;
+
+ void removeRepository(String url) throws Exception;
+
+ void removeRepository(String url, boolean uninstall) throws Exception;
+
+ void installFeature(String name) throws Exception;
+
+ void installFeature(String name, boolean noRefresh) throws Exception;
+
+ void installFeature(String name, boolean noRefresh, boolean noStart) throws Exception;
+
+ void installFeature(String name, String version) throws Exception;
+
+ void installFeature(String name, String version, boolean noRefresh) throws Exception;
+
+ void installFeature(String name, String version, boolean noRefresh, boolean noStart) throws Exception;
+
+ TabularData infoFeature(String name) throws Exception;
+
+ TabularData infoFeature(String name, String version) throws Exception;
+
+ void uninstallFeature(String name) throws Exception;
+
+ void uninstallFeature(String name, boolean noRefresh) throws Exception;
+
+ void uninstallFeature(String name, String version) throws Exception;
+
+ void uninstallFeature(String name, String version, boolean noRefresh) throws Exception;
+
+ String FEATURE_NAME = "Name";
+
+ String FEATURE_VERSION = "Version";
+
+ String FEATURE_DEPENDENCIES = "Dependencies";
+
+ String FEATURE_BUNDLES = "Bundles";
+
+ String FEATURE_CONFIGURATIONS = "Configurations";
+
+ String FEATURE_CONFIGURATIONFILES = "Configuration Files";
+
+ String FEATURE_INSTALLED = "Installed";
+
+ String FEATURE_CONFIG_PID = "Pid";
+ String FEATURE_CONFIG_ELEMENTS = "Elements";
+ String FEATURE_CONFIG_ELEMENT_KEY = "Key";
+ String FEATURE_CONFIG_ELEMENT_VALUE = "Value";
+
+ String FEATURE_CONFIG_FILES_ELEMENTS = "Files";
+
+ /**
+ * The type of the event which is emitted for features events
+ */
+ String FEATURE_EVENT_TYPE = "org.apache.karaf.features.featureEvent";
+
+ String FEATURE_EVENT_EVENT_TYPE = "Type";
+
+ String FEATURE_EVENT_EVENT_TYPE_INSTALLED = "Installed";
+
+ String FEATURE_EVENT_EVENT_TYPE_UNINSTALLED = "Uninstalled";
+
+ /**
+ * The item names in the CompositeData representing a feature
+ */
+ String[] FEATURE = { FEATURE_NAME, FEATURE_VERSION, FEATURE_DEPENDENCIES, FEATURE_BUNDLES,
+ FEATURE_CONFIGURATIONS, FEATURE_CONFIGURATIONFILES, FEATURE_INSTALLED };
+
+ String[] FEATURE_IDENTIFIER = { FEATURE_NAME, FEATURE_VERSION };
+
+ String[] FEATURE_CONFIG = { FEATURE_CONFIG_PID, FEATURE_CONFIG_ELEMENTS };
+
+ String[] FEATURE_CONFIG_FILES = { FEATURE_CONFIG_FILES_ELEMENTS };
+
+ String[] FEATURE_CONFIG_ELEMENT = { FEATURE_CONFIG_ELEMENT_KEY, FEATURE_CONFIG_ELEMENT_VALUE };
+
+ /**
+ * The item names in the CompositeData representing the event raised for
+ * feature events within the OSGi container by this bean
+ */
+ String[] FEATURE_EVENT = { FEATURE_NAME, FEATURE_VERSION, FEATURE_EVENT_EVENT_TYPE };
+
+
+ String REPOSITORY_NAME = "Name";
+
+ String REPOSITORY_URI = "Uri";
+
+ String REPOSITORY_REPOSITORIES = "Repositories";
+
+ String REPOSITORY_FEATURES = "Features";
+
+ /**
+ * The type of the event which is emitted for repositories events
+ */
+ String REPOSITORY_EVENT_TYPE = "org.apache.karaf.features.repositoryEvent";
+
+ String REPOSITORY_EVENT_EVENT_TYPE = "Type";
+
+ String REPOSITORY_EVENT_EVENT_TYPE_ADDED = "Added";
+
+ String REPOSITORY_EVENT_EVENT_TYPE_REMOVED = "Removed";
+
+ /**
+ * The item names in the CompositeData representing a feature
+ */
+ String[] REPOSITORY = { REPOSITORY_NAME, REPOSITORY_URI, REPOSITORY_REPOSITORIES, REPOSITORY_FEATURES };
+
+ /**
+ * The item names in the CompositeData representing the event raised for
+ * feature events within the OSGi container by this bean
+ */
+ String[] REPOSITORY_EVENT = { REPOSITORY_URI, REPOSITORY_EVENT_EVENT_TYPE };
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
new file mode 100644
index 0000000..54fa3c0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeature.java
@@ -0,0 +1,323 @@
+/*
+ * 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.karaf.features.management.codec;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.TabularDataSupport;
+
+import org.apache.karaf.features.BundleInfo;
+import org.apache.karaf.features.ConfigFileInfo;
+import org.apache.karaf.features.Dependency;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.management.FeaturesServiceMBean;
+
+public class JmxFeature {
+
+ /**
+ * The CompositeType which represents a single feature
+ */
+ public final static CompositeType FEATURE;
+
+ /**
+ * The TabularType which represents a list of features
+ */
+ public final static TabularType FEATURE_TABLE;
+
+ public final static CompositeType FEATURE_IDENTIFIER;
+
+ public final static TabularType FEATURE_IDENTIFIER_TABLE;
+
+ public final static CompositeType FEATURE_CONFIG_ELEMENT;
+
+ public final static TabularType FEATURE_CONFIG_ELEMENT_TABLE;
+
+ public final static CompositeType FEATURE_CONFIG;
+
+ public final static TabularType FEATURE_CONFIG_TABLE;
+
+ public final static CompositeType FEATURE_CONFIG_FILES;
+
+ public final static TabularType FEATURE_CONFIG_FILES_TABLE;
+
+ private final CompositeData data;
+
+ public JmxFeature(Feature feature, boolean installed) {
+ try {
+ String[] itemNames = FeaturesServiceMBean.FEATURE;
+ Object[] itemValues = new Object[itemNames.length];
+ itemValues[0] = feature.getName();
+ itemValues[1] = feature.getVersion();
+ itemValues[2] = getDependencyIdentifierTable(feature.getDependencies());
+ itemValues[3] = getBundleUris(feature.getBundles());
+ itemValues[4] = getConfigTable(feature.getConfigurations());
+ itemValues[5] = getConfigFileList(feature.getConfigurationFiles());
+ itemValues[6] = installed;
+ data = new CompositeDataSupport(FEATURE, itemNames, itemValues);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Cannot form feature open data", e);
+ }
+ }
+
+ public CompositeData asCompositeData() {
+ return data;
+ }
+
+ public static TabularData tableFrom(Collection<JmxFeature> features) {
+ TabularDataSupport table = new TabularDataSupport(FEATURE_TABLE);
+ for (JmxFeature feature : features) {
+ table.put(feature.asCompositeData());
+ }
+ return table;
+ }
+
+ private static TabularData getDependencyIdentifierTable(List<Dependency> features) throws OpenDataException {
+ TabularDataSupport table = new TabularDataSupport(FEATURE_IDENTIFIER_TABLE);
+ Set<String> featureSet = new HashSet<String>();
+ for (Dependency feature : features) {
+ if (featureSet.contains(feature.getName() + feature.getVersion())) {
+ continue;
+ } else {
+ featureSet.add(feature.getName() + feature.getVersion());
+ }
+ String[] itemNames = new String[] { FeaturesServiceMBean.FEATURE_NAME, FeaturesServiceMBean.FEATURE_VERSION };
+ Object[] itemValues = new Object[] { feature.getName(), feature.getVersion() };
+ CompositeData ident = new CompositeDataSupport(FEATURE_IDENTIFIER, itemNames, itemValues);
+ table.put(ident);
+ }
+ return table;
+ }
+
+ static String[] getBundleUris(List<BundleInfo> infos) {
+ String[] array = new String[infos.size()];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = infos.get(i).getLocation();
+ }
+ return array;
+ }
+
+ static TabularData getConfigTable(Map<String, Map<String, String>> configs) throws OpenDataException {
+ TabularDataSupport table = new TabularDataSupport(FEATURE_CONFIG_TABLE);
+ for (Map.Entry<String, Map<String, String>> entry : configs.entrySet()) {
+ String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG;
+ Object[] itemValues = new Object[2];
+ itemValues[0] = entry.getKey();
+ itemValues[1] = getConfigElementTable(entry.getValue());
+ CompositeData config = new CompositeDataSupport(FEATURE_CONFIG, itemNames, itemValues);
+ table.put(config);
+ }
+ return table;
+ }
+
+ static TabularData getConfigFileList(List<ConfigFileInfo> configFiles) throws OpenDataException {
+ TabularDataSupport table = new TabularDataSupport(FEATURE_CONFIG_FILES_TABLE);
+ for (ConfigFileInfo configFile : configFiles) {
+ String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG_FILES;
+ Object[] itemValues = { configFile.getFinalname() };
+ CompositeData config = new CompositeDataSupport(FEATURE_CONFIG_FILES, itemNames, itemValues);
+ table.put(config);
+ }
+ return table;
+ }
+
+ static TabularData getConfigElementTable(Map<String, String> config) throws OpenDataException {
+ TabularDataSupport table = new TabularDataSupport(FEATURE_CONFIG_ELEMENT_TABLE);
+ for (Map.Entry<String, String> entry : config.entrySet()) {
+ String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG_ELEMENT;
+ Object[] itemValues = { entry.getKey(), entry.getValue() };
+ CompositeData element = new CompositeDataSupport(FEATURE_CONFIG_ELEMENT, itemNames, itemValues);
+ table.put(element);
+ }
+ return table;
+ }
+
+
+ static {
+ FEATURE_IDENTIFIER = createFeatureIdentifierType();
+ FEATURE_IDENTIFIER_TABLE = createFeatureIdentifierTableType();
+ FEATURE_CONFIG_ELEMENT = createFeatureConfigElementType();
+ FEATURE_CONFIG_ELEMENT_TABLE = createFeatureConfigElementTableType();
+ FEATURE_CONFIG = createFeatureConfigType();
+ FEATURE_CONFIG_TABLE = createFeatureConfigTableType();
+ FEATURE_CONFIG_FILES = createFeatureConfigFilesType();
+ FEATURE_CONFIG_FILES_TABLE = createFeatureConfigFilesTableType();
+ FEATURE = createFeatureType();
+ FEATURE_TABLE = createFeatureTableType();
+ }
+
+ private static CompositeType createFeatureIdentifierType() {
+ try {
+ String description = "This type identify a Karaf features";
+ String[] itemNames = FeaturesServiceMBean.FEATURE_IDENTIFIER;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = SimpleType.STRING;
+
+ itemDescriptions[0] = "The id of the feature";
+ itemDescriptions[1] = "The version of the feature";
+
+ return new CompositeType("FeatureIdentifier", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build featureIdentifier type", e);
+ }
+ }
+
+ private static TabularType createFeatureIdentifierTableType() {
+ try {
+ return new TabularType("Features", "The table of featureIdentifiers",
+ FEATURE_IDENTIFIER, new String[] { FeaturesServiceMBean.FEATURE_NAME, FeaturesServiceMBean.FEATURE_VERSION });
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build featureIdentifier table type", e);
+ }
+ }
+
+ private static CompositeType createFeatureConfigElementType() {
+ try {
+ String description = "This type encapsulates Karaf feature config element";
+ String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG_ELEMENT;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = SimpleType.STRING;
+
+ itemDescriptions[0] = "The key";
+ itemDescriptions[1] = "The value";
+
+ return new CompositeType("ConfigElement", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build configElement type", e);
+ }
+ }
+
+ private static TabularType createFeatureConfigElementTableType() {
+ try {
+ return new TabularType("ConfigElement", "The table of configurations elements",
+ FEATURE_CONFIG_ELEMENT, new String[] { FeaturesServiceMBean.FEATURE_CONFIG_ELEMENT_KEY});
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build feature table type", e);
+ }
+ }
+
+ private static CompositeType createFeatureConfigType() {
+ try {
+ String description = "This type encapsulates Karaf feature config";
+ String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = FEATURE_CONFIG_ELEMENT_TABLE;
+
+ itemDescriptions[0] = "The PID of the config";
+ itemDescriptions[1] = "The configuration elements";
+
+ return new CompositeType("Config", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build configElement type", e);
+ }
+ }
+
+ private static CompositeType createFeatureConfigFilesType() {
+ try {
+ String description = "This type encapsulates Karaf feature config files";
+ String[] itemNames = FeaturesServiceMBean.FEATURE_CONFIG_FILES;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+
+ itemDescriptions[0] = "The configuration file";
+
+ return new CompositeType("Config", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build configElement type", e);
+ }
+ }
+
+ private static TabularType createFeatureConfigTableType() {
+ try {
+ return new TabularType("Features", "The table of configurations",
+ FEATURE_CONFIG, new String[] { FeaturesServiceMBean.FEATURE_CONFIG_PID});
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build feature table type", e);
+ }
+ }
+
+ private static TabularType createFeatureConfigFilesTableType() {
+ try {
+ return new TabularType("Features", "The table of configuration files",
+ FEATURE_CONFIG_FILES, new String[] { FeaturesServiceMBean.FEATURE_CONFIG_FILES_ELEMENTS });
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build feature table type", e);
+ }
+ }
+
+ private static CompositeType createFeatureType() {
+ try {
+ String description = "This type encapsulates Karaf features";
+ String[] itemNames = FeaturesServiceMBean.FEATURE;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = SimpleType.STRING;
+ itemTypes[2] = FEATURE_IDENTIFIER_TABLE;
+ itemTypes[3] = new ArrayType(1, SimpleType.STRING);
+ itemTypes[4] = FEATURE_CONFIG_TABLE;
+ itemTypes[5] = FEATURE_CONFIG_FILES_TABLE;
+ itemTypes[6] = SimpleType.BOOLEAN;
+
+ itemDescriptions[0] = "The name of the feature";
+ itemDescriptions[1] = "The version of the feature";
+ itemDescriptions[2] = "The feature dependencies";
+ itemDescriptions[3] = "The feature bundles";
+ itemDescriptions[4] = "The feature configurations";
+ itemDescriptions[5] = "The feature configuration files";
+ itemDescriptions[6] = "Whether the feature is installed";
+
+ return new CompositeType("Feature", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build feature type", e);
+ }
+ }
+
+ private static TabularType createFeatureTableType() {
+ try {
+ return new TabularType("Features", "The table of all features",
+ FEATURE, new String[] { FeaturesServiceMBean.FEATURE_NAME, FeaturesServiceMBean.FEATURE_VERSION });
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build feature table type", e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeatureEvent.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeatureEvent.java b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeatureEvent.java
new file mode 100644
index 0000000..81f446b
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxFeatureEvent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.karaf.features.management.codec;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.apache.karaf.features.FeatureEvent;
+import org.apache.karaf.features.management.FeaturesServiceMBean;
+
+public class JmxFeatureEvent {
+
+ public static final CompositeType FEATURE_EVENT;
+
+ private final CompositeData data;
+
+ public JmxFeatureEvent(FeatureEvent event) {
+ try {
+ String[] itemNames = FeaturesServiceMBean.FEATURE_EVENT;
+ Object[] itemValues = new Object[itemNames.length];
+ itemValues[0] = event.getFeature().getName();
+ itemValues[1] = event.getFeature().getVersion();
+ switch (event.getType()) {
+ case FeatureInstalled: itemValues[2] = FeaturesServiceMBean.FEATURE_EVENT_EVENT_TYPE_INSTALLED; break;
+ case FeatureUninstalled: itemValues[2] = FeaturesServiceMBean.FEATURE_EVENT_EVENT_TYPE_UNINSTALLED; break;
+ default: throw new IllegalStateException("Unsupported event type: " + event.getType());
+ }
+ data = new CompositeDataSupport(FEATURE_EVENT, itemNames, itemValues);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Cannot form feature event open data", e);
+ }
+ }
+
+ public CompositeData asCompositeData() {
+ return data;
+ }
+
+ static {
+ FEATURE_EVENT = createFeatureEventType();
+ }
+
+ private static CompositeType createFeatureEventType() {
+ try {
+ String description = "This type identify a Karaf feature event";
+ String[] itemNames = FeaturesServiceMBean.FEATURE_EVENT;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = SimpleType.STRING;
+ itemTypes[2] = SimpleType.STRING;
+
+ itemDescriptions[0] = "The id of the feature";
+ itemDescriptions[1] = "The version of the feature";
+ itemDescriptions[2] = "The type of the event";
+
+ return new CompositeType("FeatureEvent", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build featureEvent type", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepository.java b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepository.java
new file mode 100644
index 0000000..fee1ab2
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepository.java
@@ -0,0 +1,132 @@
+/*
+ * 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.karaf.features.management.codec;
+
+import java.util.Collection;
+import java.util.Arrays;
+import java.net.URI;
+import java.util.List;
+
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.TabularType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.CompositeDataSupport;
+
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.Repository;
+import org.apache.karaf.features.management.FeaturesServiceMBean;
+
+public class JmxRepository {
+
+ public final static CompositeType REPOSITORY;
+
+ public final static TabularType REPOSITORY_TABLE;
+
+ private final CompositeData data;
+
+ public JmxRepository(Repository repository) {
+ try {
+ String[] itemNames = FeaturesServiceMBean.REPOSITORY;
+ Object[] itemValues = new Object[itemNames.length];
+ itemValues[0] = repository.getName();
+ itemValues[1] = repository.getURI().toString();
+ itemValues[2] = toStringArray(repository.getRepositories());
+ itemValues[3] = getFeatureIdentifierTable(Arrays.asList(repository.getFeatures()));
+ data = new CompositeDataSupport(REPOSITORY, itemNames, itemValues);
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot form repository open data", e);
+ }
+ }
+
+ public CompositeData asCompositeData() {
+ return data;
+ }
+
+ public static TabularData tableFrom(Collection<JmxRepository> repositories) {
+ TabularDataSupport table = new TabularDataSupport(REPOSITORY_TABLE);
+ for (JmxRepository repository : repositories) {
+ table.put(repository.asCompositeData());
+ }
+ return table;
+ }
+
+ private static String[] toStringArray(URI[] uris) {
+ if (uris == null) {
+ return null;
+ }
+ String[] res = new String[uris.length];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = uris[i].toString();
+ }
+ return res;
+ }
+
+ static TabularData getFeatureIdentifierTable(List<Feature> features) throws OpenDataException {
+ TabularDataSupport table = new TabularDataSupport(JmxFeature.FEATURE_IDENTIFIER_TABLE);
+ for (Feature feature : features) {
+ String[] itemNames = new String[] { FeaturesServiceMBean.FEATURE_NAME, FeaturesServiceMBean.FEATURE_VERSION };
+ Object[] itemValues = new Object[] { feature.getName(), feature.getVersion() };
+ CompositeData ident = new CompositeDataSupport(JmxFeature.FEATURE_IDENTIFIER, itemNames, itemValues);
+ table.put(ident);
+ }
+ return table;
+ }
+
+ static {
+ REPOSITORY = createRepositoryType();
+ REPOSITORY_TABLE = createRepositoryTableType();
+ }
+
+ private static CompositeType createRepositoryType() {
+ try {
+ String description = "This type identify a Karaf repository";
+ String[] itemNames = FeaturesServiceMBean.REPOSITORY;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = SimpleType.STRING;
+ itemTypes[2] = new ArrayType(1, SimpleType.STRING);
+ itemTypes[3] = JmxFeature.FEATURE_IDENTIFIER_TABLE;
+
+ itemDescriptions[0] = "The name of the repository";
+ itemDescriptions[1] = "The uri of the repository";
+ itemDescriptions[2] = "The dependent repositories";
+ itemDescriptions[3] = "The list of included features";
+
+ return new CompositeType("Repository", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build repository type", e);
+ }
+ }
+
+ private static TabularType createRepositoryTableType() {
+ try {
+ return new TabularType("Features", "The table of repositories",
+ REPOSITORY, new String[] { FeaturesServiceMBean.REPOSITORY_URI });
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build repository table type", e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
new file mode 100644
index 0000000..e00e85d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
@@ -0,0 +1,77 @@
+/*
+ * 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.karaf.features.management.codec;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.apache.karaf.features.RepositoryEvent;
+import org.apache.karaf.features.management.FeaturesServiceMBean;
+
+public class JmxRepositoryEvent {
+
+ public static final CompositeType REPOSITORY_EVENT;
+
+ private final CompositeData data;
+
+ public JmxRepositoryEvent(RepositoryEvent event) {
+ try {
+ String[] itemNames = FeaturesServiceMBean.REPOSITORY_EVENT;
+ Object[] itemValues = new Object[itemNames.length];
+ itemValues[0] = event.getRepository().getURI().toString();
+ switch (event.getType()) {
+ case RepositoryAdded: itemValues[1] = FeaturesServiceMBean.REPOSITORY_EVENT_EVENT_TYPE_ADDED; break;
+ case RepositoryRemoved: itemValues[1] = FeaturesServiceMBean.REPOSITORY_EVENT_EVENT_TYPE_REMOVED; break;
+ default: throw new IllegalStateException("Unsupported event type: " + event.getType());
+ }
+ data = new CompositeDataSupport(REPOSITORY_EVENT, itemNames, itemValues);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Cannot form repository event open data", e);
+ }
+ }
+
+ public CompositeData asCompositeData() {
+ return data;
+ }
+
+ static {
+ REPOSITORY_EVENT = createRepositoryEventType();
+ }
+
+ private static CompositeType createRepositoryEventType() {
+ try {
+ String description = "This type identify a Karaf repository event";
+ String[] itemNames = FeaturesServiceMBean.REPOSITORY_EVENT;
+ OpenType[] itemTypes = new OpenType[itemNames.length];
+ String[] itemDescriptions = new String[itemNames.length];
+ itemTypes[0] = SimpleType.STRING;
+ itemTypes[1] = SimpleType.STRING;
+
+ itemDescriptions[0] = "The uri of the repository";
+ itemDescriptions[1] = "The type of event";
+
+ return new CompositeType("RepositoryEvent", description, itemNames,
+ itemDescriptions, itemTypes);
+ } catch (OpenDataException e) {
+ throw new IllegalStateException("Unable to build repositoryEvent type", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/features/core/src/main/resources/OSGI-INF/bundle.info b/features/core/src/main/resources/OSGI-INF/bundle.info
new file mode 100644
index 0000000..d5b4180
--- /dev/null
+++ b/features/core/src/main/resources/OSGI-INF/bundle.info
@@ -0,0 +1,20 @@
+h1. Synopsis
+
+${project.name}
+
+${project.description}
+
+Maven URL:
+[mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+This bundle is the core implementation of the Karaf features support.
+
+Karaf provides a simple, yet flexible, way to provision applications or "features". Such a mechanism is mainly
+provided by a set of commands available in the features shell. The provisioning system uses xml "repositories"
+that define a set of features.
+
+h1. See also
+
+Provisioning - section of the Karaf User Guide