You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2020/03/30 07:00:46 UTC
[camel-karaf] 01/03: Moved platform commands to camel-karaf
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-karaf.git
commit 96546def5a3c631ab8e5167fe47525611f97c69d
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Mon Mar 30 08:39:07 2020 +0200
Moved platform commands to camel-karaf
---
platforms/commands/commands-core/pom.xml | 109 ++++
.../camel/commands/AbstractCamelCommand.java | 42 ++
.../camel/commands/AbstractCamelController.java | 51 ++
.../camel/commands/AbstractContextCommand.java | 92 +++
.../commands/AbstractLocalCamelController.java | 657 +++++++++++++++++++++
.../camel/commands/AbstractRouteCommand.java | 109 ++++
.../org/apache/camel/commands/CamelCommand.java | 37 ++
.../org/apache/camel/commands/CamelController.java | 266 +++++++++
.../camel/commands/ContextInflightCommand.java | 162 +++++
.../apache/camel/commands/ContextInfoCommand.java | 245 ++++++++
.../apache/camel/commands/ContextListCommand.java | 145 +++++
.../camel/commands/ContextResumeCommand.java | 35 ++
.../apache/camel/commands/ContextStartCommand.java | 35 ++
.../apache/camel/commands/ContextStopCommand.java | 35 ++
.../camel/commands/ContextSuspendCommand.java | 35 ++
.../apache/camel/commands/EndpointListCommand.java | 169 ++++++
.../camel/commands/EndpointStatisticCommand.java | 249 ++++++++
.../camel/commands/LocalCamelController.java | 47 ++
.../apache/camel/commands/RestApiDocCommand.java | 42 ++
.../camel/commands/RestRegistryListCommand.java | 198 +++++++
.../org/apache/camel/commands/RestShowCommand.java | 40 ++
.../apache/camel/commands/RouteInfoCommand.java | 120 ++++
.../apache/camel/commands/RouteListCommand.java | 167 ++++++
.../apache/camel/commands/RouteProfileCommand.java | 85 +++
.../camel/commands/RouteResetStatsCommand.java | 35 ++
.../apache/camel/commands/RouteResumeCommand.java | 34 ++
.../apache/camel/commands/RouteShowCommand.java | 39 ++
.../apache/camel/commands/RouteStartCommand.java | 34 ++
.../apache/camel/commands/RouteStepCommand.java | 83 +++
.../apache/camel/commands/RouteStopCommand.java | 34 ++
.../apache/camel/commands/RouteSuspendCommand.java | 34 ++
.../org/apache/camel/commands/StringEscape.java | 29 +
.../camel/commands/TransformerListCommand.java | 198 +++++++
.../camel/commands/ValidatorListCommand.java | 180 ++++++
.../apache/camel/commands/internal/MatchUtil.java | 31 +
.../apache/camel/commands/internal/RegexUtil.java | 69 +++
.../src/main/resources/META-INF/LICENSE.txt | 203 +++++++
.../src/main/resources/META-INF/NOTICE.txt | 11 +
.../commands/AbstractLocalCamelControllerTest.java | 159 +++++
.../camel/commands/ContextListCommandTest.java | 97 +++
.../camel/commands/DummyCamelController.java | 51 ++
.../camel/commands/ValidatorListCommandTest.java | 123 ++++
.../camel/commands/internal/RegexUtilTest.java | 52 ++
.../src/test/resources/log4j2.properties | 30 +
platforms/commands/pom.xml | 37 ++
platforms/pom.xml | 1 +
46 files changed, 4736 insertions(+)
diff --git a/platforms/commands/commands-core/pom.xml b/platforms/commands/commands-core/pom.xml
new file mode 100644
index 0000000..de13c29
--- /dev/null
+++ b/platforms/commands/commands-core/pom.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>commands</artifactId>
+ <version>3.2.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-commands-core</artifactId>
+ <packaging>jar</packaging>
+ <name>Camel :: Platforms :: Commands :: Core</name>
+ <description>Core Camel Commands</description>
+
+ <properties>
+ <!-- use by camel-catalog -->
+ <firstVersion>2.15.0</firstVersion>
+ <label>tooling</label>
+
+ <camel.osgi.export.pkg>org.apache.camel.commands.*</camel.osgi.export.pkg>
+ </properties>
+
+ <dependencies>
+
+ <!-- camel -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-management</artifactId>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- logging -->
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-bundle-plugin</artifactId>
+ <version>${project.version}</version>
+ <configuration>
+ <instructions>
+ <Private-Package>
+ org.apache.camel.commands.internal
+ </Private-Package>
+ </instructions>
+ </configuration>
+ <executions>
+ <execution>
+ <id>bundle-manifest</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractCamelCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractCamelCommand.java
new file mode 100644
index 0000000..fe58e28
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractCamelCommand.java
@@ -0,0 +1,42 @@
+/*
+ * 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.commands;
+
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Abstract base command for {@link org.apache.camel.commands.CamelCommand}
+ */
+public abstract class AbstractCamelCommand implements CamelCommand {
+
+ public String safeNull(String s) {
+ if (ObjectHelper.isEmpty(s)) {
+ return "";
+ } else {
+ return s;
+ }
+ }
+
+ public String safeNull(Object s) {
+ if (ObjectHelper.isEmpty(s)) {
+ return "";
+ } else {
+ return s.toString();
+ }
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractCamelController.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractCamelController.java
new file mode 100644
index 0000000..0e79098
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractCamelController.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.camel.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.commands.internal.MatchUtil;
+import org.apache.camel.commands.internal.RegexUtil;
+
+/**
+ * Abstract {@link org.apache.camel.commands.CamelController} that implementators should extend.
+ */
+public abstract class AbstractCamelController implements CamelController {
+
+ @Override
+ public List<Map<String, String>> getCamelContexts(String filter) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ List<Map<String, String>> context = getCamelContexts();
+ if (filter != null) {
+ filter = RegexUtil.wildcardAsRegex(filter);
+ } else {
+ filter = "*";
+ }
+ for (Map<String, String> entry : context) {
+ String name = entry.get("name");
+ if (name.equalsIgnoreCase(filter) || MatchUtil.matchWildcard(name, filter) || name.matches(filter)) {
+ answer.add(entry);
+ }
+ }
+
+ return answer;
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractContextCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractContextCommand.java
new file mode 100644
index 0000000..1ddd134
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractContextCommand.java
@@ -0,0 +1,92 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+
+/**
+ * Abstract command for working with a single {@link org.apache.camel.CamelContext}
+ */
+public abstract class AbstractContextCommand extends AbstractCamelCommand {
+
+ String context;
+
+ /**
+ * @param context The name of the Camel context.
+ */
+ protected AbstractContextCommand(String context) {
+ this.context = context;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ if (camelController instanceof LocalCamelController) {
+ return executeLocal((LocalCamelController) camelController, out, err);
+ } else {
+ boolean found = false;
+ List<Map<String, String>> contexts = camelController.getCamelContexts();
+ for (Map<String, String> entry : contexts) {
+ String name = entry.get("name");
+ if (context.equals(name)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ err.println("Camel context " + context + " not found.");
+ return null;
+ } else {
+ return performContextCommand(camelController, context, out, err);
+ }
+ }
+ }
+
+ protected Object executeLocal(LocalCamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ CamelContext camelContext = camelController.getLocalCamelContext(context);
+ if (camelContext == null) {
+ err.println("Camel context " + context + " not found.");
+ return null;
+ }
+
+ // Setting thread context classloader to the bundle classloader to enable legacy code that relies on it
+ ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(camelContext.getApplicationContextClassLoader());
+ try {
+ return performContextCommand(camelController, camelContext.getName(), out, err);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassloader);
+ }
+ }
+
+ /**
+ * Perform Context-specific command
+ *
+ * @param camelController the Camel controller
+ * @param contextName the Camel context name
+ * @param out the output printer stream
+ * @param err the error print stream
+ * @return response from command, or <tt>null</tt> if nothing to return
+ * @throws Exception is thrown if error executing command
+ */
+ protected abstract Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception;
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractLocalCamelController.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractLocalCamelController.java
new file mode 100644
index 0000000..a65b02b
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractLocalCamelController.java
@@ -0,0 +1,657 @@
+/*
+ * 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.commands;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.Route;
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.StatefulService;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedRouteMBean;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.spi.EndpointRegistry;
+import org.apache.camel.spi.ManagementAgent;
+import org.apache.camel.spi.RestRegistry;
+import org.apache.camel.spi.RuntimeEndpointRegistry;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.spi.Validator;
+
+/**
+ * Abstract {@link org.apache.camel.commands.LocalCamelController} that implementators should extend when implementing
+ * a controller that runs locally in the same JVM as Camel.
+ */
+public abstract class AbstractLocalCamelController extends AbstractCamelController implements LocalCamelController {
+
+ @Override
+ public CamelContext getLocalCamelContext(String name) throws Exception {
+ for (CamelContext camelContext : this.getLocalCamelContexts()) {
+ if (camelContext.getName().equals(name)) {
+ return camelContext;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Map<String, Object> getCamelContextInformation(String name) throws Exception {
+ Map<String, Object> answer = new LinkedHashMap<>();
+ CamelContext context = getLocalCamelContext(name);
+ if (context != null) {
+ answer.put("name", context.getName());
+ answer.put("managementName", context.getManagementName());
+ answer.put("version", context.getVersion());
+ answer.put("status", context.getStatus().name());
+ answer.put("uptime", context.getUptime());
+ answer.put("suspended", context.getStatus().isSuspended());
+ if (context.getManagementStrategy().getManagementAgent() != null) {
+ String level = context.getManagementStrategy().getManagementAgent().getStatisticsLevel().name();
+ answer.put("managementStatisticsLevel", level);
+ }
+ answer.put("allowUseOriginalMessage", context.isAllowUseOriginalMessage());
+ answer.put("messageHistory", context.isMessageHistory());
+ answer.put("tracing", context.isTracing());
+ answer.put("logMask", context.isLogMask());
+ answer.put("shutdownTimeout", context.getShutdownStrategy().getTimeUnit().toSeconds(context.getShutdownStrategy().getTimeout()));
+ answer.put("classResolver", context.getClassResolver().toString());
+ answer.put("packageScanClassResolver", context.adapt(ExtendedCamelContext.class).getPackageScanClassResolver().toString());
+ answer.put("applicationContextClassLoader", context.getApplicationContextClassLoader().toString());
+ answer.put("headersMapFactory", context.adapt(ExtendedCamelContext.class).getHeadersMapFactory().toString());
+
+ for (Map.Entry<String, String> entry : context.getGlobalOptions().entrySet()) {
+ answer.put("property." + entry.getKey(), entry.getValue());
+ }
+
+ long activeRoutes = 0;
+ long inactiveRoutes = 0;
+ List<Route> routeList = context.getRoutes();
+ for (Route route : routeList) {
+ if (context.getRouteController().getRouteStatus(route.getId()).isStarted()) {
+ activeRoutes++;
+ } else {
+ inactiveRoutes++;
+ }
+ }
+ answer.put("startedRoutes", activeRoutes);
+ answer.put("totalRoutes", activeRoutes + inactiveRoutes);
+
+ // add type converter details
+ answer.put("typeConverter.numberOfTypeConverters", context.getTypeConverterRegistry().size());
+ answer.put("typeConverter.statisticsEnabled", context.getTypeConverterRegistry().getStatistics().isStatisticsEnabled());
+ answer.put("typeConverter.noopCounter", context.getTypeConverterRegistry().getStatistics().getNoopCounter());
+ answer.put("typeConverter.attemptCounter", context.getTypeConverterRegistry().getStatistics().getAttemptCounter());
+ answer.put("typeConverter.hitCounter", context.getTypeConverterRegistry().getStatistics().getHitCounter());
+ answer.put("typeConverter.missCounter", context.getTypeConverterRegistry().getStatistics().getMissCounter());
+ answer.put("typeConverter.failedCounter", context.getTypeConverterRegistry().getStatistics().getFailedCounter());
+
+ // add async processor await manager details
+ ExtendedCamelContext ecc = context.adapt(ExtendedCamelContext.class);
+ answer.put("asyncProcessorAwaitManager.size", ecc.getAsyncProcessorAwaitManager().size());
+ answer.put("asyncProcessorAwaitManager.statisticsEnabled", ecc.getAsyncProcessorAwaitManager().getStatistics().isStatisticsEnabled());
+ answer.put("asyncProcessorAwaitManager.threadsBlocked", ecc.getAsyncProcessorAwaitManager().getStatistics().getThreadsBlocked());
+ answer.put("asyncProcessorAwaitManager.threadsInterrupted", ecc.getAsyncProcessorAwaitManager().getStatistics().getThreadsInterrupted());
+ answer.put("asyncProcessorAwaitManager.totalDuration", ecc.getAsyncProcessorAwaitManager().getStatistics().getTotalDuration());
+ answer.put("asyncProcessorAwaitManager.minDuration", ecc.getAsyncProcessorAwaitManager().getStatistics().getMinDuration());
+ answer.put("asyncProcessorAwaitManager.maxDuration", ecc.getAsyncProcessorAwaitManager().getStatistics().getMaxDuration());
+ answer.put("asyncProcessorAwaitManager.meanDuration", ecc.getAsyncProcessorAwaitManager().getStatistics().getMeanDuration());
+
+ // add stream caching details if enabled
+ if (context.getStreamCachingStrategy().isEnabled()) {
+ answer.put("streamCachingEnabled", true);
+ answer.put("streamCaching.spoolDirectory", context.getStreamCachingStrategy().getSpoolDirectory());
+ answer.put("streamCaching.spoolCipher", context.getStreamCachingStrategy().getSpoolCipher());
+ answer.put("streamCaching.spoolThreshold", context.getStreamCachingStrategy().getSpoolThreshold());
+ answer.put("streamCaching.spoolUsedHeapMemoryThreshold", context.getStreamCachingStrategy().getSpoolUsedHeapMemoryThreshold());
+ answer.put("streamCaching.spoolUsedHeapMemoryLimit", context.getStreamCachingStrategy().getSpoolUsedHeapMemoryLimit());
+ answer.put("streamCaching.anySpoolRules", context.getStreamCachingStrategy().isAnySpoolRules());
+ answer.put("streamCaching.bufferSize", context.getStreamCachingStrategy().getBufferSize());
+ answer.put("streamCaching.removeSpoolDirectoryWhenStopping", context.getStreamCachingStrategy().isRemoveSpoolDirectoryWhenStopping());
+ answer.put("streamCaching.statisticsEnabled", context.getStreamCachingStrategy().getStatistics().isStatisticsEnabled());
+
+ if (context.getStreamCachingStrategy().getStatistics().isStatisticsEnabled()) {
+ answer.put("streamCaching.cacheMemoryCounter", context.getStreamCachingStrategy().getStatistics().getCacheMemoryCounter());
+ answer.put("streamCaching.cacheMemorySize", context.getStreamCachingStrategy().getStatistics().getCacheMemorySize());
+ answer.put("streamCaching.cacheMemoryAverageSize", context.getStreamCachingStrategy().getStatistics().getCacheMemoryAverageSize());
+ answer.put("streamCaching.cacheSpoolCounter", context.getStreamCachingStrategy().getStatistics().getCacheSpoolCounter());
+ answer.put("streamCaching.cacheSpoolSize", context.getStreamCachingStrategy().getStatistics().getCacheSpoolSize());
+ answer.put("streamCaching.cacheSpoolAverageSize", context.getStreamCachingStrategy().getStatistics().getCacheSpoolAverageSize());
+ }
+ } else {
+ answer.put("streamCachingEnabled", false);
+ }
+ }
+
+ return answer;
+ }
+
+ @Override
+ public String getCamelContextStatsAsXml(String camelContextName, boolean fullStats, boolean includeProcessors) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+
+ ManagementAgent agent = context.getManagementStrategy().getManagementAgent();
+ if (agent != null) {
+ MBeanServer mBeanServer = agent.getMBeanServer();
+ ObjectName query = ObjectName.getInstance(agent.getMBeanObjectDomainName() + ":type=context,*");
+ Set<ObjectName> set = mBeanServer.queryNames(query, null);
+ for (ObjectName contextMBean : set) {
+ String camelId = (String) mBeanServer.getAttribute(contextMBean, "CamelId");
+ if (camelId != null && camelId.equals(context.getName())) {
+ String xml = (String) mBeanServer.invoke(contextMBean, "dumpRoutesStatsAsXml", new Object[]{fullStats, includeProcessors}, new String[]{"boolean", "boolean"});
+ return xml;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<Map<String, Object>> browseInflightExchanges(String camelContextName, String route, int limit, boolean sortByLongestDuration) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+
+ List<Map<String, Object>> answer = new ArrayList<>();
+
+ ManagementAgent agent = context.getManagementStrategy().getManagementAgent();
+ if (agent != null) {
+ MBeanServer mBeanServer = agent.getMBeanServer();
+ ObjectName on = new ObjectName(agent.getMBeanObjectDomainName() + ":type=services,name=DefaultInflightRepository,context=" + context.getManagementName());
+ if (mBeanServer.isRegistered(on)) {
+ TabularData list = (TabularData) mBeanServer.invoke(on, "browse", new Object[]{route, limit, sortByLongestDuration}, new String[]{"java.lang.String", "int", "boolean"});
+ Collection<CompositeData> values = (Collection<CompositeData>) list.values();
+ for (CompositeData data : values) {
+ Map<String, Object> row = new LinkedHashMap<>();
+ Object exchangeId = data.get("exchangeId");
+ if (exchangeId != null) {
+ row.put("exchangeId", exchangeId);
+ }
+ Object fromRouteId = data.get("fromRouteId");
+ if (fromRouteId != null) {
+ row.put("fromRouteId", fromRouteId);
+ }
+ Object routeId = data.get("routeId");
+ if (routeId != null) {
+ row.put("routeId", routeId);
+ }
+ Object nodeId = data.get("nodeId");
+ if (nodeId != null) {
+ row.put("nodeId", nodeId);
+ }
+ Object elapsed = data.get("elapsed");
+ if (elapsed != null) {
+ row.put("elapsed", elapsed);
+ }
+ Object duration = data.get("duration");
+ if (duration != null) {
+ row.put("duration", duration);
+ }
+ answer.add(row);
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ @Override
+ public void startContext(String camelContextName) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ if (context.getStatus().equals(ServiceStatus.Suspended)) {
+ context.resume();
+ } else {
+ context.start();
+ }
+ }
+ }
+
+ @Override
+ public void stopContext(String camelContextName) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.stop();
+ }
+ }
+
+ @Override
+ public void suspendContext(String camelContextName) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.suspend();
+ }
+ }
+
+ @Override
+ public void resumeContext(String camelContextName) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.resume();
+ }
+ }
+
+ @Override
+ public List<Map<String, String>> getRoutes(String camelContextName) throws Exception {
+ return getRoutes(camelContextName, null);
+ }
+
+ @Override
+ public List<Map<String, String>> getRoutes(String camelContextName, String filter) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ if (camelContextName != null) {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context != null) {
+ for (Route route : context.getRoutes()) {
+ if (filter == null || route.getId().matches(filter)) {
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("camelContextName", context.getName());
+ row.put("routeId", route.getId());
+ row.put("state", getRouteState(route));
+ row.put("uptime", route.getUptime());
+ ManagedCamelContext mcc = context.getExtension(ManagedCamelContext.class);
+ if (mcc != null && mcc.getManagedCamelContext() != null) {
+ ManagedRouteMBean mr = mcc.getManagedRoute(route.getId());
+ row.put("exchangesTotal", "" + mr.getExchangesTotal());
+ row.put("exchangesInflight", "" + mr.getExchangesInflight());
+ row.put("exchangesFailed", "" + mr.getExchangesFailed());
+ } else {
+ row.put("exchangesTotal", "0");
+ row.put("exchangesInflight", "0");
+ row.put("exchangesFailed", "0");
+ }
+ answer.add(row);
+ }
+ }
+ }
+ } else {
+ List<Map<String, String>> camelContexts = this.getCamelContexts();
+ for (Map<String, String> row : camelContexts) {
+ List<Map<String, String>> routes = getRoutes(row.get("name"), filter);
+ answer.addAll(routes);
+ }
+ }
+
+ // sort the list
+ Collections.sort(answer, new Comparator<Map<String, String>>() {
+ @Override
+ public int compare(Map<String, String> o1, Map<String, String> o2) {
+ // group by camel context first, then by route name
+ String c1 = o1.get("camelContextName");
+ String c2 = o2.get("camelContextName");
+
+ int answer = c1.compareTo(c2);
+ if (answer == 0) {
+ // okay from same camel context, then sort by route id
+ answer = o1.get("routeId").compareTo(o2.get("routeId"));
+ }
+ return answer;
+ }
+ });
+ return answer;
+ }
+
+ @Override
+ public void resetRouteStats(String camelContextName) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return;
+ }
+
+ ManagementAgent agent = context.getManagementStrategy().getManagementAgent();
+ if (agent != null) {
+ MBeanServer mBeanServer = agent.getMBeanServer();
+
+ // reset route mbeans
+ ObjectName query = ObjectName.getInstance(agent.getMBeanObjectDomainName() + ":type=routes,*");
+ Set<ObjectName> set = mBeanServer.queryNames(query, null);
+ for (ObjectName routeMBean : set) {
+ String camelId = (String) mBeanServer.getAttribute(routeMBean, "CamelId");
+ if (camelId != null && camelId.equals(context.getName())) {
+ mBeanServer.invoke(routeMBean, "reset", new Object[]{true}, new String[]{"boolean"});
+ }
+ }
+ }
+ }
+
+ @Override
+ public void startRoute(String camelContextName, String routeId) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.getRouteController().startRoute(routeId);
+ }
+ }
+
+ @Override
+ public void stopRoute(String camelContextName, String routeId) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.getRouteController().stopRoute(routeId);
+ }
+ }
+
+ @Override
+ public void suspendRoute(String camelContextName, String routeId) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.getRouteController().suspendRoute(routeId);
+ }
+ }
+
+ @Override
+ public void resumeRoute(String camelContextName, String routeId) throws Exception {
+ CamelContext context = getLocalCamelContext(camelContextName);
+ if (context != null) {
+ context.getRouteController().resumeRoute(routeId);
+ }
+ }
+
+ @Override
+ public String getRouteModelAsXml(String routeId, String camelContextName) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+ RouteDefinition route = context.getExtension(Model.class).getRouteDefinition(routeId);
+ if (route == null) {
+ return null;
+ }
+
+ ExtendedCamelContext ecc = context.adapt(ExtendedCamelContext.class);
+ return ecc.getModelToXMLDumper().dumpModelAsXml(context, route);
+ }
+
+ @Override
+ public String getRouteStatsAsXml(String routeId, String camelContextName, boolean fullStats, boolean includeProcessors) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+
+ ManagementAgent agent = context.getManagementStrategy().getManagementAgent();
+ if (agent != null) {
+ MBeanServer mBeanServer = agent.getMBeanServer();
+ Set<ObjectName> set = mBeanServer.queryNames(new ObjectName(agent.getMBeanObjectDomainName() + ":type=routes,name=\"" + routeId + "\",*"), null);
+ for (ObjectName routeMBean : set) {
+
+ // the route must be part of the camel context
+ String camelId = (String) mBeanServer.getAttribute(routeMBean, "CamelId");
+ if (camelId != null && camelId.equals(camelContextName)) {
+ String xml = (String) mBeanServer.invoke(routeMBean, "dumpRouteStatsAsXml", new Object[]{fullStats, includeProcessors}, new String[]{"boolean", "boolean"});
+ return xml;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getStepStatsAsXml(String routeId, String camelContextName, boolean fullStats) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+
+ ManagementAgent agent = context.getManagementStrategy().getManagementAgent();
+ if (agent != null) {
+ MBeanServer mBeanServer = agent.getMBeanServer();
+ Set<ObjectName> set = mBeanServer.queryNames(new ObjectName(agent.getMBeanObjectDomainName() + ":type=routes,name=\"" + routeId + "\",*"), null);
+ for (ObjectName routeMBean : set) {
+
+ // the route must be part of the camel context
+ String camelId = (String) mBeanServer.getAttribute(routeMBean, "CamelId");
+ if (camelId != null && camelId.equals(camelContextName)) {
+ String xml = (String) mBeanServer.invoke(routeMBean, "dumpStepStatsAsXml", new Object[]{fullStats}, new String[]{"boolean"});
+ return xml;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getRestModelAsXml(String camelContextName) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+
+ List<RestDefinition> rests = context.getExtension(Model.class).getRestDefinitions();
+ if (rests == null || rests.isEmpty()) {
+ return null;
+ }
+ // use a rests definition to dump the rests
+ RestsDefinition def = new RestsDefinition();
+ def.setRests(rests);
+
+ ExtendedCamelContext ecc = context.adapt(ExtendedCamelContext.class);
+ return ecc.getModelToXMLDumper().dumpModelAsXml(context, def);
+ }
+
+ @Override
+ public String getRestApiDocAsJson(String camelContextName) throws Exception {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context == null) {
+ return null;
+ }
+
+ return context.getRestRegistry().apiDocAsJson();
+ }
+
+ @Override
+ public List<Map<String, String>> getEndpoints(String camelContextName) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ if (camelContextName != null) {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context != null) {
+ List<Endpoint> endpoints = new ArrayList<>(context.getEndpoints());
+ // sort routes
+ Collections.sort(endpoints, new Comparator<Endpoint>() {
+ @Override
+ public int compare(Endpoint o1, Endpoint o2) {
+ return o1.getEndpointKey().compareTo(o2.getEndpointKey());
+ }
+ });
+ for (Endpoint endpoint : endpoints) {
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("camelContextName", context.getName());
+ row.put("uri", endpoint.getEndpointUri());
+ row.put("state", getEndpointState(endpoint));
+ answer.add(row);
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public List<Map<String, String>> getEndpointRuntimeStatistics(String camelContextName) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ if (camelContextName != null) {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context != null && context.getRuntimeEndpointRegistry() != null) {
+ EndpointRegistry staticRegistry = context.getEndpointRegistry();
+ for (RuntimeEndpointRegistry.Statistic stat : context.getRuntimeEndpointRegistry().getEndpointStatistics()) {
+
+ String url = stat.getUri();
+ String routeId = stat.getRouteId();
+ String direction = stat.getDirection();
+ boolean isStatic = staticRegistry.isStatic(url);
+ boolean isDynamic = staticRegistry.isDynamic(url);
+ long hits = stat.getHits();
+
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("camelContextName", context.getName());
+ row.put("uri", url);
+ row.put("routeId", routeId);
+ row.put("direction", direction);
+ row.put("static", Boolean.toString(isStatic));
+ row.put("dynamic", Boolean.toString(isDynamic));
+ row.put("hits", "" + hits);
+ answer.add(row);
+ }
+ }
+
+ // sort the list
+ Collections.sort(answer, new Comparator<Map<String, String>>() {
+ @Override
+ public int compare(Map<String, String> endpoint1, Map<String, String> endpoint2) {
+ // sort by route id
+ String route1 = endpoint1.get("routeId");
+ String route2 = endpoint2.get("routeId");
+ int num = route1.compareTo(route2);
+ if (num == 0) {
+ // we want in before out
+ String dir1 = endpoint1.get("direction");
+ String dir2 = endpoint2.get("direction");
+ num = dir1.compareTo(dir2);
+ }
+ return num;
+ }
+
+ });
+ }
+ return answer;
+ }
+
+ @Override
+ public List<Map<String, String>> getRestServices(String camelContextName) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ if (camelContextName != null) {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context != null) {
+ List<RestRegistry.RestService> services = new ArrayList<>(context.getRestRegistry().listAllRestServices());
+ Collections.sort(services, new Comparator<RestRegistry.RestService>() {
+ @Override
+ public int compare(RestRegistry.RestService o1, RestRegistry.RestService o2) {
+ return o1.getUrl().compareTo(o2.getUrl());
+ }
+ });
+ for (RestRegistry.RestService service : services) {
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("basePath", service.getBasePath());
+ row.put("baseUrl", service.getBaseUrl());
+ row.put("consumes", service.getConsumes());
+ row.put("description", service.getDescription());
+ row.put("inType", service.getInType());
+ row.put("method", service.getMethod());
+ row.put("outType", service.getOutType());
+ row.put("produces", service.getProduces());
+ row.put("routeId", service.getRouteId());
+ row.put("state", service.getState());
+ row.put("uriTemplate", service.getUriTemplate());
+ row.put("url", service.getUrl());
+ answer.add(row);
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public List<Map<String, String>> getTransformers(String camelContextName) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ if (camelContextName != null) {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context != null) {
+ List<Transformer> transformers = new ArrayList<>(context.getTransformerRegistry().values());
+ for (Transformer transformer : transformers) {
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("camelContextName", context.getName());
+ row.put("scheme", transformer.getModel());
+ row.put("from", transformer.getFrom().toString());
+ row.put("to", transformer.getTo().toString());
+ row.put("state", transformer.getStatus().toString());
+ row.put("description", transformer.toString());
+ answer.add(row);
+ }
+ }
+ }
+ return answer;
+ }
+
+ @Override
+ public List<Map<String, String>> getValidators(String camelContextName) throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>();
+
+ if (camelContextName != null) {
+ CamelContext context = this.getLocalCamelContext(camelContextName);
+ if (context != null) {
+ List<Validator> validators = new ArrayList<>(context.getValidatorRegistry().values());
+ for (Validator validator : validators) {
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("camelContextName", context.getName());
+ row.put("type", validator.getType().toString());
+ row.put("state", validator.getStatus().toString());
+ row.put("description", validator.toString());
+ answer.add(row);
+ }
+ }
+ }
+ return answer;
+ }
+
+ private static String getEndpointState(Endpoint endpoint) {
+ // must use String type to be sure remote JMX can read the attribute without requiring Camel classes.
+ if (endpoint instanceof StatefulService) {
+ ServiceStatus status = ((StatefulService) endpoint).getStatus();
+ return status.name();
+ }
+
+ // assume started if not a ServiceSupport instance
+ return ServiceStatus.Started.name();
+ }
+
+ private static String getRouteState(Route route) {
+ // must use String type to be sure remote JMX can read the attribute without requiring Camel classes.
+
+ ServiceStatus status = route.getCamelContext().getRouteController().getRouteStatus(route.getId());
+ if (status != null) {
+ return status.name();
+ }
+
+ // assume started if not a ServiceSupport instance
+ return ServiceStatus.Started.name();
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractRouteCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractRouteCommand.java
new file mode 100644
index 0000000..a3be7d7
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/AbstractRouteCommand.java
@@ -0,0 +1,109 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.commands.internal.RegexUtil;
+
+/**
+ * Abstract command for working with a one ore more routes.
+ */
+public abstract class AbstractRouteCommand extends AbstractCamelCommand {
+
+ private String route;
+ private String context;
+
+ /**
+ * @param route The Camel route ID or a wildcard expression
+ * @param context The name of the Camel context.
+ */
+ protected AbstractRouteCommand(String route, String context) {
+ this.route = route;
+ this.context = context;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ List<Map<String, String>> camelRoutes = camelController.getRoutes(context, RegexUtil.wildcardAsRegex(route));
+ if (camelRoutes == null || camelRoutes.isEmpty()) {
+ err.println("Camel routes using " + route + " not found.");
+ return null;
+ }
+ // we want the routes sorted
+ Collections.sort(camelRoutes, new RouteComparator());
+
+ for (Map<String, String> row : camelRoutes) {
+ String camelContextName = row.get("camelContextName");
+ String routeId = row.get("routeId");
+ if (camelController instanceof LocalCamelController) {
+ executeLocal((LocalCamelController) camelController, camelContextName, routeId, out, err);
+ } else {
+ executeOnRoute(camelController, camelContextName, routeId, out, err);
+ }
+ }
+
+ return null;
+ }
+
+ private void executeLocal(LocalCamelController camelController, String camelContextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ CamelContext camelContext = camelController.getLocalCamelContext(context);
+ if (camelContext == null) {
+ err.println("Camel context " + context + " not found.");
+ return;
+ }
+
+ // Setting thread context classloader to the bundle classloader to enable legacy code that relies on it
+ ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader();
+ ClassLoader applicationContextClassLoader = camelContext.getApplicationContextClassLoader();
+ if (applicationContextClassLoader != null) {
+ Thread.currentThread().setContextClassLoader(applicationContextClassLoader);
+ }
+ try {
+ executeOnRoute(camelController, camelContextName, routeId, out, err);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassloader);
+ }
+ }
+
+ public abstract void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception;
+
+ /**
+ * To sort the routes.
+ */
+ private static final class RouteComparator implements Comparator<Map<String, String>> {
+
+ @Override
+ public int compare(Map<String, String> route1, Map<String, String> route2) {
+ // sort by camel context first
+ String camel1 = route1.get("camelContextName");
+ String camel2 = route2.get("camelContextName");
+
+ if (camel1.equals(camel2)) {
+ return route1.get("routeId").compareTo(route2.get("routeId"));
+ } else {
+ return camel1.compareTo(camel2);
+ }
+ }
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/CamelCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/CamelCommand.java
new file mode 100644
index 0000000..f42b7b4
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/CamelCommand.java
@@ -0,0 +1,37 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * A Camel command.
+ */
+public interface CamelCommand {
+
+ /**
+ * Executes the given command.
+ *
+ * @param camelController the Camel controller to access Camel information
+ * @param out the output printer stream
+ * @param err the error print stream
+ * @return response from command, or <tt>null</tt> if nothing to return
+ * @throws Exception is thrown if error executing command
+ */
+ Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception;
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/CamelController.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/CamelController.java
new file mode 100644
index 0000000..c3e6482
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/CamelController.java
@@ -0,0 +1,266 @@
+/*
+ * 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.commands;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CamelController interface defines the expected behaviors to manipulate Camel resources (context, route, etc).
+ */
+public interface CamelController {
+
+ /**
+ * Gets information about a given Camel context by the given name.
+ *
+ * @param name the Camel context name.
+ * @return a list of key/value pairs with CamelContext information
+ * @throws java.lang.Exception can be thrown
+ */
+ Map<String, Object> getCamelContextInformation(String name) throws Exception;
+
+ /**
+ * Get the list of Camel context.
+ *
+ * @return a list of key/value pairs with CamelContext information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getCamelContexts() throws Exception;
+
+ /**
+ * Get the list of Camel context filter by reg ex.
+ *
+ * @param filter the filter which supports * and ? as wildcards
+ * @return a list of key/value pairs with CamelContext information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getCamelContexts(String filter) throws Exception;
+
+ /**
+ * Returns detailed CamelContext and route statistics as XML identified by a ID and a Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @param fullStats whether to include verbose stats
+ * @param includeProcessors whether to embed per processor stats from the route
+ * @return the CamelContext statistics as XML
+ * @throws java.lang.Exception can be thrown
+ */
+ String getCamelContextStatsAsXml(String camelContextName, boolean fullStats, boolean includeProcessors) throws Exception;
+
+ /**
+ * Browses the inflight exchanges
+ *
+ * @param camelContextName the Camel context.
+ * @param route the Camel route ID
+ * @param limit maximum number of exchanges to return
+ * @param sortByLongestDuration <tt>true</tt> to sort by longest duration, <tt>false</tt> to sort by exchange id
+ * @return a list of key/value pairs with inflight exchange information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, Object>> browseInflightExchanges(String camelContextName, String route, int limit, boolean sortByLongestDuration) throws Exception;
+
+ /**
+ * Starts the given Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @throws java.lang.Exception can be thrown
+ */
+ void startContext(String camelContextName) throws Exception;
+
+ /**
+ * Stops the given Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @throws java.lang.Exception can be thrown
+ */
+ void stopContext(String camelContextName) throws Exception;
+
+ /**
+ * Suspends the given Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @throws java.lang.Exception can be thrown
+ */
+ void suspendContext(String camelContextName) throws Exception;
+
+ /**
+ * Resumes the given Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @throws java.lang.Exception can be thrown
+ */
+ void resumeContext(String camelContextName) throws Exception;
+
+ /**
+ * Get all routes. If Camel context name is null, all routes from all contexts are listed.
+ *
+ * @param camelContextName the Camel context name. If null, all contexts are considered.
+ * @return a list of key/value pairs with routes information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getRoutes(String camelContextName) throws Exception;
+
+ /**
+ * Get all routes filtered by the regex.
+ *
+ * @param camelContextName the Camel context name. If null, all contexts are considered.
+ * @param filter the filter which supports * and ? as wildcards
+ * @return a list of key/value pairs with routes information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getRoutes(String camelContextName, String filter) throws Exception;
+
+ /**
+ * Reset all the route stats for the given Camel context
+ *
+ * @param camelContextName the Camel context.
+ * @throws java.lang.Exception can be thrown
+ */
+ void resetRouteStats(String camelContextName) throws Exception;
+
+ /**
+ * Starts the given route
+ *
+ * @param camelContextName the Camel context.
+ * @param routeId the route ID.
+ * @throws java.lang.Exception can be thrown
+ */
+ void startRoute(String camelContextName, String routeId) throws Exception;
+
+ /**
+ * Stops the given route
+ *
+ * @param camelContextName the Camel context.
+ * @param routeId the route ID.
+ * @throws java.lang.Exception can be thrown
+ */
+ void stopRoute(String camelContextName, String routeId) throws Exception;
+
+ /**
+ * Suspends the given route
+ *
+ * @param camelContextName the Camel context.
+ * @param routeId the route ID.
+ * @throws java.lang.Exception can be thrown
+ */
+ void suspendRoute(String camelContextName, String routeId) throws Exception;
+
+ /**
+ * Resumes the given route
+ *
+ * @param camelContextName the Camel context.
+ * @param routeId the route ID.
+ * @throws java.lang.Exception can be thrown
+ */
+ void resumeRoute(String camelContextName, String routeId) throws Exception;
+
+ /**
+ * Return the definition of a route as XML identified by a ID and a Camel context.
+ *
+ * @param routeId the route ID.
+ * @param camelContextName the Camel context.
+ * @return the route model as XML
+ * @throws java.lang.Exception can be thrown
+ */
+ String getRouteModelAsXml(String routeId, String camelContextName) throws Exception;
+
+ /**
+ * Returns detailed route statistics as XML identified by a ID and a Camel context.
+ *
+ * @param routeId the route ID.
+ * @param camelContextName the Camel context.
+ * @param fullStats whether to include verbose stats
+ * @param includeProcessors whether to embed per processor stats from the route
+ * @return the route statistics as XML
+ * @throws java.lang.Exception can be thrown
+ */
+ String getRouteStatsAsXml(String routeId, String camelContextName, boolean fullStats, boolean includeProcessors) throws Exception;
+
+ /**
+ * Returns detailed step statistics as XML identified by a ID and a Camel context.
+ *
+ * @param routeId the route ID.
+ * @param camelContextName the Camel context.
+ * @param fullStats whether to include verbose stats
+ * @return the step statistics as XML
+ * @throws java.lang.Exception can be thrown
+ */
+ String getStepStatsAsXml(String routeId, String camelContextName, boolean fullStats) throws Exception;
+
+ /**
+ * Return the endpoints
+ *
+ * @param camelContextName the Camel context.
+ * @return a list of key/value pairs with endpoint information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getEndpoints(String camelContextName) throws Exception;
+
+ /**
+ * Return endpoint runtime statistics
+ *
+ * @param camelContextName the Camel context
+ * @return a list of key/value pairs with endpoint runtime statistics
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getEndpointRuntimeStatistics(String camelContextName) throws Exception;
+
+ /**
+ * Return the definition of the REST services as XML for the given Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @return the REST model as xml
+ * @throws java.lang.Exception can be thrown
+ */
+ String getRestModelAsXml(String camelContextName) throws Exception;
+
+ /**
+ * Return the REST services API documentation as JSon (requires camel-swagger-java on classpath)
+ *
+ * @param camelContextName the Camel context.
+ * @return the REST API documentation as JSon
+ * @throws java.lang.Exception can be thrown
+ */
+ String getRestApiDocAsJson(String camelContextName) throws Exception;
+
+ /**
+ * Return the REST services for the given Camel context.
+ *
+ * @param camelContextName the Camel context.
+ * @return a list of key/value pairs with REST information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getRestServices(String camelContextName) throws Exception;
+
+ /**
+ * Return the transformers
+ *
+ * @param camelContextName the Camel context.
+ * @return a list of key/value pairs with transformer information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getTransformers(String camelContextName) throws Exception;
+
+ /**
+ * Return the validators
+ *
+ * @param camelContextName the Camel context.
+ * @return a list of key/value pairs with validator information
+ * @throws java.lang.Exception can be thrown
+ */
+ List<Map<String, String>> getValidators(String camelContextName) throws Exception;
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextInflightCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextInflightCommand.java
new file mode 100644
index 0000000..665f398
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextInflightCommand.java
@@ -0,0 +1,162 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Command to display inflight exchange information
+ */
+public class ContextInflightCommand extends AbstractContextCommand {
+
+ private static final String EXCHANGE_COLUMN_LABEL = "ExchangeId";
+ private static final String FROM_ROUTE_COLUMN_LABEL = "From Route";
+ private static final String ROUTE_COLUMN_LABEL = "Route";
+ private static final String NODE_COLUMN_LABEL = "Node";
+ private static final String ELAPSED_COLUMN_LABEL = "Elapsed (ms)";
+ private static final String DURATION_COLUMN_LABEL = "Duration (ms)";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ private static final int MAX_COLUMN_WIDTH = Integer.MAX_VALUE;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ private int limit;
+ private String route;
+ private boolean sortByLongestDuration;
+
+ public ContextInflightCommand(String context, String route, int limit, boolean sortByLongestDuration) {
+ super(context);
+ this.route = route;
+ this.limit = limit;
+ this.sortByLongestDuration = sortByLongestDuration;
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ List<Map<String, Object>> inflight = camelController.browseInflightExchanges(contextName, route, limit, sortByLongestDuration);
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(inflight);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ if (inflight.size() > 0) {
+ out.println(String.format(headerFormat, EXCHANGE_COLUMN_LABEL, FROM_ROUTE_COLUMN_LABEL, ROUTE_COLUMN_LABEL, NODE_COLUMN_LABEL, ELAPSED_COLUMN_LABEL, DURATION_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "----------", "----------", "-----", "----", "------------", "-------------"));
+ for (Map<String, Object> row : inflight) {
+ Object exchangeId = row.get("exchangeId");
+ Object fromRouteId = row.get("fromRouteId");
+ Object routeId = row.get("routeId");
+ Object nodeId = row.get("nodeId");
+ Object elapsed = row.get("elapsed");
+ Object duration = row.get("duration");
+ out.println(String.format(rowFormat, exchangeId, fromRouteId, routeId, nodeId, safeNull(elapsed), safeNull(duration)));
+ }
+ }
+
+ return null;
+ }
+
+ private Map<String, Integer> computeColumnWidths(final Iterable<Map<String, Object>> inflight) throws Exception {
+ if (inflight == null) {
+ throw new IllegalArgumentException("Unable to determine column widths from null Iterable<Inflight>");
+ } else {
+ int maxExchangeLen = 0;
+ int maxFromRouteLen = 0;
+ int maxRouteLen = 0;
+ int maxNodeLen = 0;
+ int maxElapsedLen = 0;
+ int maxDurationLen = 0;
+
+ for (Map<String, Object> row : inflight) {
+ final String exchangeId = safeNull(row.get("exchangeId"));
+ maxExchangeLen = java.lang.Math.max(maxExchangeLen, exchangeId == null ? 0 : exchangeId.length());
+
+ final String fromRouteId = safeNull(row.get("fromRouteId"));
+ maxFromRouteLen = java.lang.Math.max(maxFromRouteLen, fromRouteId == null ? 0 : fromRouteId.length());
+
+ final String routeId = safeNull(row.get("routeId"));
+ maxRouteLen = java.lang.Math.max(maxRouteLen, routeId == null ? 0 : routeId.length());
+
+ final String nodeId = safeNull(row.get("nodeId"));
+ maxNodeLen = java.lang.Math.max(maxNodeLen, nodeId == null ? 0 : nodeId.length());
+
+ final String elapsed = safeNull(row.get("elapsed"));
+ maxElapsedLen = java.lang.Math.max(maxElapsedLen, elapsed == null ? 0 : elapsed.length());
+
+ final String duration = safeNull(row.get("duration"));
+ maxDurationLen = java.lang.Math.max(maxDurationLen, duration == null ? 0 : duration.length());
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>(5);
+ retval.put(EXCHANGE_COLUMN_LABEL, maxExchangeLen);
+ retval.put(FROM_ROUTE_COLUMN_LABEL, maxFromRouteLen);
+ retval.put(ROUTE_COLUMN_LABEL, maxRouteLen);
+ retval.put(NODE_COLUMN_LABEL, maxNodeLen);
+ retval.put(ELAPSED_COLUMN_LABEL, maxElapsedLen);
+ retval.put(DURATION_COLUMN_LABEL, maxDurationLen);
+
+ return retval;
+ }
+ }
+
+ private static String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int exchangeLen = Math.min(columnWidths.get(EXCHANGE_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int fromRouteLen = Math.min(columnWidths.get(FROM_ROUTE_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int routeLen = Math.min(columnWidths.get(ROUTE_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int nodeLen = Math.min(columnWidths.get(NODE_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int elapsedLen = Math.min(columnWidths.get(ELAPSED_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int durationLen = Math.min(columnWidths.get(DURATION_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ exchangeLen = Math.max(MIN_COLUMN_WIDTH, exchangeLen);
+ fromRouteLen = Math.max(MIN_COLUMN_WIDTH, fromRouteLen);
+ routeLen = Math.max(MIN_COLUMN_WIDTH, routeLen);
+ nodeLen = Math.max(MIN_COLUMN_WIDTH, nodeLen);
+ elapsedLen = Math.max(MIN_COLUMN_WIDTH, elapsedLen);
+ durationLen = Math.max(13, durationLen);
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(exchangeLen).append('.').append(exchangeLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(fromRouteLen).append('.').append(fromRouteLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(routeLen).append('.').append(routeLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(nodeLen).append('.').append(nodeLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(elapsedLen).append('.').append(elapsedLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(durationLen).append('.').append(durationLen).append('s').append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextInfoCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextInfoCommand.java
new file mode 100644
index 0000000..6c5ed2e
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextInfoCommand.java
@@ -0,0 +1,245 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.camel.support.dump.CamelContextStatDump;
+
+import static org.apache.camel.util.ObjectHelper.isEmpty;
+
+/**
+ * Command to display detailed information about a given {@link org.apache.camel.CamelContext}.
+ */
+public class ContextInfoCommand extends AbstractContextCommand {
+
+ public static final String XML_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+ public static final String OUTPUT_TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ private StringEscape stringEscape;
+ private boolean verbose;
+
+ /**
+ * @param context The name of the Camel context
+ * @param verbose Whether to output verbose
+ */
+ public ContextInfoCommand(String context, boolean verbose) {
+ super(context);
+ this.verbose = verbose;
+ }
+
+ /**
+ * Sets the {@link org.apache.camel.commands.StringEscape} to use.
+ */
+ public void setStringEscape(StringEscape stringEscape) {
+ this.stringEscape = stringEscape;
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ Map<String, Object> row = camelController.getCamelContextInformation(context);
+ if (row == null || row.isEmpty()) {
+ err.println("Camel context " + context + " not found.");
+ return null;
+ }
+
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mCamel Context " + context + "\u001B[0m"));
+ out.println(stringEscape.unescapeJava("\tName: " + row.get("name")));
+ out.println(stringEscape.unescapeJava("\tManagementName: " + row.get("managementName")));
+ out.println(stringEscape.unescapeJava("\tVersion: " + row.get("version")));
+ out.println(stringEscape.unescapeJava("\tStatus: " + row.get("status")));
+ out.println(stringEscape.unescapeJava("\tUptime: " + row.get("uptime")));
+
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mMiscellaneous\u001B[0m"));
+ out.println(stringEscape.unescapeJava("\tSuspended: " + row.get("suspended")));
+ out.println(stringEscape.unescapeJava("\tShutdown Timeout: " + row.get("shutdownTimeout") + " sec."));
+ if (row.get("managementStatisticsLevel") != null) {
+ out.println(stringEscape.unescapeJava("\tManagement StatisticsLevel: " + row.get("managementStatisticsLevel")));
+ }
+ out.println(stringEscape.unescapeJava("\tAllow UseOriginalMessage: " + row.get("allowUseOriginalMessage")));
+ out.println(stringEscape.unescapeJava("\tMessage History: " + row.get("messageHistory")));
+ out.println(stringEscape.unescapeJava("\tTracing: " + row.get("tracing")));
+ out.println(stringEscape.unescapeJava("\tLog Mask: " + row.get("logMask")));
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mProperties\u001B[0m"));
+ for (Map.Entry<String, Object> entry : row.entrySet()) {
+ String key = entry.getKey();
+ if (key.startsWith("property.")) {
+ key = key.substring(9);
+ out.println(stringEscape.unescapeJava("\t" + key + " = " + entry.getValue()));
+ }
+ }
+
+ if (verbose) {
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mAdvanced\u001B[0m"));
+ out.println(stringEscape.unescapeJava("\tClassResolver: " + row.get("classResolver")));
+ out.println(stringEscape.unescapeJava("\tPackageScanClassResolver: " + row.get("packageScanClassResolver")));
+ out.println(stringEscape.unescapeJava("\tApplicationContextClassLoader: " + row.get("applicationContextClassLoader")));
+ out.println(stringEscape.unescapeJava("\tHeadersMapFactory: " + row.get("headersMapFactory")));
+
+ printStatistics(camelController, out);
+
+ // add type converter details
+ out.println(stringEscape.unescapeJava("\tNumber of type converters: " + row.get("typeConverter.numberOfTypeConverters")));
+ boolean enabled = false;
+ if (row.get("typeConverter.statisticsEnabled") != null) {
+ enabled = (boolean) row.get("typeConverter.statisticsEnabled");
+ }
+ if (enabled) {
+ long noop = (long) row.get("typeConverter.noopCounter");
+ long attempt = (long) row.get("typeConverter.attemptCounter");
+ long hit = (long) row.get("typeConverter.hitCounter");
+ long miss = (long) row.get("typeConverter.missCounter");
+ long failed = (long) row.get("typeConverter.failedCounter");
+ out.println(stringEscape.unescapeJava(String.format("\tType converter usage: [noop=%s, attempts=%s, hits=%s, misses=%s, failures=%s]", noop, attempt, hit, miss, failed)));
+ }
+
+ // add async processor await details
+ out.println(stringEscape.unescapeJava("\tNumber of blocked threads: " + row.get("asyncProcessorAwaitManager.size")));
+ enabled = false;
+ if (row.get("asyncProcessorAwaitManager.statisticsEnabled") != null) {
+ enabled = (boolean) row.get("asyncProcessorAwaitManager.statisticsEnabled");
+ }
+ if (enabled) {
+ long blocked = (long) row.get("asyncProcessorAwaitManager.threadsBlocked");
+ long interrupted = (long) row.get("asyncProcessorAwaitManager.threadsInterrupted");
+ long total = (long) row.get("asyncProcessorAwaitManager.totalDuration");
+ long min = (long) row.get("asyncProcessorAwaitManager.minDuration");
+ long max = (long) row.get("asyncProcessorAwaitManager.maxDuration");
+ long mean = (long) row.get("asyncProcessorAwaitManager.meanDuration");
+ out.println(stringEscape.unescapeJava(String.format("\tAsyncProcessorAwaitManager usage: [blocked=%s, interrupted=%s, total=%s msec, min=%s msec, max=%s msec, mean=%s msec]",
+ blocked, interrupted, total, min, max, mean)));
+ }
+
+ // add stream caching details if enabled
+ enabled = (boolean) row.get("streamCachingEnabled");
+ if (enabled) {
+ Object spoolDirectory = safeNull(row.get("streamCaching.spoolDirectory"));
+ Object spoolCipher = safeNull(row.get("streamCaching.spoolCipher"));
+ Object spoolThreshold = safeNull(row.get("streamCaching.spoolThreshold"));
+ Object spoolUsedHeapMemoryThreshold = safeNull(row.get("streamCaching.spoolUsedHeapMemoryThreshold"));
+ Object spoolUsedHeapMemoryLimit = safeNull(row.get("streamCaching.spoolUsedHeapMemoryLimit"));
+ Object anySpoolRules = safeNull(row.get("streamCaching.anySpoolRules"));
+ Object bufferSize = safeNull(row.get("streamCaching.bufferSize"));
+ Object removeSpoolDirectoryWhenStopping = safeNull(row.get("streamCaching.removeSpoolDirectoryWhenStopping"));
+ boolean statisticsEnabled = (boolean) row.get("streamCaching.statisticsEnabled");
+
+ String text = String.format("\tStream caching: [spoolDirectory=%s, spoolCipher=%s, spoolThreshold=%s, spoolUsedHeapMemoryThreshold=%s, "
+ + "spoolUsedHeapMemoryLimit=%s, anySpoolRules=%s, bufferSize=%s, removeSpoolDirectoryWhenStopping=%s, statisticsEnabled=%s]",
+ spoolDirectory, spoolCipher, spoolThreshold, spoolUsedHeapMemoryThreshold, spoolUsedHeapMemoryLimit, anySpoolRules, bufferSize,
+ removeSpoolDirectoryWhenStopping, statisticsEnabled);
+ out.println(stringEscape.unescapeJava(text));
+
+ if (statisticsEnabled) {
+ Object cacheMemoryCounter = safeNull(row.get("streamCaching.cacheMemoryCounter"));
+ Object cacheMemorySize = safeNull(row.get("streamCaching.cacheMemorySize"));
+ Object cacheMemoryAverageSize = safeNull(row.get("streamCaching.cacheMemoryAverageSize"));
+ Object cacheSpoolCounter = safeNull(row.get("streamCaching.cacheSpoolCounter"));
+ Object cacheSpoolSize = safeNull(row.get("streamCaching.cacheSpoolSize"));
+ Object cacheSpoolAverageSize = safeNull(row.get("streamCaching.cacheSpoolAverageSize"));
+
+ text = String.format("\t [cacheMemoryCounter=%s, cacheMemorySize=%s, cacheMemoryAverageSize=%s, cacheSpoolCounter=%s, "
+ + "cacheSpoolSize=%s, cacheSpoolAverageSize=%s]",
+ cacheMemoryCounter, cacheMemorySize, cacheMemoryAverageSize, cacheSpoolCounter, cacheSpoolSize, cacheSpoolAverageSize);
+ out.println(stringEscape.unescapeJava(text));
+ }
+ }
+
+ long totalRoutes = (long) row.get("totalRoutes");
+ long startedRoutes = (long) row.get("totalRoutes");
+ out.println(stringEscape.unescapeJava("\tNumber of running routes: " + startedRoutes));
+ out.println(stringEscape.unescapeJava("\tNumber of not running routes: " + (totalRoutes - startedRoutes)));
+ }
+
+ return null;
+ }
+
+ protected void printStatistics(CamelController camelController, PrintStream out) throws Exception {
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mStatistics\u001B[0m"));
+
+ String xml = camelController.getCamelContextStatsAsXml(context, true, false);
+ if (xml != null) {
+ JAXBContext context = JAXBContext.newInstance(CamelContextStatDump.class);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ CamelContextStatDump stat = (CamelContextStatDump) unmarshaller.unmarshal(new StringReader(xml));
+
+ long total = stat.getExchangesCompleted() + stat.getExchangesFailed();
+ out.println(stringEscape.unescapeJava("\tExchanges Total: " + total));
+ out.println(stringEscape.unescapeJava("\tExchanges Completed: " + stat.getExchangesCompleted()));
+ out.println(stringEscape.unescapeJava("\tExchanges Failed: " + stat.getExchangesFailed()));
+ out.println(stringEscape.unescapeJava("\tExchanges Inflight: " + stat.getExchangesInflight()));
+ out.println(stringEscape.unescapeJava("\tMin Processing Time: " + stat.getMinProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tMax Processing Time: " + stat.getMaxProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tMean Processing Time: " + stat.getMeanProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tTotal Processing Time: " + stat.getTotalProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tLast Processing Time: " + stat.getLastProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tDelta Processing Time: " + stat.getDeltaProcessingTime() + " ms"));
+
+ if (isEmpty(stat.getStartTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tStart Statistics Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(stat.getStartTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tStart Statistics Date: " + text));
+ }
+
+ // Test for null to see if a any exchanges have been processed first to avoid NPE
+ if (isEmpty(stat.getResetTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tReset Statistics Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(stat.getResetTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tReset Statistics Date: " + text));
+ }
+
+ // Test for null to see if a any exchanges have been processed first to avoid NPE
+ if (isEmpty(stat.getFirstExchangeCompletedTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tFirst Exchange Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(stat.getFirstExchangeCompletedTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tFirst Exchange Date: " + text));
+ }
+
+ // Test for null to see if a any exchanges have been processed first to avoid NPE
+ if (isEmpty(stat.getLastExchangeCompletedTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tLast Exchange Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(stat.getLastExchangeCompletedTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tLast Exchange Date: " + text));
+ }
+ }
+
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextListCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextListCommand.java
new file mode 100644
index 0000000..59b536b
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextListCommand.java
@@ -0,0 +1,145 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Command to list all {@link org.apache.camel.CamelContext} in the JVM.
+ */
+public class ContextListCommand extends AbstractCamelCommand {
+
+ private static final String CONTEXT_COLUMN_LABEL = "Context";
+ private static final String STATUS_COLUMN_LABEL = "Status";
+ private static final String TOTAL_COLUMN_LABEL = "Total #";
+ private static final String FAILED_COLUMN_LABEL = "Failed #";
+ private static final String INFLIGHT_COLUMN_LABEL = "Inflight #";
+ private static final String UPTIME_COLUMN_LABEL = "Uptime";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final int MAX_COLUMN_WIDTH = Integer.MAX_VALUE;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ final List<Map<String, String>> camelContexts = camelController.getCamelContexts();
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(camelContexts);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ if (camelContexts.size() > 0) {
+ out.println(String.format(headerFormat, CONTEXT_COLUMN_LABEL, STATUS_COLUMN_LABEL, TOTAL_COLUMN_LABEL, FAILED_COLUMN_LABEL, INFLIGHT_COLUMN_LABEL, UPTIME_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "------", "-------", "--------", "----------", "------"));
+ for (Map<String, String> row : camelContexts) {
+ out.println(String.format(rowFormat, row.get("name"), row.get("state"), row.get("exchangesTotal"),
+ row.get("exchangesFailed"), row.get("exchangesInflight"), row.get("uptime")));
+ }
+ }
+
+ return null;
+ }
+
+ private static Map<String, Integer> computeColumnWidths(final Iterable<Map<String, String>> camelContexts) throws Exception {
+ if (camelContexts == null) {
+ throw new IllegalArgumentException("Unable to determine column widths from null Iterable<CamelContext>");
+ } else {
+ int maxNameLen = 0;
+ int maxStatusLen = 0;
+ int maxTotalLen = 0;
+ int maxFailedLen = 0;
+ int maxInflightLen = 0;
+ int maxUptimeLen = 0;
+
+ for (Map<String, String> row : camelContexts) {
+ final String name = row.get("name");
+ maxNameLen = java.lang.Math.max(maxNameLen, name == null ? 0 : name.length());
+
+ final String status = row.get("state");
+ maxStatusLen = java.lang.Math.max(maxStatusLen, status == null ? 0 : status.length());
+
+ final String total = row.get("exchangesTotal");
+ maxTotalLen = java.lang.Math.max(maxTotalLen, total == null ? 0 : total.length());
+
+ final String failed = row.get("exchangesFailed");
+ maxFailedLen = java.lang.Math.max(maxFailedLen, failed == null ? 0 : failed.length());
+
+ final String inflight = row.get("exchangesInflight");
+ maxInflightLen = java.lang.Math.max(maxInflightLen, inflight == null ? 0 : inflight.length());
+
+ final String uptime = row.get("uptime");
+ maxUptimeLen = java.lang.Math.max(maxUptimeLen, uptime == null ? 0 : uptime.length());
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>();
+ retval.put(CONTEXT_COLUMN_LABEL, maxNameLen);
+ retval.put(STATUS_COLUMN_LABEL, maxStatusLen);
+ retval.put(TOTAL_COLUMN_LABEL, maxTotalLen);
+ retval.put(FAILED_COLUMN_LABEL, maxFailedLen);
+ retval.put(INFLIGHT_COLUMN_LABEL, maxInflightLen);
+ retval.put(UPTIME_COLUMN_LABEL, maxUptimeLen);
+
+ return retval;
+ }
+ }
+
+ private static String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int contextLen = java.lang.Math.min(columnWidths.get(CONTEXT_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int statusLen = java.lang.Math.min(columnWidths.get(STATUS_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int totalLen = java.lang.Math.min(columnWidths.get(TOTAL_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int failedlLen = java.lang.Math.min(columnWidths.get(FAILED_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int inflightLen = java.lang.Math.min(columnWidths.get(INFLIGHT_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int uptimeLen = java.lang.Math.min(columnWidths.get(UPTIME_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ contextLen = Math.max(MIN_COLUMN_WIDTH, contextLen);
+ statusLen = Math.max(MIN_COLUMN_WIDTH, statusLen);
+ totalLen = Math.max(MIN_COLUMN_WIDTH, totalLen);
+ failedlLen = Math.max(MIN_COLUMN_WIDTH, failedlLen);
+ inflightLen = Math.max(MIN_COLUMN_WIDTH, inflightLen);
+ uptimeLen = Math.max(MIN_COLUMN_WIDTH, uptimeLen);
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(contextLen).append('.').append(contextLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(statusLen).append('.').append(statusLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(totalLen).append('.').append(totalLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(failedlLen).append('.').append(failedlLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(inflightLen).append('.').append(inflightLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(uptimeLen).append('.').append(uptimeLen).append('s').append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextResumeCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextResumeCommand.java
new file mode 100644
index 0000000..e949ad6
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextResumeCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to resume a Camel context.
+ */
+public class ContextResumeCommand extends AbstractContextCommand {
+
+ public ContextResumeCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ camelController.resumeContext(contextName);
+ return null;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextStartCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextStartCommand.java
new file mode 100644
index 0000000..63a20da
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextStartCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to start a Camel context.
+ */
+public class ContextStartCommand extends AbstractContextCommand {
+
+ public ContextStartCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ camelController.startContext(contextName);
+ return null;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextStopCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextStopCommand.java
new file mode 100644
index 0000000..db33f71
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextStopCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to stop a Camel context.
+ */
+public class ContextStopCommand extends AbstractContextCommand {
+
+ public ContextStopCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ camelController.stopContext(contextName);
+ return null;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextSuspendCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextSuspendCommand.java
new file mode 100644
index 0000000..19dcaa4
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ContextSuspendCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to suspend a Camel context.
+ */
+public class ContextSuspendCommand extends AbstractContextCommand {
+
+ public ContextSuspendCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ camelController.suspendContext(contextName);
+ return null;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/EndpointListCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/EndpointListCommand.java
new file mode 100644
index 0000000..18ac8dd
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/EndpointListCommand.java
@@ -0,0 +1,169 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.util.URISupport;
+
+/**
+ * List the Camel endpoints available in the JVM.
+ */
+public class EndpointListCommand extends AbstractCamelCommand {
+
+ private static final String URI_COLUMN_LABEL = "Uri";
+ private static final String STATUS_COLUMN_LABEL = "Status";
+ private static final String CONTEXT_NAME_COLUMN_LABEL = "Context";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ // endpoint uris can be very long so clip by default after 120 chars
+ private static final int MAX_COLUMN_WIDTH = 120;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ boolean decode = true;
+ boolean verbose;
+ private final String context;
+
+ public EndpointListCommand(String context, boolean decode, boolean verbose) {
+ this.decode = decode;
+ this.verbose = verbose;
+ this.context = context;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ final List<Map<String, String>> camelContextInfos = camelController.getCamelContexts(this.context);
+ final Map<String, List<Map<String, String>>> contextsToEndpoints = new HashMap<>();
+
+ for (Map<String, String> camelContextInfo : camelContextInfos) {
+ String camelContextName = camelContextInfo.get("name");
+ final List<Map<String, String>> endpoints = camelController.getEndpoints(camelContextName);
+ if (endpoints.isEmpty()) {
+ continue;
+ }
+ contextsToEndpoints.put(camelContextName, endpoints);
+ }
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(contextsToEndpoints);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ for (Map.Entry<String, List<Map<String, String>>> stringListEntry : contextsToEndpoints.entrySet()) {
+ final String camelContextName = stringListEntry.getKey();
+ final List<Map<String, String>> endpoints = stringListEntry.getValue();
+
+ out.println(String.format(headerFormat, CONTEXT_NAME_COLUMN_LABEL, URI_COLUMN_LABEL, STATUS_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "---", "------"));
+ for (Map<String, String> row : endpoints) {
+ String uri = row.get("uri");
+ if (decode) {
+ // decode uri so its more human readable
+ uri = URLDecoder.decode(uri, "UTF-8");
+ }
+ // sanitize and mask uri so we dont see passwords
+ uri = URISupport.sanitizeUri(uri);
+ String state = row.get("state");
+ out.println(String.format(rowFormat, camelContextName, uri, state));
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Integer> computeColumnWidths(final Map<String, List<Map<String, String>>> contextsToEndpoints) throws Exception {
+ int maxUriLen = 0;
+ int maxStatusLen = 0;
+ int maxCamelContextLen = 0;
+
+ for (Map.Entry<String, List<Map<String, String>>> stringListEntry : contextsToEndpoints.entrySet()) {
+ final String camelContextName = stringListEntry.getKey();
+
+ maxCamelContextLen = java.lang.Math.max(maxCamelContextLen, camelContextName.length());
+
+ final List<Map<String, String>> endpoints = stringListEntry.getValue();
+
+
+ for (Map<String, String> row : endpoints) {
+ String uri = row.get("uri");
+ if (decode) {
+ // decode uri so its more human readable
+ uri = URLDecoder.decode(uri, "UTF-8");
+ }
+ // sanitize and mask uri so we dont see passwords
+ uri = URISupport.sanitizeUri(uri);
+
+ maxUriLen = java.lang.Math.max(maxUriLen, uri == null ? 0 : uri.length());
+
+ final String status = row.get("state");
+ maxStatusLen = java.lang.Math.max(maxStatusLen, status == null ? 0 : status.length());
+ }
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>();
+ retval.put(URI_COLUMN_LABEL, maxUriLen);
+ retval.put(STATUS_COLUMN_LABEL, maxStatusLen);
+ retval.put(CONTEXT_NAME_COLUMN_LABEL, maxCamelContextLen);
+
+ return retval;
+ }
+
+ private String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int uriLen = java.lang.Math.min(columnWidths.get(URI_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ uriLen = Math.max(MIN_COLUMN_WIDTH, uriLen);
+
+ int ctxLen = java.lang.Math.min(columnWidths.get(CONTEXT_NAME_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ ctxLen = Math.max(MIN_COLUMN_WIDTH, ctxLen);
+ // last row does not have min width
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(ctxLen).append('.').append(ctxLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(uriLen).append('.').append(uriLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%s").append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+ private int getMaxColumnWidth() {
+ if (verbose) {
+ return Integer.MAX_VALUE;
+ } else {
+ return MAX_COLUMN_WIDTH;
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/EndpointStatisticCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/EndpointStatisticCommand.java
new file mode 100644
index 0000000..ab02639
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/EndpointStatisticCommand.java
@@ -0,0 +1,249 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.net.URLDecoder;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.util.URISupport;
+
+/**
+ * Display endpoint runtime statistics for a CamelContext
+ */
+public class EndpointStatisticCommand extends AbstractCamelCommand {
+
+ private static final String CONTEXT_COLUMN_LABEL = "Context";
+ private static final String URI_COLUMN_LABEL = "Uri";
+ private static final String ROUTE_COLUMN_LABEL = "Route Id";
+ private static final String DIRECTION_COLUMN_LABEL = "Direction";
+ private static final String STATIC_COLUMN_LABEL = "Static";
+ private static final String DYNAMIC_COLUMN_LABEL = "Dynamic";
+ private static final String HITS_COLUMN_LABEL = "Total #";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ // endpoint uris can be very long so clip by default after 120 chars
+ private static final int MAX_COLUMN_WIDTH = 120;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ String context;
+ boolean decode = true;
+ private String[] filter;
+
+ public EndpointStatisticCommand(String context, boolean decode, String[] filter) {
+ this.context = context;
+ this.decode = decode;
+ this.filter = filter;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ List<Map<String, String>> contexts = camelController.getCamelContexts(context);
+
+ boolean header = false;
+
+ Map<String, List<Map<String, String>>> allEndpoints = new LinkedHashMap<>();
+
+ for (Map<String, String> context : contexts) {
+ String contextName = context.get("name");
+ List<Map<String, String>> endpoints = camelController.getEndpointRuntimeStatistics(contextName);
+ allEndpoints.put(contextName, endpoints);
+ }
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(allEndpoints);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ for (Map.Entry<String, List<Map<String, String>>> entry : allEndpoints.entrySet()) {
+ String contextName = entry.getKey();
+ for (Map<String, String> row : entry.getValue()) {
+
+ if (!header) {
+ out.println(String.format(headerFormat, CONTEXT_COLUMN_LABEL, URI_COLUMN_LABEL,
+ ROUTE_COLUMN_LABEL, DIRECTION_COLUMN_LABEL, STATIC_COLUMN_LABEL,
+ DYNAMIC_COLUMN_LABEL, HITS_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "---", "--------",
+ "---------", "------", "-------", "-------"));
+ header = true;
+ }
+
+ String uri = row.get("uri");
+ if (decode) {
+ // decode uri so its more human readable
+ uri = URLDecoder.decode(uri, "UTF-8");
+ }
+ // sanitize and mask uri so we dont see passwords
+ uri = URISupport.sanitizeUri(uri);
+ String routeId = row.get("routeId");
+ String direction = row.get("direction");
+ String isStatic = row.get("static");
+ String isDynamic = row.get("dynamic");
+ String hits = row.get("hits");
+
+ // should we filter
+ if (isValidRow(direction, isStatic, isDynamic)) {
+ out.println(String.format(rowFormat, contextName, uri, routeId, direction, isStatic, isDynamic, hits));
+ }
+ }
+ }
+
+ return null;
+ }
+
+ protected boolean isValidRow(String direction, String isStatic, String isDynamic) {
+ if (filter == null || filter.length == 0) {
+ return true;
+ }
+
+ boolean answer = false;
+ for (String f : filter) {
+ if ("in".equals(f)) {
+ answer = "in".equals(direction);
+ } else if ("out".equals(f)) {
+ answer = "out".equals(direction);
+ } else if ("static".equals(f)) {
+ answer = "true".equals(isStatic);
+ } else if ("dynamic".equals(f)) {
+ answer = "true".equals(isDynamic);
+ }
+ // all filters must apply to accept when multi valued
+ if (!answer) {
+ return false;
+ }
+ }
+ return answer;
+ }
+
+ private Map<String, Integer> computeColumnWidths(Map<String, List<Map<String, String>>> allEndpoints) throws Exception {
+ if (allEndpoints == null) {
+ throw new IllegalArgumentException("Unable to determine column widths from null Iterable<Endpoint>");
+ } else {
+ int maxContextLen = 0;
+ int maxUriLen = 0;
+ int maxRouteIdLen = 0;
+ int maxDirectionLen = 0;
+ int maxStaticLen = 0;
+ int maxDynamicLen = 0;
+ int maxHitsLen = 0;
+
+ for (Map.Entry<String, List<Map<String, String>>> entry : allEndpoints.entrySet()) {
+ String contextName = entry.getKey();
+ for (Map<String, String> row : entry.getValue()) {
+ maxContextLen = Math.max(maxContextLen, contextName == null ? 0 : contextName.length());
+
+ String uri = row.get("uri");
+ if (decode) {
+ // decode uri so its more human readable
+ uri = URLDecoder.decode(uri, "UTF-8");
+ }
+ // sanitize and mask uri so we dont see passwords
+ uri = URISupport.sanitizeUri(uri);
+
+ maxUriLen = Math.max(maxUriLen, uri == null ? 0 : uri.length());
+
+ final String routeId = row.get("routeId");
+ maxRouteIdLen = Math.max(maxRouteIdLen, routeId == null ? 0 : routeId.length());
+
+ final String direction = row.get("direction");
+ maxDirectionLen = Math.max(maxDirectionLen, direction == null ? 0 : direction.length());
+
+ final String isStatic = row.get("static");
+ maxStaticLen = Math.max(maxStaticLen, isStatic == null ? 0 : isStatic.length());
+
+ final String isDynamic = row.get("dynamic");
+ maxDynamicLen = Math.max(maxDynamicLen, isDynamic == null ? 0 : isDynamic.length());
+
+ final String hits = row.get("hits");
+ maxHitsLen = Math.max(maxHitsLen, hits == null ? 0 : hits.length());
+ }
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>();
+ retval.put(CONTEXT_COLUMN_LABEL, maxContextLen);
+ retval.put(URI_COLUMN_LABEL, maxUriLen);
+ retval.put(ROUTE_COLUMN_LABEL, maxRouteIdLen);
+ retval.put(DIRECTION_COLUMN_LABEL, maxDirectionLen);
+ retval.put(STATIC_COLUMN_LABEL, maxStaticLen);
+ retval.put(DYNAMIC_COLUMN_LABEL, maxDynamicLen);
+ retval.put(HITS_COLUMN_LABEL, maxHitsLen);
+
+ return retval;
+ }
+ }
+
+ private String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int contextLen = Math.min(columnWidths.get(CONTEXT_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ contextLen = Math.max(MIN_COLUMN_WIDTH, contextLen);
+
+ int uriLen = Math.min(columnWidths.get(URI_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ uriLen = Math.max(MIN_COLUMN_WIDTH, uriLen);
+
+ int routeIdLen = Math.min(columnWidths.get(ROUTE_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ routeIdLen = Math.max(MIN_COLUMN_WIDTH, routeIdLen);
+
+ int directionLen = Math.min(columnWidths.get(DIRECTION_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ directionLen = Math.max(MIN_COLUMN_WIDTH, directionLen);
+
+ int staticLen = Math.min(columnWidths.get(STATIC_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ staticLen = Math.max(MIN_COLUMN_WIDTH, staticLen);
+
+ int dynamicLen = Math.min(columnWidths.get(DYNAMIC_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ dynamicLen = Math.max(MIN_COLUMN_WIDTH, dynamicLen);
+
+ int totalLen = Math.min(columnWidths.get(HITS_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ totalLen = Math.max(MIN_COLUMN_WIDTH, totalLen);
+
+ // last row does not have min width
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(contextLen).append('.').append(contextLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(uriLen).append('.').append(uriLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(routeIdLen).append('.').append(routeIdLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(directionLen).append('.').append(directionLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(staticLen).append('.').append(staticLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(dynamicLen).append('.').append(dynamicLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(totalLen).append('.').append(totalLen).append('s').append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+ private int getMaxColumnWidth() {
+ return MAX_COLUMN_WIDTH;
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/LocalCamelController.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/LocalCamelController.java
new file mode 100644
index 0000000..6c3cf1f
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/LocalCamelController.java
@@ -0,0 +1,47 @@
+/*
+ * 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.commands;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+
+/**
+ * A {@link org.apache.camel.commands.CamelController} that runs locally, eg within the same JVM as the {@link CamelContext}s
+ * it manages.
+ *
+ * For example the Apache Camel Karaf commands does this.
+ */
+public interface LocalCamelController extends CamelController {
+
+ /**
+ * Get the list of Camel context.
+ *
+ * @return the list of Camel contexts.
+ * @throws Exception can be thrown
+ */
+ List<CamelContext> getLocalCamelContexts() throws Exception;
+
+ /**
+ * Get a Camel context identified by the given name.
+ *
+ * @param name the Camel context name.
+ * @return the Camel context or null if not found.
+ * @throws Exception can be thrown
+ */
+ CamelContext getLocalCamelContext(String name) throws Exception;
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestApiDocCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestApiDocCommand.java
new file mode 100644
index 0000000..8db7bc4
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestApiDocCommand.java
@@ -0,0 +1,42 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * List the Camel REST services API documentation (requires camel-swagger-java on classpath)
+ */
+public class RestApiDocCommand extends AbstractContextCommand {
+
+ public RestApiDocCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ String json = camelController.getRestApiDocAsJson(contextName);
+ if (json != null) {
+ out.print(json);
+ } else {
+ out.print("There are no REST services");
+ }
+
+ return null;
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestRegistryListCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestRegistryListCommand.java
new file mode 100644
index 0000000..e2327d5
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestRegistryListCommand.java
@@ -0,0 +1,198 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.net.URLDecoder;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.util.URISupport;
+
+/**
+ * List the Camel REST services from the Rest registry available in the JVM.
+ */
+public class RestRegistryListCommand extends AbstractContextCommand {
+
+ private static final String URL_COLUMN_NAME = "Url";
+ private static final String BASE_PATH_LABEL = "Base Path";
+ private static final String URI_TEMPLATE_LABEL = "Uri Template";
+ private static final String METHOD_COLUMN_LABEL = "Method";
+ private static final String STATE_COLUMN_LABEL = "State";
+ private static final String ROUTE_COLUMN_LABEL = "Route Id";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ // endpoint uris can be very long so clip by default after 120 chars
+ private static final int MAX_COLUMN_WIDTH = 120;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ boolean decode = true;
+ boolean verbose;
+
+ public RestRegistryListCommand(String context, boolean decode, boolean verbose) {
+ super(context);
+ this.decode = decode;
+ this.verbose = verbose;
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ List<Map<String, String>> services = camelController.getRestServices(contextName);
+ if (services.isEmpty()) {
+ out.print("There are no REST services");
+ return null;
+ }
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(services);
+ final String headerFormat = buildFormatString(columnWidths, true, verbose);
+ final String rowFormat = buildFormatString(columnWidths, false, verbose);
+
+ if (services.size() > 0) {
+ if (verbose) {
+ out.println(String.format(headerFormat, URL_COLUMN_NAME, BASE_PATH_LABEL, URI_TEMPLATE_LABEL, METHOD_COLUMN_LABEL, STATE_COLUMN_LABEL, ROUTE_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "---", "---------", "------------", "------", "-----", "--------"));
+ } else {
+ out.println(String.format(headerFormat, BASE_PATH_LABEL, URI_TEMPLATE_LABEL, METHOD_COLUMN_LABEL, STATE_COLUMN_LABEL, ROUTE_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "---------", "------------", "------", "-----", "--------"));
+ }
+ for (Map<String, String> row : services) {
+ String uri = null;
+ if (verbose) {
+ uri = row.get("url");
+ if (decode) {
+ // decode uri so its more human readable
+ uri = URLDecoder.decode(uri, "UTF-8");
+ }
+ // sanitize and mask uri so we dont see passwords
+ uri = URISupport.sanitizeUri(uri);
+ }
+ String basePath = row.get("basePath");
+ String uriTemplate = row.get("uriTemplate") != null ? row.get("uriTemplate") : "";
+ String method = row.get("method");
+ String state = row.get("state");
+ String route = row.get("routeId");
+ if (verbose) {
+ out.println(String.format(rowFormat, uri, basePath, uriTemplate, method, state, route));
+ } else {
+ out.println(String.format(rowFormat, basePath, uriTemplate, method, state, route));
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private Map<String, Integer> computeColumnWidths(List<Map<String, String>> services) throws Exception {
+ int maxUriLen = 0;
+ int maxBasePathLen = 0;
+ int maxUriTemplateLen = 0;
+ int maxMethodLen = 0;
+ int maxStatusLen = 0;
+ int maxRouteLen = 0;
+
+ for (Map<String, String> row : services) {
+ String uri = row.get("url");
+ if (decode) {
+ // decode uri so its more human readable
+ uri = URLDecoder.decode(uri, "UTF-8");
+ }
+ // sanitize and mask uri so we dont see passwords
+ uri = URISupport.sanitizeUri(uri);
+ maxUriLen = Math.max(maxUriLen, uri == null ? 0 : uri.length());
+
+ String basePath = row.get("basePath");
+ maxBasePathLen = Math.max(maxBasePathLen, basePath == null ? 0 : basePath.length());
+
+ String uriTemplate = row.get("uriTemplate");
+ maxUriTemplateLen = Math.max(maxUriTemplateLen, uriTemplate == null ? 0 : uriTemplate.length());
+
+ String method = row.get("method");
+ maxMethodLen = Math.max(maxMethodLen, method == null ? 0 : method.length());
+
+ String status = row.get("state");
+ maxStatusLen = Math.max(maxStatusLen, status == null ? 0 : status.length());
+
+ String routeId = row.get("routeId");
+ maxRouteLen = Math.max(maxRouteLen, routeId == null ? 0 : routeId.length());
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>();
+ retval.put(URL_COLUMN_NAME, maxUriLen);
+ retval.put(BASE_PATH_LABEL, maxBasePathLen);
+ retval.put(URI_TEMPLATE_LABEL, maxUriTemplateLen);
+ retval.put(METHOD_COLUMN_LABEL, maxMethodLen);
+ retval.put(STATE_COLUMN_LABEL, maxStatusLen);
+ retval.put(ROUTE_COLUMN_LABEL, maxRouteLen);
+
+ return retval;
+ }
+
+ private String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader, final boolean isVerbose) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int uriLen = Math.min(columnWidths.get(URL_COLUMN_NAME) + columnWidthIncrement, getMaxColumnWidth());
+ int basePathLen = Math.min(columnWidths.get(BASE_PATH_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ int uriTemplateLen = Math.min(columnWidths.get(URI_TEMPLATE_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ int methodLen = Math.min(columnWidths.get(METHOD_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ int statusLen = Math.min(columnWidths.get(STATE_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ int routeLen = Math.min(columnWidths.get(ROUTE_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ uriLen = Math.max(MIN_COLUMN_WIDTH, uriLen);
+ basePathLen = Math.max(MIN_COLUMN_WIDTH, basePathLen);
+ uriTemplateLen = Math.max(MIN_COLUMN_WIDTH, uriTemplateLen);
+ methodLen = Math.max(MIN_COLUMN_WIDTH, methodLen);
+ statusLen = Math.max(MIN_COLUMN_WIDTH, statusLen);
+
+ // last row does not have min width
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ if (isVerbose) {
+ retval.append(fieldPreamble).append("%-").append(uriLen).append('.').append(uriLen).append('s').append(fieldPostamble).append(' ');
+ }
+ retval.append(fieldPreamble).append("%-").append(basePathLen).append('.').append(basePathLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(uriTemplateLen).append('.').append(uriTemplateLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(methodLen).append('.').append(methodLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(statusLen).append('.').append(statusLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(routeLen).append('.').append(routeLen).append('s').append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+ private int getMaxColumnWidth() {
+ if (verbose) {
+ return Integer.MAX_VALUE;
+ } else {
+ return MAX_COLUMN_WIDTH;
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestShowCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestShowCommand.java
new file mode 100644
index 0000000..fe027f9
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RestShowCommand.java
@@ -0,0 +1,40 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to show the REST marshaled in XML.
+ */
+public class RestShowCommand extends AbstractContextCommand {
+
+ public RestShowCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ String xml = camelController.getRestModelAsXml(contextName);
+ if (xml == null) {
+ out.println("There are no REST services in CamelContext with name: " + contextName);
+ } else {
+ out.println(xml);
+ }
+ return null;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteInfoCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteInfoCommand.java
new file mode 100644
index 0000000..fe83b02
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteInfoCommand.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.camel.commands;
+
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.camel.support.dump.RouteStatDump;
+
+import static org.apache.camel.util.ObjectHelper.isEmpty;
+
+/**
+ * Command to display detailed information about a Camel route.
+ */
+public class RouteInfoCommand extends AbstractRouteCommand {
+
+ public static final String XML_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+ public static final String OUTPUT_TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ private StringEscape stringEscape;
+
+ public RouteInfoCommand(String route, String context) {
+ super(route, context);
+ }
+
+ /**
+ * Sets the {@link org.apache.camel.commands.StringEscape} to use.
+ */
+ public void setStringEscape(StringEscape stringEscape) {
+ this.stringEscape = stringEscape;
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ out.println(stringEscape.unescapeJava("\u001B[1mCamel Route " + routeId + "\u001B[0m"));
+ out.println(stringEscape.unescapeJava("\tCamel Context: " + contextName));
+
+ String xml = camelController.getRouteStatsAsXml(routeId, contextName, true, false);
+ if (xml != null) {
+ JAXBContext context = JAXBContext.newInstance(RouteStatDump.class);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ RouteStatDump route = (RouteStatDump) unmarshaller.unmarshal(new StringReader(xml));
+ out.println(stringEscape.unescapeJava("\tState: " + route.getState()));
+ out.println(stringEscape.unescapeJava("\tState: " + route.getState()));
+
+ out.println("");
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mStatistics\u001B[0m"));
+ long total = route.getExchangesCompleted() + route.getExchangesFailed();
+ out.println(stringEscape.unescapeJava("\tExchanges Total: " + total));
+ out.println(stringEscape.unescapeJava("\tExchanges Completed: " + route.getExchangesCompleted()));
+ out.println(stringEscape.unescapeJava("\tExchanges Failed: " + route.getExchangesFailed()));
+ out.println(stringEscape.unescapeJava("\tExchanges Inflight: " + route.getExchangesInflight()));
+ out.println(stringEscape.unescapeJava("\tMin Processing Time: " + route.getMinProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tMax Processing Time: " + route.getMaxProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tMean Processing Time: " + route.getMeanProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tTotal Processing Time: " + route.getTotalProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tLast Processing Time: " + route.getLastProcessingTime() + " ms"));
+ out.println(stringEscape.unescapeJava("\tDelta Processing Time: " + route.getDeltaProcessingTime() + " ms"));
+
+ if (isEmpty(route.getStartTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tStart Statistics Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(route.getStartTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tStart Statistics Date: " + text));
+ }
+
+ // Test for null to see if a any exchanges have been processed first to avoid NPE
+ if (isEmpty(route.getResetTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tReset Statistics Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(route.getResetTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tReset Statistics Date: " + text));
+ }
+
+ // Test for null to see if a any exchanges have been processed first to avoid NPE
+ if (isEmpty(route.getFirstExchangeCompletedTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tFirst Exchange Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(route.getFirstExchangeCompletedTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tFirst Exchange Date: " + text));
+ }
+
+ // Test for null to see if a any exchanges have been processed first to avoid NPE
+ if (isEmpty(route.getLastExchangeCompletedTimestamp())) {
+ // Print an empty value for scripting
+ out.println(stringEscape.unescapeJava("\tLast Exchange Date:"));
+ } else {
+ Date date = new SimpleDateFormat(XML_TIMESTAMP_FORMAT).parse(route.getLastExchangeCompletedTimestamp());
+ String text = new SimpleDateFormat(OUTPUT_TIMESTAMP_FORMAT).format(date);
+ out.println(stringEscape.unescapeJava("\tLast Exchange Date: " + text));
+ }
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteListCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteListCommand.java
new file mode 100644
index 0000000..9a03694
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteListCommand.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.commands;
+
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Command to list all Camel routes.
+ */
+public class RouteListCommand extends AbstractCamelCommand {
+
+ private static final String CONTEXT_COLUMN_LABEL = "Context";
+ private static final String ROUTE_COLUMN_LABEL = "Route";
+ private static final String STATUS_COLUMN_LABEL = "Status";
+ private static final String TOTAL_COLUMN_LABEL = "Total #";
+ private static final String FAILED_COLUMN_LABEL = "Failed #";
+ private static final String INFLIGHT_COLUMN_LABEL = "Inflight #";
+ private static final String UPTIME_COLUMN_LABEL = "Uptime";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ private static final int MAX_COLUMN_WIDTH = Integer.MAX_VALUE;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ String name;
+
+ public RouteListCommand(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ List<Map<String, String>> routes = camelController.getRoutes(name);
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(routes);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ if (routes.size() > 0) {
+ out.println(String.format(headerFormat, CONTEXT_COLUMN_LABEL, ROUTE_COLUMN_LABEL, STATUS_COLUMN_LABEL, TOTAL_COLUMN_LABEL, FAILED_COLUMN_LABEL, INFLIGHT_COLUMN_LABEL,
+ UPTIME_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "-----", "------", "-------", "--------", "----------", "------"));
+ for (Map<String, String> row : routes) {
+ String contextId = row.get("camelContextName");
+ String routeId = row.get("routeId");
+ String state = row.get("state");
+ String total = row.get("exchangesTotal");
+ String failed = row.get("exchangesFailed");
+ String inflight = row.get("exchangesInflight");
+ String uptime = row.get("uptime");
+ out.println(String.format(rowFormat, contextId, routeId, state, total, failed, inflight, uptime));
+ }
+ }
+
+ return null;
+ }
+
+ private static Map<String, Integer> computeColumnWidths(final Iterable<Map<String, String>> routes) throws Exception {
+ if (routes == null) {
+ throw new IllegalArgumentException("Unable to determine column widths from null Iterable<Route>");
+ } else {
+ int maxContextLen = 0;
+ int maxRouteLen = 0;
+ int maxStatusLen = 0;
+ int maxTotalLen = 0;
+ int maxFailedLen = 0;
+ int maxInflightLen = 0;
+ int maxUptimeLen = 0;
+
+ for (Map<String, String> row : routes) {
+ final String contextId = row.get("camelContextName");
+ maxContextLen = java.lang.Math.max(maxContextLen, contextId == null ? 0 : contextId.length());
+
+ final String routeId = row.get("routeId");
+ maxRouteLen = java.lang.Math.max(maxRouteLen, routeId == null ? 0 : routeId.length());
+
+ final String status = row.get("state");
+ maxStatusLen = java.lang.Math.max(maxStatusLen, status == null ? 0 : status.length());
+
+ final String total = row.get("exchangesTotal");
+ maxTotalLen = java.lang.Math.max(maxTotalLen, total == null ? 0 : total.length());
+
+ final String failed = row.get("exchangesFailed");
+ maxFailedLen = java.lang.Math.max(maxFailedLen, failed == null ? 0 : failed.length());
+
+ final String inflight = row.get("exchangesInflight");
+ maxInflightLen = java.lang.Math.max(maxInflightLen, inflight == null ? 0 : inflight.length());
+
+ final String uptime = row.get("uptime");
+ maxUptimeLen = java.lang.Math.max(maxUptimeLen, uptime == null ? 0 : uptime.length());
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>(3);
+ retval.put(CONTEXT_COLUMN_LABEL, maxContextLen);
+ retval.put(ROUTE_COLUMN_LABEL, maxRouteLen);
+ retval.put(STATUS_COLUMN_LABEL, maxStatusLen);
+ retval.put(TOTAL_COLUMN_LABEL, maxTotalLen);
+ retval.put(FAILED_COLUMN_LABEL, maxFailedLen);
+ retval.put(INFLIGHT_COLUMN_LABEL, maxInflightLen);
+ retval.put(UPTIME_COLUMN_LABEL, maxUptimeLen);
+
+ return retval;
+ }
+ }
+
+ private static String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int contextLen = Math.min(columnWidths.get(CONTEXT_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int routeLen = Math.min(columnWidths.get(ROUTE_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int statusLen = Math.min(columnWidths.get(STATUS_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int totalLen = Math.min(columnWidths.get(TOTAL_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int failedlLen = Math.min(columnWidths.get(FAILED_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int inflightLen = Math.min(columnWidths.get(INFLIGHT_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ int uptimeLen = Math.min(columnWidths.get(UPTIME_COLUMN_LABEL) + columnWidthIncrement, MAX_COLUMN_WIDTH);
+ contextLen = Math.max(MIN_COLUMN_WIDTH, contextLen);
+ routeLen = Math.max(MIN_COLUMN_WIDTH, routeLen);
+ statusLen = Math.max(MIN_COLUMN_WIDTH, statusLen);
+ totalLen = Math.max(MIN_COLUMN_WIDTH, totalLen);
+ failedlLen = Math.max(MIN_COLUMN_WIDTH, failedlLen);
+ inflightLen = Math.max(MIN_COLUMN_WIDTH, inflightLen);
+ uptimeLen = Math.max(MIN_COLUMN_WIDTH, uptimeLen);
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(contextLen).append('.').append(contextLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(routeLen).append('.').append(routeLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(statusLen).append('.').append(statusLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(totalLen).append('.').append(totalLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(failedlLen).append('.').append(failedlLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%").append(inflightLen).append('.').append(inflightLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(uptimeLen).append('.').append(uptimeLen).append('s').append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteProfileCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteProfileCommand.java
new file mode 100644
index 0000000..469a79a
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteProfileCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.io.StringReader;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.camel.support.dump.ProcessorStatDump;
+import org.apache.camel.support.dump.RouteStatDump;
+
+/**
+ * Command to display profile information about a Camel route.
+ */
+public class RouteProfileCommand extends AbstractRouteCommand {
+
+ protected static final String HEADER_FORMAT = "%-30s %10s %12s %12s %12s %12s %12s %12s %12s";
+ protected static final String OUTPUT_FORMAT = "%-30s %10d %12d %12d %12d %12d %12d %12d %12d";
+
+ private StringEscape stringEscape;
+ private volatile String previousCamelContextName;
+
+ public RouteProfileCommand(String route, String context) {
+ super(route, context);
+ }
+
+ /**
+ * Sets the {@link org.apache.camel.commands.StringEscape} to use.
+ */
+ public void setStringEscape(StringEscape stringEscape) {
+ this.stringEscape = stringEscape;
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+
+ JAXBContext context = JAXBContext.newInstance(RouteStatDump.class);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ // write new header for new camel context
+ if (previousCamelContextName == null || !previousCamelContextName.equals(contextName)) {
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mProfile\u001B[0m"));
+ out.println(stringEscape.unescapeJava("\tCamel Context: " + contextName));
+ out.println(String.format(HEADER_FORMAT, "Id", "Count", "Last (ms)", "Delta (ms)", "Mean (ms)", "Min (ms)", "Max (ms)", "Total (ms)", "Self (ms)"));
+ }
+
+ String xml = camelController.getRouteStatsAsXml(routeId, contextName, true, true);
+ if (xml != null) {
+ RouteStatDump route = (RouteStatDump) unmarshaller.unmarshal(new StringReader(xml));
+
+ long count = route.getExchangesCompleted() + route.getExchangesFailed();
+ out.println(String.format(OUTPUT_FORMAT, route.getId(), count, route.getLastProcessingTime(), route.getDeltaProcessingTime(),
+ route.getMeanProcessingTime(), route.getMinProcessingTime(), route.getMaxProcessingTime(), route.getTotalProcessingTime(), route.getSelfProcessingTime()));
+
+ for (ProcessorStatDump ps : route.getProcessorStats()) {
+ // the self time is the total time of the processor itself
+ long selfTime = ps.getTotalProcessingTime();
+ count = ps.getExchangesCompleted() + ps.getExchangesFailed();
+ // indent route id with 2 spaces
+ out.println(String.format(OUTPUT_FORMAT, " " + ps.getId(), count, ps.getLastProcessingTime(), ps.getDeltaProcessingTime(),
+ ps.getMeanProcessingTime(), ps.getMinProcessingTime(), ps.getMaxProcessingTime(), ps.getAccumulatedProcessingTime(), selfTime));
+ }
+ }
+
+ // we want to group routes from the same context in the same table
+ previousCamelContextName = contextName;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteResetStatsCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteResetStatsCommand.java
new file mode 100644
index 0000000..c59d8f0
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteResetStatsCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to reset route stats.
+ */
+public class RouteResetStatsCommand extends AbstractContextCommand {
+
+ public RouteResetStatsCommand(String context) {
+ super(context);
+ }
+
+ @Override
+ protected Object performContextCommand(CamelController camelController, String contextName, PrintStream out, PrintStream err) throws Exception {
+ camelController.resetRouteStats(contextName);
+ return null;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteResumeCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteResumeCommand.java
new file mode 100644
index 0000000..fc6a240
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteResumeCommand.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.camel.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to resume a route.
+ */
+public class RouteResumeCommand extends AbstractRouteCommand {
+
+ public RouteResumeCommand(String route, String context) {
+ super(route, context);
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ camelController.resumeRoute(contextName, routeId);
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteShowCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteShowCommand.java
new file mode 100644
index 0000000..a04e5c4
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteShowCommand.java
@@ -0,0 +1,39 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to show the route marshaled in XML.
+ */
+public class RouteShowCommand extends AbstractRouteCommand {
+
+ public RouteShowCommand(String route, String context) {
+ super(route, context);
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ String xml = camelController.getRouteModelAsXml(routeId, contextName);
+ if (xml == null) {
+ err.println("Definition of route " + routeId + " not found.");
+ } else {
+ out.println(xml);
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStartCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStartCommand.java
new file mode 100644
index 0000000..bc0bedc
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStartCommand.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.camel.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to start a route.
+ */
+public class RouteStartCommand extends AbstractRouteCommand {
+
+ public RouteStartCommand(String route, String context) {
+ super(route, context);
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ camelController.startRoute(contextName, routeId);
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStepCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStepCommand.java
new file mode 100644
index 0000000..7f6256c
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStepCommand.java
@@ -0,0 +1,83 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.io.StringReader;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.camel.support.dump.RouteStatDump;
+import org.apache.camel.support.dump.StepStatDump;
+
+/**
+ * Command to display step information about a Camel route.
+ */
+public class RouteStepCommand extends AbstractRouteCommand {
+
+ protected static final String HEADER_FORMAT = "%-30s %10s %12s %12s %12s %12s %12s %12s";
+ protected static final String OUTPUT_FORMAT = "%-30s %10d %12d %12d %12d %12d %12d %12d";
+
+ private StringEscape stringEscape;
+ private volatile String previousCamelContextName;
+
+ public RouteStepCommand(String route, String context) {
+ super(route, context);
+ }
+
+ /**
+ * Sets the {@link StringEscape} to use.
+ */
+ public void setStringEscape(StringEscape stringEscape) {
+ this.stringEscape = stringEscape;
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+
+ JAXBContext context = JAXBContext.newInstance(RouteStatDump.class);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ // write new header for new camel context
+ if (previousCamelContextName == null || !previousCamelContextName.equals(contextName)) {
+ out.println("");
+ out.println(stringEscape.unescapeJava("\u001B[1mStep\u001B[0m"));
+ out.println(stringEscape.unescapeJava("\tCamel Context: " + contextName));
+ out.println(String.format(HEADER_FORMAT, "Id", "Count", "Last (ms)", "Delta (ms)", "Mean (ms)", "Min (ms)", "Max (ms)", "Total (ms)"));
+ }
+
+ String xml = camelController.getStepStatsAsXml(routeId, contextName, true);
+ if (xml != null) {
+ RouteStatDump route = (RouteStatDump) unmarshaller.unmarshal(new StringReader(xml));
+
+ long count = route.getExchangesCompleted() + route.getExchangesFailed();
+ out.println(String.format(OUTPUT_FORMAT, route.getId(), count, route.getLastProcessingTime(), route.getDeltaProcessingTime(),
+ route.getMeanProcessingTime(), route.getMinProcessingTime(), route.getMaxProcessingTime(), route.getTotalProcessingTime()));
+
+ for (StepStatDump ss : route.getStepStats()) {
+ count = ss.getExchangesCompleted() + ss.getExchangesFailed();
+ // indent step id with 2 spaces
+ out.println(String.format(OUTPUT_FORMAT, " " + ss.getId(), count, ss.getLastProcessingTime(), ss.getDeltaProcessingTime(),
+ ss.getMeanProcessingTime(), ss.getMinProcessingTime(), ss.getMaxProcessingTime(), ss.getTotalProcessingTime()));
+ }
+ }
+
+ // we want to group routes from the same context in the same table
+ previousCamelContextName = contextName;
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStopCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStopCommand.java
new file mode 100644
index 0000000..30ac5f7
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteStopCommand.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.camel.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to stop a route.
+ */
+public class RouteStopCommand extends AbstractRouteCommand {
+
+ public RouteStopCommand(String route, String context) {
+ super(route, context);
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ camelController.stopRoute(contextName, routeId);
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteSuspendCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteSuspendCommand.java
new file mode 100644
index 0000000..9b6bfc6
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/RouteSuspendCommand.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.camel.commands;
+
+import java.io.PrintStream;
+
+/**
+ * Command to suspend a route.
+ */
+public class RouteSuspendCommand extends AbstractRouteCommand {
+
+ public RouteSuspendCommand(String route, String context) {
+ super(route, context);
+ }
+
+ @Override
+ public void executeOnRoute(CamelController camelController, String contextName, String routeId, PrintStream out, PrintStream err) throws Exception {
+ camelController.suspendRoute(contextName, routeId);
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/StringEscape.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/StringEscape.java
new file mode 100644
index 0000000..693364b
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/StringEscape.java
@@ -0,0 +1,29 @@
+/*
+ * 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.commands;
+
+/**
+ * To escape strings.
+ */
+public interface StringEscape {
+
+ String unescapeJava(String str);
+
+ String escapeJava(String str);
+
+ String hex(char ch);
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/TransformerListCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/TransformerListCommand.java
new file mode 100644
index 0000000..0326819
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/TransformerListCommand.java
@@ -0,0 +1,198 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * List the Camel transformers available in the JVM.
+ */
+public class TransformerListCommand extends AbstractCamelCommand {
+
+ private static final String CONTEXT_NAME_COLUMN_LABEL = "Context";
+ private static final String SCHEME_COLUMN_LABEL = "Scheme";
+ private static final String FROM_COLUMN_LABEL = "From";
+ private static final String TO_COLUMN_LABEL = "To";
+ private static final String STATE_COLUMN_LABEL = "State";
+ private static final String DESCRIPTION_COLUMN_LABEL = "Description";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ // endpoint uris can be very long so clip by default after 120 chars
+ private static final int MAX_COLUMN_WIDTH = 120;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ boolean decode = true;
+ boolean verbose;
+ boolean explain;
+ private final String context;
+
+ public TransformerListCommand(String context, boolean decode, boolean verbose, boolean explain) {
+ this.decode = decode;
+ this.verbose = verbose;
+ this.explain = explain;
+ this.context = context;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ final List<Map<String, String>> camelContextInfos = camelController.getCamelContexts(this.context);
+ final Map<String, List<Map<String, String>>> contextsToTransformers = new HashMap<>();
+
+ for (Map<String, String> camelContextInfo : camelContextInfos) {
+ String camelContextName = camelContextInfo.get("name");
+ final List<Map<String, String>> transformers = camelController.getTransformers(camelContextName);
+ if (transformers.isEmpty()) {
+ continue;
+ }
+ contextsToTransformers.put(camelContextName, transformers);
+ }
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(contextsToTransformers);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ for (Map.Entry<String, List<Map<String, String>>> stringListEntry : contextsToTransformers.entrySet()) {
+ final String camelContextName = stringListEntry.getKey();
+ final List<Map<String, String>> transformers = stringListEntry.getValue();
+
+ if (verbose) {
+ out.println(String.format(headerFormat, CONTEXT_NAME_COLUMN_LABEL, SCHEME_COLUMN_LABEL, FROM_COLUMN_LABEL, TO_COLUMN_LABEL, STATE_COLUMN_LABEL, DESCRIPTION_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "------", "----", "--", "-----", "-----------"));
+ } else {
+ out.println(String.format(headerFormat, CONTEXT_NAME_COLUMN_LABEL, SCHEME_COLUMN_LABEL, FROM_COLUMN_LABEL, TO_COLUMN_LABEL, STATE_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "------", "----", "--", "-----"));
+ }
+ for (Map<String, String> row : transformers) {
+ String scheme = row.get("scheme");
+ String from = row.get("from");
+ String to = row.get("to");
+ String state = row.get("state");
+ if (verbose) {
+ String desc = row.get("description");
+ out.println(String.format(rowFormat, camelContextName, scheme, from, to, state, desc));
+ } else {
+ out.println(String.format(rowFormat, camelContextName, scheme, from, to, state));
+ }
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Integer> computeColumnWidths(final Map<String, List<Map<String, String>>> contextsToTransformers) throws Exception {
+ int maxCamelContextLen = 0;
+ int maxSchemeLen = 0;
+ int maxFromLen = 0;
+ int maxToLen = 0;
+ int maxStatusLen = 0;
+ int maxDescLen = 0;
+
+ for (Map.Entry<String, List<Map<String, String>>> stringListEntry : contextsToTransformers.entrySet()) {
+ final String camelContextName = stringListEntry.getKey();
+
+ maxCamelContextLen = java.lang.Math.max(maxCamelContextLen, camelContextName.length());
+
+ final List<Map<String, String>> transformers = stringListEntry.getValue();
+
+
+ for (Map<String, String> row : transformers) {
+ String scheme = row.get("scheme");
+ maxSchemeLen = java.lang.Math.max(maxSchemeLen, scheme == null ? 0 : scheme.length());
+ String from = row.get("from");
+ maxFromLen = java.lang.Math.max(maxFromLen, from == null ? 0 : from.length());
+ String to = row.get("to");
+ maxToLen = java.lang.Math.max(maxToLen, to == null ? 0 : to.length());
+ String status = row.get("state");
+ maxStatusLen = java.lang.Math.max(maxStatusLen, status == null ? 0 : status.length());
+ if (verbose) {
+ String desc = row.get("description");
+ maxDescLen = java.lang.Math.max(maxDescLen, desc == null ? 0 : desc.length());
+ }
+ }
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>();
+ retval.put(CONTEXT_NAME_COLUMN_LABEL, maxCamelContextLen);
+ retval.put(SCHEME_COLUMN_LABEL, maxSchemeLen);
+ retval.put(FROM_COLUMN_LABEL, maxFromLen);
+ retval.put(TO_COLUMN_LABEL, maxToLen);
+ retval.put(STATE_COLUMN_LABEL, maxStatusLen);
+ if (verbose) {
+ retval.put(DESCRIPTION_COLUMN_LABEL, maxDescLen);
+ }
+
+ return retval;
+ }
+
+ private String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int ctxLen = java.lang.Math.min(columnWidths.get(CONTEXT_NAME_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ ctxLen = Math.max(MIN_COLUMN_WIDTH, ctxLen);
+ int schemeLen = java.lang.Math.min(columnWidths.get(SCHEME_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ schemeLen = Math.max(MIN_COLUMN_WIDTH, schemeLen);
+ int fromLen = java.lang.Math.min(columnWidths.get(FROM_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ fromLen = Math.max(MIN_COLUMN_WIDTH, fromLen);
+ int toLen = java.lang.Math.min(columnWidths.get(TO_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ toLen = Math.max(MIN_COLUMN_WIDTH, toLen);
+ int stateLen = -1;
+ if (verbose) {
+ stateLen = java.lang.Math.min(columnWidths.get(STATE_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ stateLen = Math.max(MIN_COLUMN_WIDTH, stateLen);
+ }
+ // last row does not have min width
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(ctxLen).append('.').append(ctxLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(schemeLen).append('.').append(schemeLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(fromLen).append('.').append(fromLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(toLen).append('.').append(toLen).append('s').append(fieldPostamble).append(' ');
+ if (verbose) {
+ retval.append(fieldPreamble).append("%-").append(stateLen).append('.').append(stateLen).append('s').append(fieldPostamble).append(' ');
+ }
+ retval.append(fieldPreamble).append("%s").append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+ private int getMaxColumnWidth() {
+ if (verbose) {
+ return Integer.MAX_VALUE;
+ } else {
+ return MAX_COLUMN_WIDTH;
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ValidatorListCommand.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ValidatorListCommand.java
new file mode 100644
index 0000000..205d00d
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/ValidatorListCommand.java
@@ -0,0 +1,180 @@
+/*
+ * 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.commands;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * List the Camel validators available in the JVM.
+ */
+public class ValidatorListCommand extends AbstractCamelCommand {
+
+ private static final String CONTEXT_NAME_COLUMN_LABEL = "Context";
+ private static final String TYPE_COLUMN_LABEL = "Type";
+ private static final String STATE_COLUMN_LABEL = "State";
+ private static final String DESCRIPTION_COLUMN_LABEL = "Description";
+
+ private static final int DEFAULT_COLUMN_WIDTH_INCREMENT = 0;
+ private static final String DEFAULT_FIELD_PREAMBLE = " ";
+ private static final String DEFAULT_FIELD_POSTAMBLE = " ";
+ private static final String DEFAULT_HEADER_PREAMBLE = " ";
+ private static final String DEFAULT_HEADER_POSTAMBLE = " ";
+ private static final int DEFAULT_FORMAT_BUFFER_LENGTH = 24;
+ // endpoint uris can be very long so clip by default after 120 chars
+ private static final int MAX_COLUMN_WIDTH = 120;
+ private static final int MIN_COLUMN_WIDTH = 12;
+
+ boolean decode = true;
+ boolean verbose;
+ boolean explain;
+ private final String context;
+
+ public ValidatorListCommand(String context, boolean decode, boolean verbose, boolean explain) {
+ this.decode = decode;
+ this.verbose = verbose;
+ this.explain = explain;
+ this.context = context;
+ }
+
+ @Override
+ public Object execute(CamelController camelController, PrintStream out, PrintStream err) throws Exception {
+ final List<Map<String, String>> camelContextInfos = camelController.getCamelContexts(this.context);
+ final Map<String, List<Map<String, String>>> contextsToValidators = new HashMap<>();
+
+ for (Map<String, String> camelContextInfo : camelContextInfos) {
+ String camelContextName = camelContextInfo.get("name");
+ final List<Map<String, String>> validators = camelController.getValidators(camelContextName);
+ if (validators.isEmpty()) {
+ continue;
+ }
+ contextsToValidators.put(camelContextName, validators);
+ }
+
+ final Map<String, Integer> columnWidths = computeColumnWidths(contextsToValidators);
+ final String headerFormat = buildFormatString(columnWidths, true);
+ final String rowFormat = buildFormatString(columnWidths, false);
+
+ for (Map.Entry<String, List<Map<String, String>>> stringListEntry : contextsToValidators.entrySet()) {
+ final String camelContextName = stringListEntry.getKey();
+ final List<Map<String, String>> validators = stringListEntry.getValue();
+
+ if (verbose) {
+ out.println(String.format(headerFormat, CONTEXT_NAME_COLUMN_LABEL, TYPE_COLUMN_LABEL, STATE_COLUMN_LABEL, DESCRIPTION_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "----", "-----", "-----------"));
+ } else {
+ out.println(String.format(headerFormat, CONTEXT_NAME_COLUMN_LABEL, TYPE_COLUMN_LABEL, STATE_COLUMN_LABEL));
+ out.println(String.format(headerFormat, "-------", "----", "-----"));
+ }
+ for (Map<String, String> row : validators) {
+ String type = row.get("type");
+ String state = row.get("state");
+ if (verbose) {
+ String desc = row.get("description");
+ out.println(String.format(rowFormat, camelContextName, type, state, desc));
+ } else {
+ out.println(String.format(rowFormat, camelContextName, type, state));
+ }
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Integer> computeColumnWidths(final Map<String, List<Map<String, String>>> contextsToValidators) throws Exception {
+ int maxCamelContextLen = 0;
+ int maxTypeLen = 0;
+ int maxStatusLen = 0;
+ int maxDescLen = 0;
+
+ for (Map.Entry<String, List<Map<String, String>>> stringListEntry : contextsToValidators.entrySet()) {
+ final String camelContextName = stringListEntry.getKey();
+
+ maxCamelContextLen = java.lang.Math.max(maxCamelContextLen, camelContextName.length());
+
+ final List<Map<String, String>> validators = stringListEntry.getValue();
+
+
+ for (Map<String, String> row : validators) {
+ String type = row.get("type");
+ maxTypeLen = java.lang.Math.max(maxTypeLen, type == null ? 0 : type.length());
+ String status = row.get("state");
+ maxStatusLen = java.lang.Math.max(maxStatusLen, status == null ? 0 : status.length());
+ if (verbose) {
+ String desc = row.get("description");
+ maxDescLen = java.lang.Math.max(maxDescLen, desc == null ? 0 : desc.length());
+ }
+ }
+ }
+
+ final Map<String, Integer> retval = new Hashtable<>();
+ retval.put(CONTEXT_NAME_COLUMN_LABEL, maxCamelContextLen);
+ retval.put(TYPE_COLUMN_LABEL, maxTypeLen);
+ retval.put(STATE_COLUMN_LABEL, maxStatusLen);
+ if (verbose) {
+ retval.put(DESCRIPTION_COLUMN_LABEL, maxDescLen);
+ }
+
+ return retval;
+ }
+
+ private String buildFormatString(final Map<String, Integer> columnWidths, final boolean isHeader) {
+ final String fieldPreamble;
+ final String fieldPostamble;
+ final int columnWidthIncrement;
+
+ if (isHeader) {
+ fieldPreamble = DEFAULT_HEADER_PREAMBLE;
+ fieldPostamble = DEFAULT_HEADER_POSTAMBLE;
+ } else {
+ fieldPreamble = DEFAULT_FIELD_PREAMBLE;
+ fieldPostamble = DEFAULT_FIELD_POSTAMBLE;
+ }
+ columnWidthIncrement = DEFAULT_COLUMN_WIDTH_INCREMENT;
+
+ int ctxLen = java.lang.Math.min(columnWidths.get(CONTEXT_NAME_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ ctxLen = Math.max(MIN_COLUMN_WIDTH, ctxLen);
+ int typeLen = java.lang.Math.min(columnWidths.get(TYPE_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ typeLen = Math.max(MIN_COLUMN_WIDTH, typeLen);
+ int stateLen = -1;
+ if (verbose) {
+ stateLen = java.lang.Math.min(columnWidths.get(STATE_COLUMN_LABEL) + columnWidthIncrement, getMaxColumnWidth());
+ stateLen = Math.max(MIN_COLUMN_WIDTH, stateLen);
+ }
+ // last row does not have min width
+
+ final StringBuilder retval = new StringBuilder(DEFAULT_FORMAT_BUFFER_LENGTH);
+ retval.append(fieldPreamble).append("%-").append(ctxLen).append('.').append(ctxLen).append('s').append(fieldPostamble).append(' ');
+ retval.append(fieldPreamble).append("%-").append(typeLen).append('.').append(typeLen).append('s').append(fieldPostamble).append(' ');
+ if (verbose) {
+ retval.append(fieldPreamble).append("%-").append(stateLen).append('.').append(stateLen).append('s').append(fieldPostamble).append(' ');
+ }
+ retval.append(fieldPreamble).append("%s").append(fieldPostamble).append(' ');
+
+ return retval.toString();
+ }
+
+ private int getMaxColumnWidth() {
+ if (verbose) {
+ return Integer.MAX_VALUE;
+ } else {
+ return MAX_COLUMN_WIDTH;
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/internal/MatchUtil.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/internal/MatchUtil.java
new file mode 100644
index 0000000..aa03d4b
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/internal/MatchUtil.java
@@ -0,0 +1,31 @@
+/*
+ * 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.commands.internal;
+
+/**
+ * Util class.
+ */
+public final class MatchUtil {
+
+ private MatchUtil() {
+ }
+
+ public static boolean matchWildcard(String name, String pattern) {
+ return pattern.endsWith("*") && name.startsWith(pattern.substring(0, pattern.length() - 1));
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/internal/RegexUtil.java b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/internal/RegexUtil.java
new file mode 100644
index 0000000..9fe5d7a
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/java/org/apache/camel/commands/internal/RegexUtil.java
@@ -0,0 +1,69 @@
+/*
+ * 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.commands.internal;
+
+/**
+ * Util class.
+ */
+public final class RegexUtil {
+
+ private RegexUtil() {
+ }
+
+ /**
+ * convert a wild card containing * and ? to the equivalent regex
+ *
+ * @param wildcard wildcard string describing a file.
+ * @return regex string that could be fed to Pattern.compile
+ */
+ public static String wildcardAsRegex(String wildcard) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < wildcard.length(); i++) {
+ final char c = wildcard.charAt(i);
+ switch (c) {
+ case '*':
+ sb.append(".*?");
+ break;
+ case '?':
+ sb.append(".");
+ break;
+ // chars that have magic regex meaning. They need quoting to be taken literally
+ case '$':
+ case '(':
+ case ')':
+ case '+':
+ case '-':
+ case '.':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '{':
+ case '|':
+ case '}':
+ sb.append('\\');
+ sb.append(c);
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/main/resources/META-INF/LICENSE.txt b/platforms/commands/commands-core/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/resources/META-INF/LICENSE.txt
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
diff --git a/platforms/commands/commands-core/src/main/resources/META-INF/NOTICE.txt b/platforms/commands/commands-core/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 0000000..2e215bf
--- /dev/null
+++ b/platforms/commands/commands-core/src/main/resources/META-INF/NOTICE.txt
@@ -0,0 +1,11 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Apache Camel distribution. ==
+ =========================================================================
+
+ This product includes software developed by
+ The Apache Software Foundation (http://www.apache.org/).
+
+ Please read the different LICENSE files present in the licenses directory of
+ this distribution.
diff --git a/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/AbstractLocalCamelControllerTest.java b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/AbstractLocalCamelControllerTest.java
new file mode 100644
index 0000000..4053c35
--- /dev/null
+++ b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/AbstractLocalCamelControllerTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.commands;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Message;
+import org.apache.camel.ValidationException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.engine.ExplicitCamelContextNameStrategy;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.spi.Validator;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class AbstractLocalCamelControllerTest {
+
+ private final DummyCamelController localCamelController;
+
+ private final CamelContext context;
+
+ public AbstractLocalCamelControllerTest() throws Exception {
+ context = new DefaultCamelContext();
+ context.setNameStrategy(new ExplicitCamelContextNameStrategy("context1"));
+ context.getInflightRepository().setInflightBrowseEnabled(true);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ transformer()
+ .fromType("xml:foo")
+ .toType("json:bar")
+ .withJava(DummyTransformer.class);
+ validator()
+ .type("xml:foo")
+ .withJava(DummyValidator.class);
+ from("direct:start1").id("route1").delay(100).to("mock:result1");
+ from("direct:start2").id("route2").delay(100).to("mock:result2");
+ from("direct:start3").id("route3").delay(100).to("mock:result3");
+ }
+ });
+ localCamelController = new DummyCamelController(context);
+ }
+
+ @Before
+ public void startContext() throws Exception {
+ context.start();
+ }
+
+ @After
+ public void stopContext() throws Exception {
+ context.stop();
+ }
+
+ @Test
+ public void testBrowseInflightExchangesWithMoreRoutes() throws Exception {
+ context.createProducerTemplate().asyncSendBody("direct:start1", "Start one");
+ context.createProducerTemplate().asyncSendBody("direct:start2", "Start two");
+ context.createProducerTemplate().asyncSendBody("direct:start3", "Start three");
+
+ // let the exchange proceed
+ Thread.sleep(50);
+
+ final List<Map<String, Object>> inflightExchanges = localCamelController.browseInflightExchanges("context1", null, 0, false);
+
+ assertEquals("Context should contain three inflight exchanges", 3, inflightExchanges.size());
+ }
+
+ @Test
+ public void testBrowseInflightExchangesWithNoRoutes() throws Exception {
+ final List<Map<String, Object>> inflightExchanges = localCamelController.browseInflightExchanges("context1", null, 0, false);
+
+ assertTrue("Context without routes should not have any inflight exchanges", inflightExchanges.isEmpty());
+ }
+
+ @Test
+ public void testBrowseInflightExchangesWithOneRoute() throws Exception {
+ context.createProducerTemplate().asyncSendBody("direct:start1", "Start one");
+
+ // let the exchange proceed
+ Thread.sleep(50);
+
+ final List<Map<String, Object>> inflightExchanges = localCamelController.browseInflightExchanges("context1", null, 0, false);
+
+ assertEquals("Context should contain one inflight exchange", 1, inflightExchanges.size());
+ }
+
+ @Test
+ public void testBrowseInflightExchangesWithSpecificRoute() throws Exception {
+ context.createProducerTemplate().asyncSendBody("direct:start1", "Start one");
+ context.createProducerTemplate().asyncSendBody("direct:start2", "Start two");
+ context.createProducerTemplate().asyncSendBody("direct:start3", "Start three");
+
+ // let the exchanges proceed
+ Thread.sleep(50);
+
+ final List<Map<String, Object>> inflightExchanges = localCamelController.browseInflightExchanges("context1", "route2", 0, false);
+
+ assertEquals("Context should contain one inflight exchange for specific route", 1, inflightExchanges.size());
+ }
+
+ @Test
+ public void testTransformer() throws Exception {
+ List<Map<String, String>> transformers = localCamelController.getTransformers("context1");
+ assertEquals(1, transformers.size());
+ Map<String, String> dummyTransformer = transformers.get(0);
+ assertEquals("context1", dummyTransformer.get("camelContextName"));
+ assertEquals("DummyTransformer[scheme='null', from='xml:foo', to='json:bar']", dummyTransformer.get("description"));
+ assertEquals(null, dummyTransformer.get("scheme"));
+ assertEquals("xml:foo", dummyTransformer.get("from"));
+ assertEquals("json:bar", dummyTransformer.get("to"));
+ assertEquals("Started", dummyTransformer.get("state"));
+ }
+
+ @Test
+ public void testValidator() throws Exception {
+ List<Map<String, String>> validators = localCamelController.getValidators("context1");
+ assertEquals(1, validators.size());
+ Map<String, String> dummyValidator = validators.get(0);
+ assertEquals("context1", dummyValidator.get("camelContextName"));
+ assertEquals("DummyValidator[type='xml:foo']", dummyValidator.get("description"));
+ assertEquals("xml:foo", dummyValidator.get("type"));
+ assertEquals("Started", dummyValidator.get("state"));
+ }
+
+ public static class DummyTransformer extends Transformer {
+ @Override
+ public void transform(Message message, DataType from, DataType to) throws Exception {
+ }
+ }
+
+ public static class DummyValidator extends Validator {
+ @Override
+ public void validate(Message message, DataType type) throws ValidationException {
+ }
+ }
+}
diff --git a/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/ContextListCommandTest.java b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/ContextListCommandTest.java
new file mode 100644
index 0000000..08a175c
--- /dev/null
+++ b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/ContextListCommandTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.commands;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.engine.DefaultRuntimeEndpointRegistry;
+import org.apache.camel.impl.engine.ExplicitCamelContextNameStrategy;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class ContextListCommandTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ContextListCommandTest.class);
+
+ @Test
+ public void testContextList() throws Exception {
+ CamelContext context = new DefaultCamelContext();
+ context.setNameStrategy(new ExplicitCamelContextNameStrategy("foobar"));
+ context.start();
+
+ CamelController controller = new DummyCamelController(context);
+
+ OutputStream os = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(os);
+
+ ContextListCommand command = new ContextListCommand();
+ command.execute(controller, ps, null);
+
+ String out = os.toString();
+ assertNotNull(out);
+ LOG.info("\n\n{}\n", out);
+
+ // should contain a table with the context
+ assertTrue(out.contains("foobar"));
+ assertTrue(out.contains("Started"));
+
+ context.stop();
+ }
+
+ @Test
+ public void testEndpointStats() throws Exception {
+ CamelContext context = new DefaultCamelContext();
+ context.setRuntimeEndpointRegistry(new DefaultRuntimeEndpointRegistry());
+ context.setNameStrategy(new ExplicitCamelContextNameStrategy("foobar"));
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start").to("mock:result");
+ }
+ });
+ context.start();
+
+ context.createProducerTemplate().sendBody("direct:start", "Hello World");
+
+ CamelController controller = new DummyCamelController(context);
+
+ OutputStream os = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(os);
+
+ EndpointStatisticCommand command = new EndpointStatisticCommand("foobar", false, null);
+ command.execute(controller, ps, null);
+
+ String out = os.toString();
+ assertNotNull(out);
+ LOG.info("\n\n{}\n", out);
+
+ assertTrue(out.contains("direct://start"));
+ assertTrue(out.contains("mock://result"));
+
+ context.stop();
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/DummyCamelController.java b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/DummyCamelController.java
new file mode 100644
index 0000000..e53e88c
--- /dev/null
+++ b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/DummyCamelController.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.camel.commands;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+
+public class DummyCamelController extends AbstractLocalCamelController {
+
+ private CamelContext camelContext;
+
+ public DummyCamelController(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public List<CamelContext> getLocalCamelContexts() {
+ List<CamelContext> answer = new ArrayList<>(1);
+ answer.add(camelContext);
+ return answer;
+ }
+
+ @Override
+ public List<Map<String, String>> getCamelContexts() throws Exception {
+ List<Map<String, String>> answer = new ArrayList<>(1);
+ Map<String, String> row = new LinkedHashMap<>();
+ row.put("name", camelContext.getName());
+ row.put("state", camelContext.getStatus().name());
+ row.put("uptime", camelContext.getUptime());
+ answer.add(row);
+ return answer;
+ }
+}
diff --git a/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/ValidatorListCommandTest.java b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/ValidatorListCommandTest.java
new file mode 100644
index 0000000..dd4ceff
--- /dev/null
+++ b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/ValidatorListCommandTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.commands;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Message;
+import org.apache.camel.ValidationException;
+import org.apache.camel.builder.ExpressionBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.engine.ExplicitCamelContextNameStrategy;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.model.validator.CustomValidatorDefinition;
+import org.apache.camel.model.validator.EndpointValidatorDefinition;
+import org.apache.camel.model.validator.PredicateValidatorDefinition;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.Validator;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class ValidatorListCommandTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ValidatorListCommandTest.class);
+
+ @Test
+ public void testValidatorList() throws Exception {
+ String out = doTest(false);
+ assertTrue(out.contains("xml:foo"));
+ assertTrue(out.contains("java:" + this.getClass().getName()));
+ assertTrue(out.contains("custom"));
+ assertTrue(out.contains("Started"));
+ assertFalse(out.contains("ProcessorValidator["));
+ assertFalse(out.contains("processor='validate(body)'"));
+ assertFalse(out.contains("processor='sendTo(direct://validator)'"));
+ assertFalse(out.contains("MyValidator["));
+ }
+
+ @Test
+ public void testValidatorListVerbose() throws Exception {
+ String out = doTest(true);
+ assertTrue(out.contains("xml:foo"));
+ assertTrue(out.contains("java:" + this.getClass().getName()));
+ assertTrue(out.contains("custom"));
+ assertTrue(out.contains("Started"));
+ assertTrue(out.contains("ProcessorValidator["));
+ assertTrue(out.contains("processor='validate(body)'"));
+ //assertTrue(out.contains("processor='sendTo(direct://validator)'"));
+ assertTrue(out.contains("MyValidator["));
+ }
+
+ private String doTest(boolean verbose) throws Exception {
+ CamelContext context = new DefaultCamelContext();
+ EndpointValidatorDefinition evd = new EndpointValidatorDefinition();
+ evd.setType("xml:foo");
+ evd.setUri("direct:validator");
+ context.getExtension(Model.class).getValidators().add(evd);
+ PredicateValidatorDefinition pvd = new PredicateValidatorDefinition();
+ pvd.setType(this.getClass());
+ pvd.setExpression(new ExpressionDefinition(ExpressionBuilder.bodyExpression()));
+ context.getExtension(Model.class).getValidators().add(pvd);
+ CustomValidatorDefinition cvd = new CustomValidatorDefinition();
+ cvd.setType("custom");
+ cvd.setClassName(MyValidator.class.getName());
+ context.getExtension(Model.class).getValidators().add(cvd);
+ context.setNameStrategy(new ExplicitCamelContextNameStrategy("foobar"));
+ context.start();
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:foo")
+ .to("mock:foo");
+ }
+ });
+
+ CamelController controller = new DummyCamelController(context);
+
+ OutputStream os = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(os);
+
+ ValidatorListCommand command = new ValidatorListCommand(null, false, verbose, false);
+ command.execute(controller, ps, null);
+
+ String out = os.toString();
+ assertNotNull(out);
+ LOG.info("\n\n{}\n", out);
+
+ context.stop();
+ return out;
+ }
+
+ public static class MyValidator extends Validator {
+ @Override
+ public void validate(Message message, DataType type) throws ValidationException {
+ return;
+ }
+ }
+}
+
diff --git a/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/internal/RegexUtilTest.java b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/internal/RegexUtilTest.java
new file mode 100644
index 0000000..26916f5
--- /dev/null
+++ b/platforms/commands/commands-core/src/test/java/org/apache/camel/commands/internal/RegexUtilTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.commands.internal;
+
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class RegexUtilTest {
+
+ @Test
+ public void testWildcardAsRegex() throws Exception {
+ String testRouteId1 = "route.inbound.systema";
+ String testRouteId2 = "route.inbound.systemb";
+ String testRouteId3 = "route.outbound.systema";
+ String testRouteId4 = "route.outbound.systemb";
+ String testRouteId5 = "outbound.systemc";
+
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("route.inbound*"), testRouteId1));
+ assertTrue(!Pattern.matches(RegexUtil.wildcardAsRegex(".inbound*"), testRouteId2));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*.inbound*"), testRouteId2));
+
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*outbound*"), testRouteId3));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*outbound*"), testRouteId4));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*outbound*"), testRouteId5));
+
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*"), testRouteId1));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*"), testRouteId2));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*"), testRouteId3));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*"), testRouteId4));
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("*"), testRouteId5));
+
+ assertTrue(Pattern.matches(RegexUtil.wildcardAsRegex("route.inbound.systema"), testRouteId1));
+ }
+
+}
diff --git a/platforms/commands/commands-core/src/test/resources/log4j2.properties b/platforms/commands/commands-core/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..03a7c00
--- /dev/null
+++ b/platforms/commands/commands-core/src/test/resources/log4j2.properties
@@ -0,0 +1,30 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-commands-core-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+logger.commands.name = org.apache.camel.commands
+logger.commands.level = DEBUG
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/platforms/commands/pom.xml b/platforms/commands/pom.xml
new file mode 100644
index 0000000..f590784
--- /dev/null
+++ b/platforms/commands/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel.karaf</groupId>
+ <artifactId>platforms</artifactId>
+ <version>3.2.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>commands</artifactId>
+ <packaging>pom</packaging>
+ <name>Camel Karaf :: Platforms :: Commands</name>
+
+ <modules>
+ <module>commands-core</module>
+ </modules>
+
+</project>
diff --git a/platforms/pom.xml b/platforms/pom.xml
index 8280cfd..9949b14 100644
--- a/platforms/pom.xml
+++ b/platforms/pom.xml
@@ -31,6 +31,7 @@
<name>Camel Karaf :: Platforms </name>
<modules>
+ <module>commands</module>
<module>karaf</module>
</modules>