Experimental camel plugin which allows tracking camel flows. Example configuration attached. Because problems with dynamic interception strategies webconsole intercept strategy must be imported before context start

Signed-off-by: Lukasz Dywicki <luke@code-house.org>

git-svn-id: https://svn.apache.org/repos/asf/karaf/sandbox/webconsole/trunk@1165854 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/camel/pom.xml b/camel/pom.xml
new file mode 100644
index 0000000..7fb3ecb
--- /dev/null
+++ b/camel/pom.xml
@@ -0,0 +1,76 @@
+<?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.karaf</groupId>
+        <artifactId>webconsole</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.karaf.webconsole</groupId>
+    <artifactId>org.apache.karaf.webconsole.camel</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Karaf Webconsole Prototype :: Camel extension</name>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.karaf.webconsole</groupId>
+            <artifactId>org.apache.karaf.webconsole.core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core</artifactId>
+            <version>${camel.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.3.5</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Import-Package>
+                            !org.apache.karaf.webconsole.camel*,
+                            *,
+                            <!-- transient dependencies -->
+                            org.ops4j.pax.wicket.api,
+                            org.ops4j.pax.wicket.util,
+                            org.ops4j.pax.wicket.util.proxy,
+                            <!-- camel dependencies -->
+                            org.apache.camel;version="[2.6,3)",
+                            org.apache.camel.model;version="[2.6,3)",
+                            org.apache.camel.spi;version="[2.6,3)",
+                            org.apache.camel.processor;version="[2.6,3)"
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/CamelPage.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/CamelPage.java
new file mode 100644
index 0000000..dcd11d3
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/CamelPage.java
@@ -0,0 +1,7 @@
+package org.apache.karaf.webconsole.camel.internal;
+
+import org.apache.karaf.webconsole.core.page.SinglePage;
+
+public class CamelPage extends SinglePage {
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/CamelContextModel.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/CamelContextModel.java
new file mode 100644
index 0000000..c3e8d0c
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/CamelContextModel.java
@@ -0,0 +1,30 @@
+package org.apache.karaf.webconsole.camel.internal.context;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.wicket.model.LoadableDetachableModel;
+
+public class CamelContextModel extends LoadableDetachableModel<CamelContext> {
+
+    private String name;
+    private List<CamelContext> contexts;
+
+    public CamelContextModel(List<CamelContext> contexts, CamelContext object) {
+        super(object);
+
+        this.contexts = contexts;
+        name = object.getName();
+    }
+
+    protected CamelContext load() {
+        for (CamelContext context : contexts) {
+            if (name.equals(context.getName())) {
+                return context;
+            }
+        }
+
+        throw new IllegalArgumentException("Camel context " + name + " not found");
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/CamelContextsPage.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/CamelContextsPage.java
new file mode 100644
index 0000000..67c2bc9
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/CamelContextsPage.java
@@ -0,0 +1,76 @@
+package org.apache.karaf.webconsole.camel.internal.context;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.karaf.webconsole.camel.internal.CamelPage;
+import org.apache.karaf.webconsole.camel.internal.tracking.TraceContainer;
+import org.apache.karaf.webconsole.core.table.OrdinalColumn;
+import org.apache.karaf.webconsole.core.table.PropertyColumnExt;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.DefaultDataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.ops4j.pax.wicket.api.PaxWicketBean;
+import org.ops4j.pax.wicket.api.PaxWicketMountPoint;
+
+@PaxWicketMountPoint(mountPoint = "/camel/contexts")
+public class CamelContextsPage extends CamelPage {
+
+    @PaxWicketBean(name = "contexts")
+    private List<CamelContext> contexts;
+
+    @PaxWicketBean(name = "tracer")
+    private TraceContainer container;
+
+    public CamelContextsPage() {
+        @SuppressWarnings("unchecked")
+        IColumn<CamelContext>[] columns = new IColumn[] {
+            new OrdinalColumn<CamelContext>(),
+            new PropertyColumnExt<CamelContext>("Name", "name"),
+            new PropertyColumnExt<CamelContext>("Version", "version"),
+            new PropertyColumnExt<CamelContext>("Status", "status"),
+            new PropertyColumnExt<CamelContext>("Uptime", "uptime"),
+            new AbstractColumn<CamelContext>(Model.of("Message preview")) {
+                public void populateItem(Item<ICellPopulator<CamelContext>> cellItem, String componentId, IModel<CamelContext> rowModel) {
+                    cellItem.add(new Label(componentId, "" + container.isTracePossible(rowModel.getObject())));
+                }
+            },
+            new AbstractColumn<CamelContext>(Model.of("Tracing enabled")) {
+                public void populateItem(Item<ICellPopulator<CamelContext>> cellItem, String componentId, IModel<CamelContext> rowModel) {
+                    cellItem.add(new Label(componentId, "" + container.isTraced(rowModel.getObject())));
+                }
+            },
+            new AbstractColumn<CamelContext>(Model.of("Operations")) {
+                public void populateItem(Item<ICellPopulator<CamelContext>> cellItem, String componentId, IModel<CamelContext> rowModel) {
+                    cellItem.add(new ContextActionsPanel(componentId, rowModel));
+                }
+            }
+        };
+
+        ISortableDataProvider<CamelContext> provider = new SortableDataProvider<CamelContext>() {
+            public Iterator<? extends CamelContext> iterator(int first, int count) {
+                return new ArrayList<CamelContext>(contexts).subList(first, first + count).iterator();
+            }
+
+            public int size() {
+                return contexts.size();
+            }
+
+            public IModel<CamelContext> model(CamelContext object) {
+                return new CamelContextModel(contexts, object);
+            }
+        };
+
+        add(new DefaultDataTable<CamelContext>("contexts", columns, provider, 20));
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/ContextActionsPanel.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/ContextActionsPanel.java
new file mode 100644
index 0000000..9351ea4
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/ContextActionsPanel.java
@@ -0,0 +1,69 @@
+package org.apache.karaf.webconsole.camel.internal.context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.karaf.webconsole.camel.internal.tracking.TraceContainer;
+import org.apache.karaf.webconsole.core.table.ActionsPanel;
+import org.apache.wicket.Session;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.model.IModel;
+import org.ops4j.pax.wicket.api.PaxWicketBean;
+
+public class ContextActionsPanel extends ActionsPanel<CamelContext> {
+
+    @PaxWicketBean(name = "tracer")
+    private TraceContainer container;
+
+    public ContextActionsPanel(String componentId, IModel<CamelContext> model) {
+        super(componentId, model);
+    }
+
+    @Override
+    protected List<Link> getLinks(CamelContext object, String id) {
+        List<Link> links = new ArrayList<Link>();
+
+        if (!object.isTracing()) {
+            Link link = new Link<CamelContext>(id) {
+                @Override
+                public void onClick() {
+                    CamelContext context = (CamelContext) ContextActionsPanel.this.getDefaultModelObject();
+                    context.setTracing(true);
+                    setResponsePage(CamelContextsPage.class);
+                    Session.get().info("Tracing enabled for context " + context.getName());
+                }
+            };
+            link.add(new Label("label", "Enable tracing"));
+            links.add(link);
+        } else {
+            Link link = new Link<CamelContext>(id) {
+                @Override
+                public void onClick() {
+                    CamelContext context = (CamelContext) ContextActionsPanel.this.getDefaultModelObject();
+                    context.setTracing(false);
+                    setResponsePage(CamelContextsPage.class);
+                    Session.get().info("Tracing disabled for context " + context.getName());
+                }
+            };
+            link.add(new Label("label", "Disable tracing"));
+            links.add(link);
+        }
+
+        if (container.isTracePossible(object)) {
+            Link link = new Link<CamelContext>(id) {
+                @Override
+                public void onClick() {
+                    CamelContext context = (CamelContext) ContextActionsPanel.this.getDefaultModelObject();
+                    setResponsePage(new DumpPage(container, context));
+                }
+            };
+            link.add(new Label("label", "View messages"));
+            links.add(link);
+        }
+
+        return links;
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/DumpPage.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/DumpPage.java
new file mode 100644
index 0000000..c5bbed7
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/context/DumpPage.java
@@ -0,0 +1,40 @@
+package org.apache.karaf.webconsole.camel.internal.context;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.karaf.webconsole.camel.internal.CamelPage;
+import org.apache.karaf.webconsole.camel.internal.tracking.TraceContainer;
+import org.apache.karaf.webconsole.camel.internal.tracking.Tracer;
+import org.apache.karaf.webconsole.core.table.map.MapDataProvider;
+import org.apache.karaf.webconsole.core.table.map.MapDataTable;
+import org.apache.wicket.Session;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+
+public class DumpPage extends CamelPage {
+
+    public DumpPage(TraceContainer container, CamelContext context) {
+        Tracer tracer = container.getTracer(context);
+        List<Map<String, Serializable>> info;
+
+        if (tracer != null) {
+            info = tracer.getInfo();
+        } else {
+            Session.get().warn("Tracer not found");
+            info = new ArrayList<Map<String,Serializable>>();
+        }
+
+        add(new ListView<Map<String, Serializable>>("properties", info) {
+            @Override
+            protected void populateItem(ListItem<Map<String, Serializable>> item) {
+                item.add(new MapDataTable<String, Serializable>("propertyMap", new MapDataProvider<String, Serializable>(item.getModelObject()), 20));
+            }
+        });
+
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/navigation/CamelConsoleTabProvider.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/navigation/CamelConsoleTabProvider.java
new file mode 100644
index 0000000..ec9b5dd
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/navigation/CamelConsoleTabProvider.java
@@ -0,0 +1,22 @@
+package org.apache.karaf.webconsole.camel.internal.navigation;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.karaf.webconsole.camel.internal.context.CamelContextsPage;
+import org.apache.karaf.webconsole.core.navigation.ConsoleTabProvider;
+import org.apache.karaf.webconsole.core.util.LinkUtils;
+import org.apache.wicket.Page;
+import org.apache.wicket.markup.html.link.Link;
+
+public class CamelConsoleTabProvider implements ConsoleTabProvider {
+
+    public List<Link<Page>> getItems(String componentId, String labelId) {
+        return Collections.emptyList();
+    }
+
+    public Link<Page> getModuleLink(String componentId, String labelId) {
+        return LinkUtils.createPageLink(componentId, labelId, "Camel", CamelContextsPage.class);
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/DefaultTracerContainer.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/DefaultTracerContainer.java
new file mode 100644
index 0000000..b3dbd60
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/DefaultTracerContainer.java
@@ -0,0 +1,29 @@
+package org.apache.karaf.webconsole.camel.internal.tracking;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+
+public class DefaultTracerContainer implements TraceContainer {
+
+    private Map<CamelContext, Tracer> tracers = Collections.synchronizedMap(new HashMap<CamelContext, Tracer>());
+
+    public boolean isTraced(CamelContext context) {
+        return isTracePossible(context) && context.isTracing();
+    }
+
+    public boolean isTracePossible(CamelContext context) {
+        return getTracer(context) != null;
+    }
+
+    public Tracer getTracer(CamelContext context) {
+        return tracers.get(context);
+    }
+
+    public void register(CamelContext context, Tracer tracer) {
+        tracers.put(context, tracer);
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceContainer.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceContainer.java
new file mode 100644
index 0000000..8dbaeab
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceContainer.java
@@ -0,0 +1,15 @@
+package org.apache.karaf.webconsole.camel.internal.tracking;
+
+import org.apache.camel.CamelContext;
+
+public interface TraceContainer {
+
+    Tracer getTracer(CamelContext context);
+
+    boolean isTraced(CamelContext context);
+
+    boolean isTracePossible(CamelContext context);
+
+    void register(CamelContext context, Tracer tracer);
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceInterceptStrategy.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceInterceptStrategy.java
new file mode 100644
index 0000000..bdc3b10
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceInterceptStrategy.java
@@ -0,0 +1,46 @@
+package org.apache.karaf.webconsole.camel.internal.tracking;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Processor;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.spi.InterceptStrategy;
+
+public class TraceInterceptStrategy implements InterceptStrategy, Tracer {
+
+    private LinkedBlockingDeque<Map<String, Serializable>> data = new LinkedBlockingDeque<Map<String,Serializable>>(20);
+    private TraceContainer container;
+
+    public TraceInterceptStrategy(TraceContainer container) {
+        this.container = container;
+    }
+
+    public Processor wrapProcessorInInterceptors(CamelContext context,
+        ProcessorDefinition<?> definition,
+        Processor target,
+        Processor nextTarget) throws Exception {
+
+        Map<String,Serializable> properties = Collections.synchronizedMap(new HashMap<String, Serializable>());
+        properties.put("context", context.getName());
+        properties.put("version", context.getVersion());
+
+        data.add(properties);
+
+        TraceProcessor tracer = new TraceProcessor(properties, target);
+        container.register(context, this);
+
+        return tracer;
+    }
+
+    public List<Map<String, Serializable>> getInfo() {
+        return new ArrayList<Map<String, Serializable>>(data);
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceProcessor.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceProcessor.java
new file mode 100644
index 0000000..1550858
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/TraceProcessor.java
@@ -0,0 +1,65 @@
+package org.apache.karaf.webconsole.camel.internal.tracking;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.processor.DelegateAsyncProcessor;
+
+public class TraceProcessor extends DelegateAsyncProcessor {
+
+    private Map<String, Serializable> properties;
+
+    public TraceProcessor(Map<String, Serializable> properties, Processor target) {
+        super(target);
+        this.properties = properties;
+    }
+
+    @Override
+    public boolean process(Exchange exchange, AsyncCallback callback) {
+
+        CamelContext context = exchange.getContext();
+        if (!context.isTracing()) {
+            return super.process(exchange, callback);
+        }
+
+        properties.put("exchangeId", exchange.getExchangeId());
+
+        properties.put("exchangeProperties", (Serializable) exchange.getProperties());
+        Message msg = exchange.getIn();
+        if (msg != null) {
+            properties.put("exchangeInId", msg.getMessageId());
+            properties.put("exchangeInHeaders", new HashMap(msg.getHeaders()));
+            Object body = msg.getBody();
+            if (body instanceof Serializable) {
+                properties.put("exchangeInBody", (Serializable) body);
+            } else {
+                properties.put("exchangeInBody", "- not serializable -");
+            }
+        } else {
+            properties.put("exchangeInId", null);
+        }
+
+        msg = exchange.getOut();
+        if (msg != null) {
+            properties.put("exchangeOutId", msg.getMessageId());
+            properties.put("exchangeOutHeaders", new HashMap(msg.getHeaders()));
+            Object body = msg.getBody();
+            if (body instanceof Serializable) {
+                properties.put("exchangeOutBody", (Serializable) body);
+            } else {
+                properties.put("exchangeOutBody", "- not serializable -");
+            }
+        } else {
+            properties.put("exchangeOutId", null);
+        }
+
+        return super.process(exchange, callback);
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/Tracer.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/Tracer.java
new file mode 100644
index 0000000..dd7f3c8
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/tracking/Tracer.java
@@ -0,0 +1,11 @@
+package org.apache.karaf.webconsole.camel.internal.tracking;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+public interface Tracer {
+
+    List<Map<String, Serializable>> getInfo();
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/widget/CamelWidget.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/widget/CamelWidget.java
new file mode 100644
index 0000000..7bdc37d
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/widget/CamelWidget.java
@@ -0,0 +1,35 @@
+package org.apache.karaf.webconsole.camel.internal.widget;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.karaf.webconsole.camel.internal.context.CamelContextsPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+
+public class CamelWidget extends Panel {
+
+    public CamelWidget(String id, List<CamelContext> contexts) {
+        super(id);
+
+//        List<CamelContext> subList = new ArrayList<CamelContext>(contexts);
+//        if (subList.size() > 4) {
+//            subList = subList.subList(0, 4);
+//        }
+        add(new Label("count", "" + contexts.size()));
+
+        add(new ListView<CamelContext>("contexts"/*, subList*/) {
+            @Override
+            protected void populateItem(ListItem<CamelContext> item) {
+                CamelContext model = item.getModelObject();
+                add(new Label("name", model.getName()));
+            }
+        });
+
+        add(new BookmarkablePageLink<CamelContextsPage>("management", CamelContextsPage.class));
+    }
+
+}
diff --git a/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/widget/CamelWidgetProvider.java b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/widget/CamelWidgetProvider.java
new file mode 100644
index 0000000..507bc91
--- /dev/null
+++ b/camel/src/main/java/org/apache/karaf/webconsole/camel/internal/widget/CamelWidgetProvider.java
@@ -0,0 +1,21 @@
+package org.apache.karaf.webconsole.camel.internal.widget;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.karaf.webconsole.core.widget.WidgetProvider;
+import org.apache.wicket.markup.html.panel.Panel;
+
+public class CamelWidgetProvider implements WidgetProvider {
+
+    private final List<CamelContext> contexts;
+
+    public CamelWidgetProvider(List<CamelContext> contexts) {
+        this.contexts = contexts;
+    }
+
+    public Panel getWidgetPanel(String id) {
+        return new CamelWidget(id, contexts);
+    }
+
+}
diff --git a/camel/src/main/resources/OSGI-INF/blueprint/camel.xml b/camel/src/main/resources/OSGI-INF/blueprint/camel.xml
new file mode 100644
index 0000000..3113b1e
--- /dev/null
+++ b/camel/src/main/resources/OSGI-INF/blueprint/camel.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <service interface="org.apache.karaf.webconsole.core.navigation.ConsoleTabProvider">
+        <bean class="org.apache.karaf.webconsole.camel.internal.navigation.CamelConsoleTabProvider" />
+    </service>
+
+    <bean id="tracer" class="org.apache.karaf.webconsole.camel.internal.tracking.DefaultTracerContainer" />
+
+    <reference-list id="contexts" interface="org.apache.camel.CamelContext"/>
+
+    <service ref="widgetProvider" interface="org.apache.karaf.webconsole.core.widget.WidgetProvider">
+        <service-properties>
+            <entry key="intention" value="dashboard" />
+        </service-properties>
+    </service>
+
+    <bean id="widgetProvider" class="org.apache.karaf.webconsole.camel.internal.widget.CamelWidgetProvider">
+        <argument ref="contexts" />
+    </bean>
+
+    <service auto-export="interfaces">
+        <bean class="org.apache.karaf.webconsole.camel.internal.tracking.TraceInterceptStrategy">
+            <argument ref="tracer" />
+        </bean>
+    </service>
+
+</blueprint>
diff --git a/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/CamelContextsPage.html b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/CamelContextsPage.html
new file mode 100644
index 0000000..bccb76c
--- /dev/null
+++ b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/CamelContextsPage.html
@@ -0,0 +1,33 @@
+<?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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf WebConsole</title>
+</head>
+<body>
+
+    <wicket:extend>
+        <h1>Deployed camel contexts</h1>
+
+        <table wicket:id="contexts" class="dataview" />
+
+    </wicket:extend>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/DumpPage.html b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/DumpPage.html
new file mode 100644
index 0000000..9f6d109
--- /dev/null
+++ b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/DumpPage.html
@@ -0,0 +1,39 @@
+<?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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf WebConsole</title>
+</head>
+<body>
+
+    <wicket:extend>
+        <h1>Traced context details</h1>
+
+        <h2>Info from trace</h2>
+
+        <ul>
+            <li wicket:id="properties">
+                <table wicket:id="propertyMap" class="dataview" />
+            </li>
+        </ul>
+
+    </wicket:extend>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/TracePage.html b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/TracePage.html
new file mode 100644
index 0000000..11618b1
--- /dev/null
+++ b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/context/TracePage.html
@@ -0,0 +1,40 @@
+<?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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf WebConsole</title>
+</head>
+<body>
+
+    <wicket:extend>
+        <h1>Deployed camel contexts</h1>
+
+        <p>
+            A tracer has been found: <label wicket:id="tracer"></label>.
+        </p>
+
+        <h2>Attached tracers</h2>
+        <ul>
+            <li wicket:id="tracers">ctx-1</li>
+        </ul>
+
+    </wicket:extend>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/widget/CamelWidget.html b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/widget/CamelWidget.html
new file mode 100644
index 0000000..1923a18
--- /dev/null
+++ b/camel/src/main/resources/org/apache/karaf/webconsole/camel/internal/widget/CamelWidget.html
@@ -0,0 +1,45 @@
+<?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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Karaf WebConsole</title>
+</head>
+<body>
+
+    <wicket:panel>
+        <div class="grid_6 widget">
+            <h1>Deployed camel contexts</h1>
+
+            <p>You have <span wicket:id="count">13</span> camel contexts deployed. First four are:</p>
+
+            <table class="dataview">
+                <tr>
+                    <th>Name</th>
+                </tr>
+                <tr wicket:id="contexts">
+                    <td><span wicket:id="name">.test-context</span></td>
+                </tr>
+            </table>
+
+            <a href="#" wicket:id="management">Go to context management</a>
+        </div>
+    </wicket:panel>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/camel/test-context.xml b/camel/test-context.xml
new file mode 100644
index 0000000..e7bafb5
--- /dev/null
+++ b/camel/test-context.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <camelContext id="timer-test" xmlns="http://camel.apache.org/schema/blueprint">
+        <route>
+            <from uri="timer:test" />
+            <to uri="log:test" />
+        </route>
+    </camelContext>
+
+    <reference id="interceptStragety" interface="org.apache.camel.spi.InterceptStrategy" />
+
+</blueprint>
diff --git a/pom.xml b/pom.xml
index 12ec443..a03fe43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,7 @@
         <ops4j.paxwicket.version>0.8.0-SNAPSHOT</ops4j.paxwicket.version>
         <karaf.version>2.2.2</karaf.version>
         <nmr.version>1.4.0</nmr.version>
+        <camel.version>[2.6.0,3)</camel.version>
     </properties>
 
     <modules>
@@ -43,6 +44,7 @@
         <module>karaf</module>
         <module>features</module>
         <module>servicemix</module>
+        <module>camel</module>
 
         <module>examples</module>
         <module>manual</module>