Repository: camel Updated Branches: refs/heads/master 1a3fa8fea -> 8ee52c744
CAMEL-9157: JMX - Add explain to data format so we can know how exactly they are configured at runtime Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/f6525494 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/f6525494 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/f6525494 Branch: refs/heads/master Commit: f6525494a0890a9feb6ca0107d4e1a336e5e9cb7 Parents: 1a3fa8f Author: Claus Ibsen <davscl...@apache.org> Authored: Fri Sep 25 09:32:47 2015 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Fri Sep 25 09:32:47 2015 +0200 ---------------------------------------------------------------------- .../java/org/apache/camel/CamelContext.java | 9 ++ .../management/mbean/CamelOpenMBeanTypes.java | 13 ++ .../mbean/ManagedDataFormatMBean.java | 3 + .../apache/camel/impl/DefaultCamelContext.java | 153 +++++++++++++++++++ .../org/apache/camel/impl/GzipDataFormat.java | 8 +- .../camel/impl/SerializationDataFormat.java | 8 +- .../org/apache/camel/impl/StringDataFormat.java | 8 +- .../org/apache/camel/impl/ZipDataFormat.java | 10 +- .../management/mbean/ManagedDataFormat.java | 61 +++++++- .../org/apache/camel/spi/DataFormatName.java | 28 ++++ .../camel/management/ManagedDataFormatTest.java | 19 +++ 11 files changed, 311 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/CamelContext.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/CamelContext.java b/camel-core/src/main/java/org/apache/camel/CamelContext.java index 9a1509a..7c2840f 100644 --- a/camel-core/src/main/java/org/apache/camel/CamelContext.java +++ b/camel-core/src/main/java/org/apache/camel/CamelContext.java @@ -1559,6 +1559,15 @@ public interface CamelContext extends SuspendableService, RuntimeConfiguration { String explainComponentJson(String componentName, boolean includeAllOptions); /** + * Returns a JSON schema representation of the component parameters (not endpoint parameters) for the given component by its id. + * + * @param dataFormat the data format instance. + * @param includeAllOptions whether to include non configured options also (eg default options) + * @return the json + */ + String explainDataFormatJson(String dataFormatName, DataFormat dataFormat, boolean includeAllOptions); + + /** * Returns a JSON schema representation of the endpoint parameters for the given endpoint uri. * * @param uri the endpoint uri http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java b/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java index 71f32f9..549a2f6 100644 --- a/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java +++ b/camel-core/src/main/java/org/apache/camel/api/management/mbean/CamelOpenMBeanTypes.java @@ -89,6 +89,19 @@ public final class CamelOpenMBeanTypes { SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.STRING}); } + public static TabularType explainDataFormatTabularType() throws OpenDataException { + CompositeType ct = explainDataFormatsCompositeType(); + return new TabularType("explainDataFormat", "Explain how this dataformat is configured", ct, new String[]{"option"}); + } + + public static CompositeType explainDataFormatsCompositeType() throws OpenDataException { + return new CompositeType("dataformats", "DataFormats", new String[]{"option", "kind", "label", "type", "java type", "deprecated", "value", "default value", "description"}, + new String[]{"Option", "Kind", "Label", "Type", "Java Type", "Deprecated", "Value", "Default Value", "Description"}, + new OpenType[]{SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, + SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.STRING}); + } + + public static TabularType explainEndpointTabularType() throws OpenDataException { CompositeType ct = explainEndpointsCompositeType(); return new TabularType("explainEndpoint", "Explain how this endpoint is configured", ct, new String[]{"option"}); http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedDataFormatMBean.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedDataFormatMBean.java b/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedDataFormatMBean.java index c5ed0e0..62b33e0 100644 --- a/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedDataFormatMBean.java +++ b/camel-core/src/main/java/org/apache/camel/api/management/mbean/ManagedDataFormatMBean.java @@ -23,6 +23,9 @@ import org.apache.camel.api.management.ManagedOperation; public interface ManagedDataFormatMBean { + @ManagedAttribute(description = "The name of the data format") + String getName(); + @ManagedAttribute(description = "Camel ID") String getCamelId(); http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java index 1c5227f..5d432a9 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java @@ -1639,6 +1639,159 @@ public class DefaultCamelContext extends ServiceSupport implements ModelCamelCon } } + public String explainDataFormatJson(String dataFormatName, DataFormat dataFormat, boolean includeAllOptions) { + try { + String json = getDataFormatParameterJsonSchema(dataFormatName); + if (json == null) { + return null; + } + + List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true); + + // selected rows to use for answer + Map<String, String[]> selected = new LinkedHashMap<String, String[]>(); + Map<String, String[]> dataFormatOptions = new LinkedHashMap<String, String[]>(); + + // extract options from the data format + Map<String, Object> options = new LinkedHashMap<String, Object>(); + IntrospectionSupport.getProperties(dataFormat, options, "", false); + + for (Map.Entry<String, Object> entry : options.entrySet()) { + String name = entry.getKey(); + String value = ""; + if (entry.getValue() != null) { + value = entry.getValue().toString(); + } + value = URISupport.sanitizePath(value); + + // find type and description from the json schema + String type = null; + String kind = null; + String label = null; + String required = null; + String javaType = null; + String deprecated = null; + String defaultValue = null; + String description = null; + for (Map<String, String> row : rows) { + if (name.equals(row.get("name"))) { + type = row.get("type"); + kind = row.get("kind"); + label = row.get("label"); + required = row.get("required"); + javaType = row.get("javaType"); + deprecated = row.get("deprecated"); + defaultValue = row.get("defaultValue"); + description = row.get("description"); + break; + } + } + + // remember this option from the uri + dataFormatOptions.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description}); + } + + // include other rows + for (Map<String, String> row : rows) { + String name = row.get("name"); + String kind = row.get("kind"); + String label = row.get("label"); + String required = row.get("required"); + String value = row.get("value"); + String defaultValue = row.get("defaultValue"); + String type = row.get("type"); + String javaType = row.get("javaType"); + String deprecated = row.get("deprecated"); + value = URISupport.sanitizePath(value); + String description = row.get("description"); + + boolean isDataFormatOption = dataFormatOptions.containsKey(name); + + // always include from uri or path options + if (includeAllOptions || isDataFormatOption) { + if (!selected.containsKey(name)) { + // add as selected row, but take the value from uri options if it was from there + if (isDataFormatOption) { + selected.put(name, dataFormatOptions.get(name)); + } else { + selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description}); + } + } + } + } + + json = ObjectHelper.before(json, " \"properties\": {"); + + StringBuilder buffer = new StringBuilder(" \"properties\": {"); + + boolean first = true; + for (String[] row : selected.values()) { + if (first) { + first = false; + } else { + buffer.append(","); + } + buffer.append("\n "); + + String name = row[0]; + String kind = row[1]; + String label = row[2]; + String required = row[3]; + String type = row[4]; + String javaType = row[5]; + String deprecated = row[6]; + String value = row[7]; + String defaultValue = row[8]; + String description = row[9]; + + // add json of the option + buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { "); + CollectionStringBuffer csb = new CollectionStringBuffer(); + if (kind != null) { + csb.append("\"kind\": \"" + kind + "\""); + } + if (label != null) { + csb.append("\"label\": \"" + label + "\""); + } + if (required != null) { + csb.append("\"required\": \"" + required + "\""); + } + if (type != null) { + csb.append("\"type\": \"" + type + "\""); + } + if (javaType != null) { + csb.append("\"javaType\": \"" + javaType + "\""); + } + if (deprecated != null) { + csb.append("\"deprecated\": \"" + deprecated + "\""); + } + if (value != null) { + csb.append("\"value\": \"" + value + "\""); + } + if (defaultValue != null) { + csb.append("\"defaultValue\": \"" + defaultValue + "\""); + } + if (description != null) { + csb.append("\"description\": \"" + description + "\""); + } + if (!csb.isEmpty()) { + buffer.append(csb.toString()); + } + buffer.append(" }"); + } + + buffer.append("\n }\n}\n"); + + // insert the original first part of the json into the start of the buffer + buffer.insert(0, json); + return buffer.toString(); + + } catch (Exception e) { + // ignore and return empty response + return null; + } + } + public String explainComponentJson(String componentName, boolean includeAllOptions) { try { String json = getComponentParameterJsonSchema(componentName); http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/impl/GzipDataFormat.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/GzipDataFormat.java b/camel-core/src/main/java/org/apache/camel/impl/GzipDataFormat.java index d89460a..7770687 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/GzipDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/impl/GzipDataFormat.java @@ -24,13 +24,19 @@ import java.util.zip.GZIPOutputStream; import org.apache.camel.Exchange; import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatName; import org.apache.camel.support.*; import org.apache.camel.util.IOHelper; /** * GZip {@link org.apache.camel.spi.DataFormat} for reading/writing data using gzip. */ -public class GzipDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat { +public class GzipDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat, DataFormatName { + + @Override + public String getDataFormatName() { + return "gzip"; + } public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception { InputStream is = exchange.getContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, graph); http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/impl/SerializationDataFormat.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/SerializationDataFormat.java b/camel-core/src/main/java/org/apache/camel/impl/SerializationDataFormat.java index eb2fe24..25f313d 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/SerializationDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/impl/SerializationDataFormat.java @@ -24,6 +24,7 @@ import java.io.OutputStream; import org.apache.camel.Exchange; import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatName; import org.apache.camel.util.ExchangeHelper; /** @@ -32,7 +33,12 @@ import org.apache.camel.util.ExchangeHelper; * * @version */ -public class SerializationDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat { +public class SerializationDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat, DataFormatName { + + @Override + public String getDataFormatName() { + return "serialization"; + } public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception { ObjectOutput out = ExchangeHelper.convertToMandatoryType(exchange, ObjectOutput.class, stream); http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/impl/StringDataFormat.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/StringDataFormat.java b/camel-core/src/main/java/org/apache/camel/impl/StringDataFormat.java index 545c482..8003f84 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/StringDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/impl/StringDataFormat.java @@ -22,6 +22,7 @@ import java.io.OutputStream; import org.apache.camel.Exchange; import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatName; import org.apache.camel.util.ExchangeHelper; /** @@ -30,7 +31,7 @@ import org.apache.camel.util.ExchangeHelper; * * @version */ -public class StringDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat { +public class StringDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat, DataFormatName { private String charset; @@ -41,6 +42,11 @@ public class StringDataFormat extends org.apache.camel.support.ServiceSupport im this.charset = charset; } + @Override + public String getDataFormatName() { + return "string"; + } + public String getCharset() { return charset; } http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/impl/ZipDataFormat.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/ZipDataFormat.java b/camel-core/src/main/java/org/apache/camel/impl/ZipDataFormat.java index daa9062..c107f3f 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/ZipDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/impl/ZipDataFormat.java @@ -25,13 +25,14 @@ import java.util.zip.InflaterInputStream; import org.apache.camel.Exchange; import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatName; import org.apache.camel.util.IOHelper; /** * "Deflate" compression data format. * See {@link org.apache.camel.model.dataformat.ZipFileDataFormat} for Zip file compression. */ -public class ZipDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat { +public class ZipDataFormat extends org.apache.camel.support.ServiceSupport implements DataFormat, DataFormatName { private int compressionLevel; @@ -43,6 +44,11 @@ public class ZipDataFormat extends org.apache.camel.support.ServiceSupport imple this.compressionLevel = compressionLevel; } + @Override + public String getDataFormatName() { + return "zip"; + } + public int getCompressionLevel() { return compressionLevel; } @@ -66,7 +72,7 @@ public class ZipDataFormat extends org.apache.camel.support.ServiceSupport imple public Object unmarshal(Exchange exchange, InputStream stream) throws Exception { InputStream is = exchange.getIn().getMandatoryBody(InputStream.class); InflaterInputStream unzipInput = new InflaterInputStream(is); - + // Create an expandable byte array to hold the inflated data ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedDataFormat.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedDataFormat.java b/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedDataFormat.java index dcb5e09..dd4f7b9 100644 --- a/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedDataFormat.java @@ -16,16 +16,26 @@ */ package org.apache.camel.management.mbean; +import java.util.List; +import java.util.Map; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; import org.apache.camel.CamelContext; import org.apache.camel.ServiceStatus; import org.apache.camel.StatefulService; import org.apache.camel.api.management.ManagedInstance; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.api.management.mbean.CamelOpenMBeanTypes; import org.apache.camel.api.management.mbean.ManagedDataFormatMBean; import org.apache.camel.spi.DataFormat; +import org.apache.camel.spi.DataFormatName; import org.apache.camel.spi.ManagementStrategy; +import org.apache.camel.util.JsonSchemaHelper; +import org.apache.camel.util.ObjectHelper; @ManagedResource(description = "Managed DataFormat") public class ManagedDataFormat implements ManagedInstance, ManagedDataFormatMBean { @@ -50,6 +60,14 @@ public class ManagedDataFormat implements ManagedInstance, ManagedDataFormatMBea } @Override + public String getName() { + if (dataFormat instanceof DataFormatName) { + return ((DataFormatName) dataFormat).getDataFormatName(); + } + return null; + } + + @Override public String getCamelId() { return camelContext.getName(); } @@ -73,14 +91,49 @@ public class ManagedDataFormat implements ManagedInstance, ManagedDataFormatMBea @Override public String informationJson() { - // TODO: not yet implemented - return null; + String dataFormatName = getName(); + if (dataFormatName != null) { + return camelContext.explainDataFormatJson(dataFormatName, dataFormat, true); + } else { + return null; + } } @Override public TabularData explain(boolean allOptions) { - // TODO: not yet implemented - return null; + String dataFormatName = getName(); + if (dataFormatName != null) { + try { + TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.explainDataFormatTabularType()); + + String json = camelContext.explainDataFormatJson(dataFormatName, dataFormat, allOptions); + List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true); + + for (Map<String, String> row : rows) { + String name = row.get("name"); + String kind = row.get("kind"); + String label = row.get("label") != null ? row.get("label") : ""; + String type = row.get("type"); + String javaType = row.get("javaType"); + String deprecated = row.get("deprecated") != null ? row.get("deprecated") : ""; + String value = row.get("value") != null ? row.get("value") : ""; + String defaultValue = row.get("defaultValue") != null ? row.get("defaultValue") : ""; + String description = row.get("description") != null ? row.get("description") : ""; + + CompositeType ct = CamelOpenMBeanTypes.explainDataFormatsCompositeType(); + CompositeData data = new CompositeDataSupport(ct, + new String[]{"option", "kind", "label", "type", "java type", "deprecated", "value", "default value", "description"}, + new Object[]{name, kind, label, type, javaType, deprecated, value, defaultValue, description}); + answer.put(data); + } + + return answer; + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } else { + return null; + } } @Override http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/main/java/org/apache/camel/spi/DataFormatName.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/spi/DataFormatName.java b/camel-core/src/main/java/org/apache/camel/spi/DataFormatName.java new file mode 100644 index 0000000..1e23d3b --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/spi/DataFormatName.java @@ -0,0 +1,28 @@ +/** + * 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.camel.spi; + +/** + * The name of the {@link org.apache.camel.spi.DataFormat} which allows to know which kind of data format the implementation is. + */ +public interface DataFormatName { + + /** + * The name of the data format + */ + String getDataFormatName(); +} http://git-wip-us.apache.org/repos/asf/camel/blob/f6525494/camel-core/src/test/java/org/apache/camel/management/ManagedDataFormatTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/management/ManagedDataFormatTest.java b/camel-core/src/test/java/org/apache/camel/management/ManagedDataFormatTest.java index fe5a58e..0429269 100644 --- a/camel-core/src/test/java/org/apache/camel/management/ManagedDataFormatTest.java +++ b/camel-core/src/test/java/org/apache/camel/management/ManagedDataFormatTest.java @@ -19,6 +19,7 @@ package org.apache.camel.management; import java.util.Set; import javax.management.MBeanServer; import javax.management.ObjectName; +import javax.management.openmbean.TabularData; import org.apache.camel.builder.RouteBuilder; @@ -38,6 +39,24 @@ public class ManagedDataFormatTest extends ManagementTestSupport { // there should be 1 data format Set<ObjectName> set = mbeanServer.queryNames(new ObjectName("*:type=dataformats,*"), null); assertEquals(1, set.size()); + + ObjectName on = set.iterator().next(); + + String json = (String) mbeanServer.invoke(on, "informationJson", null, null); + assertNotNull(json); + + assertTrue(json.contains("\"title\": \"String Encoding\"")); + assertTrue(json.contains("\"modelJavaType\": \"org.apache.camel.model.dataformat.StringDataFormat\"")); + assertTrue(json.contains("\"charset\": { \"kind\": \"attribute\"")); + assertTrue(json.contains("\"value\": \"iso-8859-1\"")); + + TabularData data = (TabularData) mbeanServer.invoke(on, "explain", new Object[]{true}, new String[]{"boolean"}); + assertNotNull(data); + assertEquals(2, data.size()); + + data = (TabularData) mbeanServer.invoke(on, "explain", new Object[]{false}, new String[]{"boolean"}); + assertNotNull(data); + assertEquals(1, data.size()); } @Override