You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gg...@apache.org on 2019/01/11 09:51:08 UTC
[camel] branch master updated: [CAMEL-12980] Interact with Karaf's
BundleStateService about Blueprint Camel Context problems
This is an automated email from the ASF dual-hosted git repository.
ggrzybek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new eb855a1 [CAMEL-12980] Interact with Karaf's BundleStateService about Blueprint Camel Context problems
eb855a1 is described below
commit eb855a14c7aa35b98118de965d939a749d1783db
Author: Grzegorz Grzybek <gr...@gmail.com>
AuthorDate: Fri Jan 11 10:43:53 2019 +0100
[CAMEL-12980] Interact with Karaf's BundleStateService about Blueprint Camel Context problems
(cherry picked from commit aadb0f83c6502219b058510487b1106508fd29ae)
---
components/camel-blueprint/pom.xml | 9 ++
.../camel/blueprint/BlueprintCamelContext.java | 14 ++
.../blueprint/BlueprintCamelStateService.java | 164 +++++++++++++++++++++
.../camel/blueprint/KarafBundleStateService.java | 95 ++++++++++++
.../blueprint/handler/CamelNamespaceHandler.java | 26 ++++
5 files changed, 308 insertions(+)
diff --git a/components/camel-blueprint/pom.xml b/components/camel-blueprint/pom.xml
index 3e67c96..870e851 100644
--- a/components/camel-blueprint/pom.xml
+++ b/components/camel-blueprint/pom.xml
@@ -44,6 +44,7 @@
!org.apache.camel.core.xml.*,
org.apache.camel.*;${camel.osgi.import.strict.version},
org.osgi.service.event*;resolution:=optional,
+ org.apache.karaf.bundle.core;version="[4,5)";resolution:=optional,
org.apache.aries*;version="[1.0,2)",
${camel.osgi.import.defaults},
*
@@ -93,6 +94,14 @@
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.bundle</groupId>
+ <artifactId>org.apache.karaf.bundle.core</artifactId>
+ <version>${karaf4-version}</version>
+ <optional>true</optional>
+ <scope>provided</scope>
+ </dependency>
+
<!-- for testing -->
<dependency>
<groupId>org.apache.camel</groupId>
diff --git a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelContext.java b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelContext.java
index 7030c90..defe8eb 100644
--- a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelContext.java
+++ b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelContext.java
@@ -54,6 +54,8 @@ public class BlueprintCamelContext extends DefaultCamelContext implements Servic
private BlueprintContainer blueprintContainer;
private ServiceRegistration<?> registration;
+ private BlueprintCamelStateService bundleStateService;
+
public BlueprintCamelContext(BundleContext bundleContext, BlueprintContainer blueprintContainer) {
super(false);
this.bundleContext = bundleContext;
@@ -91,6 +93,14 @@ public class BlueprintCamelContext extends DefaultCamelContext implements Servic
public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
this.blueprintContainer = blueprintContainer;
}
+
+ public BlueprintCamelStateService getBundleStateService() {
+ return bundleStateService;
+ }
+
+ public void setBundleStateService(BlueprintCamelStateService bundleStateService) {
+ this.bundleStateService = bundleStateService;
+ }
public void doInit() {
log.trace("init {}", this);
@@ -121,6 +131,7 @@ public class BlueprintCamelContext extends DefaultCamelContext implements Servic
}
registration = null;
}
+ bundleStateService.setBundleState(bundleContext.getBundle(), this.getName(), null);
// must stop Camel
stop();
@@ -236,8 +247,11 @@ public class BlueprintCamelContext extends DefaultCamelContext implements Servic
try {
// let's set a more suitable TCCL while starting the context
Thread.currentThread().setContextClassLoader(getApplicationContextClassLoader());
+ bundleStateService.setBundleState(bundleContext.getBundle(), this.getName(), BlueprintCamelStateService.State.Starting);
super.start();
+ bundleStateService.setBundleState(bundleContext.getBundle(), this.getName(), BlueprintCamelStateService.State.Active);
} catch (Exception e) {
+ bundleStateService.setBundleState(bundleContext.getBundle(), this.getName(), BlueprintCamelStateService.State.Failure, e);
routeDefinitionValid.set(false);
throw e;
} finally {
diff --git a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelStateService.java b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelStateService.java
new file mode 100644
index 0000000..1df75cc
--- /dev/null
+++ b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/BlueprintCamelStateService.java
@@ -0,0 +1,164 @@
+/**
+ * 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.blueprint;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Used by {@link BlueprintCamelContext} to inform about state of Camel context. If running inside Karaf
+ * and Karaf's BundleStateService is accessible, Camel context state will propagate as <em>extended
+ * bundle state</em>.
+ */
+public class BlueprintCamelStateService {
+
+ public static final Logger LOG = LoggerFactory.getLogger(BlueprintCamelStateService.class);
+
+ public enum State {
+ Starting,
+ Active,
+ Failure
+ }
+
+ private Map<String, State> states;
+ private Map<String, Throwable> exceptions;
+
+ private BundleContext bundleContext;
+
+ private ServiceRegistration<?> registration;
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ /**
+ * One of four {@link State states} is set for given {@link org.osgi.framework.Bundle} and context Id.
+ * One (blueprint) bundle may declare one or more Camel context.
+ * @param contextId
+ * @param state
+ */
+ public void setBundleState(Bundle bundle, String contextId, State state) {
+ setBundleState(bundle, contextId, state, null);
+ }
+
+ /**
+ * One of four {@link State states} is set for given {@link org.osgi.framework.Bundle} and context Id.
+ * One (blueprint) bundle may declare one or more Camel context.
+ * @param contextId
+ * @param state
+ * @param t
+ */
+ public void setBundleState(Bundle bundle, String contextId, State state, Throwable t) {
+ if (state == State.Failure) {
+ LOG.warn("Changing Camel state for bundle {} to {}", bundle.getBundleId(), state);
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug("Changing Camel state for bundle {} to {}", bundle.getBundleId(), state);
+ }
+
+ String key = String.format("%d:%s", bundle.getBundleId(), contextId);
+ if (state != null) {
+ states.put(key, state);
+ } else {
+ states.remove(key);
+ }
+ if (t != null) {
+ exceptions.put(key, t);
+ } else {
+ exceptions.remove(key);
+ }
+ }
+
+ /**
+ * Get states for all context registered for given {@link Bundle}
+ * @param bundle
+ * @return
+ */
+ public List<State> getStates(Bundle bundle) {
+ List<State> result = new LinkedList<>();
+ for (Map.Entry<String, State> e : states.entrySet()) {
+ if (e.getKey().startsWith(bundle.getBundleId() + ":")) {
+ result.add(e.getValue());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get exceptions for all camel contexts for given bundle
+ * @param bundle
+ * @return
+ */
+ public Map<String, Throwable> getExceptions(Bundle bundle) {
+ Map<String, Throwable> result = new LinkedHashMap<>();
+ for (Map.Entry<String, Throwable> e : exceptions.entrySet()) {
+ if (e.getKey().startsWith(bundle.getBundleId() + ":")) {
+ result.put(e.getKey().substring(e.getKey().indexOf(":") + 1), e.getValue());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Attempts to register Karaf-specific BundleStateService - if possible
+ */
+ public void init() {
+ try {
+ states = new ConcurrentHashMap<>();
+ exceptions = new ConcurrentHashMap<>();
+
+ registration = new KarafBundleStateServiceCreator().create(bundleContext, this);
+ } catch (NoClassDefFoundError e) {
+ LOG.info("Karaf BundleStateService not accessible. Bundle state won't reflect Camel context state");
+ }
+ }
+
+ /**
+ * Unregisters any OSGi service registered
+ */
+ public void destroy() {
+ if (registration != null) {
+ registration.unregister();
+ }
+ states.clear();
+ states = null;
+ exceptions.clear();
+ exceptions = null;
+ }
+
+ /**
+ * Static creator to decouple from optional Karaf classes.
+ */
+ private static class KarafBundleStateServiceCreator {
+ public ServiceRegistration<?> create(BundleContext context, BlueprintCamelStateService camelStateService) {
+ KarafBundleStateService karafBundleStateService = new KarafBundleStateService(camelStateService);
+ return karafBundleStateService.register(context);
+ }
+ }
+
+}
diff --git a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/KarafBundleStateService.java b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/KarafBundleStateService.java
new file mode 100644
index 0000000..6e84278
--- /dev/null
+++ b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/KarafBundleStateService.java
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.blueprint;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Map;
+
+import org.apache.karaf.bundle.core.BundleState;
+import org.apache.karaf.bundle.core.BundleStateService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * A service for Karaf to get extended Bundle information related to Camel Context(s) declared in Blueprint
+ * container.
+ */
+public class KarafBundleStateService implements BundleStateService {
+
+ BlueprintCamelStateService camelStateService;
+
+ public KarafBundleStateService(BlueprintCamelStateService camelStateService) {
+ this.camelStateService = camelStateService;
+ }
+
+ @Override
+ public String getName() {
+ return "Camel Blueprint";
+ }
+
+ @Override
+ public String getDiag(Bundle bundle) {
+ if (getState(bundle) == BundleState.Failure) {
+ // return stacktraces for failed camel contexts
+ Map<String, Throwable> exceptions = camelStateService.getExceptions(bundle);
+ StringWriter sw = new StringWriter();
+ for (String contextId : exceptions.keySet()) {
+ sw.append("Camel context \"").append(contextId).append("\"\n");
+ Throwable t = exceptions.get(contextId);
+ if (t instanceof NullPointerException) {
+ sw.append("Exception: NullPointerException\n");
+ } else if (t.getMessage() != null) {
+ sw.append("Exception: ").append(t.getMessage()).append("\n");
+ }
+ t.printStackTrace(new PrintWriter(sw));
+ sw.append("\n");
+ }
+ return sw.toString();
+ }
+ return null;
+ }
+
+ @Override
+ public BundleState getState(Bundle bundle) {
+ BundleState effective = BundleState.Unknown;
+ for (BlueprintCamelStateService.State s : camelStateService.getStates(bundle)) {
+ if (effective == BundleState.Unknown || s == BlueprintCamelStateService.State.Failure) {
+ switch (s) {
+ case Starting:
+ effective = BundleState.Starting;
+ break;
+ case Active:
+ effective = BundleState.Active;
+ break;
+ case Failure:
+ effective = BundleState.Failure;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return effective;
+ }
+
+ public ServiceRegistration<?> register(BundleContext context) {
+ return context.registerService(BundleStateService.class, this, null);
+ }
+
+}
diff --git a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/handler/CamelNamespaceHandler.java b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/handler/CamelNamespaceHandler.java
index 2aefba3..6804828 100644
--- a/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/handler/CamelNamespaceHandler.java
+++ b/components/camel-blueprint/src/main/java/org/apache/camel/blueprint/handler/CamelNamespaceHandler.java
@@ -57,6 +57,7 @@ import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.PropertyInject;
import org.apache.camel.blueprint.BlueprintCamelContext;
+import org.apache.camel.blueprint.BlueprintCamelStateService;
import org.apache.camel.blueprint.BlueprintModelJAXBContextFactory;
import org.apache.camel.blueprint.CamelContextFactoryBean;
import org.apache.camel.blueprint.CamelEndpointFactoryBean;
@@ -281,6 +282,7 @@ public class CamelNamespaceHandler implements NamespaceHandler {
ctx.setRuntimeClass(BlueprintCamelContext.class);
ctx.setFactoryComponent(factory2);
ctx.setFactoryMethod("getContext");
+ ctx.addProperty("bundleStateService", createRef(context, ".camelBlueprint.bundleStateService"));
ctx.setInitMethod("init");
ctx.setDestroyMethod("destroy");
@@ -290,6 +292,9 @@ public class CamelNamespaceHandler implements NamespaceHandler {
registerBeans(context, contextId, ccfb.getRedeliveryPolicies());
registerBeans(context, contextId, ccfb.getBeansFactory());
+ // Register single CamelBundleStateService - shared for all bundles and all Blueprint Camel contexts
+ registerBundleStateService(context);
+
// Register processors
MutablePassThroughMetadata beanProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
beanProcessorFactory.setId(".camelBlueprint.processor.bean.passThrough." + contextId);
@@ -630,6 +635,27 @@ public class CamelNamespaceHandler implements NamespaceHandler {
context.getComponentDefinitionRegistry().registerComponentDefinition(e);
}
+ /**
+ * There's single instance of {@link BlueprintCamelStateService} that's used by all Blueprint Camel contexts
+ * to inform about state of Camel contexts. If Karaf is available, this information will propagate to
+ * <em>extended bundle info</em>.
+ * See CAMEL-12980
+ * @param context
+ */
+ private void registerBundleStateService(ParserContext context) {
+ ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
+ ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.bundleStateService");
+ if (cm == null) {
+ MutableBeanMetadata ssm = context.createMetadata(MutableBeanMetadata.class);
+ ssm.setId(".camelBlueprint.bundleStateService");
+ ssm.setRuntimeClass(BlueprintCamelStateService.class);
+ ssm.addProperty("bundleContext", createRef(context, "blueprintBundleContext"));
+ ssm.setInitMethod("init");
+ ssm.setDestroyMethod("destroy");
+ componentDefinitionRegistry.registerComponentDefinition(ssm);
+ }
+ }
+
protected BlueprintContainer getBlueprintContainer(ParserContext context) {
PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintContainer");
return (BlueprintContainer) ptm.getObject();