From f100269795f6a4d7688c2f8c8b6c66964a710bfe Mon Sep 17 00:00:00 2001 From: lburgazzoli Date: Sat, 18 May 2019 15:00:39 +0200 Subject: [PATCH] add servlet support --- .../java/org/apache/camel/k/Constants.java | 9 +- .../main/java/org/apache/camel/k/Runtime.java | 33 +++- .../k/listener/AbstractPhaseListener.java | 7 +- .../camel/k/listener/RoutesConfigurer.java | 7 +- .../camel/k/support/RuntimeSupport.java | 10 +- .../camel-k-runtime-example-servlet/pom.xml | 134 +++++++++++++++ .../camel/k/example/WebhookCustomizer.java | 33 ++++ .../org/apache/camel/k/customizer/webhook | 18 ++ .../src/main/resources/application.properties | 9 + .../src/main/resources/routes.groovy | 5 + camel-k-runtime-examples/pom.xml | 36 ++++ .../camel/k/health/HealthConfigurer.java | 18 +- .../camel/k/jvm/ApplicationRuntime.java | 28 ++- camel-k-runtime-servlet/pom.xml | 159 ++++++++++++++++++ .../camel/k/servlet/ServletConfigurer.java | 107 ++++++++++++ .../camel/k/servlet/ServletEndpoint.java | 95 +++++++++++ .../camel/k/servlet/ServletRegistration.java | 81 +++++++++ .../org.apache.camel.k.Runtime$Listener | 19 +++ .../k/servlet/ServletConfigurerTest.java | 52 ++++++ .../src/test/resources/log4j2-test.xml | 18 ++ pom.xml | 9 + 21 files changed, 871 insertions(+), 16 deletions(-) create mode 100644 camel-k-runtime-examples/camel-k-runtime-example-servlet/pom.xml create mode 100644 camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/java/org/apache/camel/k/example/WebhookCustomizer.java create mode 100644 camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/META-INF/services/org/apache/camel/k/customizer/webhook create mode 100644 camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/application.properties create mode 100644 camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/routes.groovy create mode 100644 camel-k-runtime-examples/pom.xml create mode 100644 camel-k-runtime-servlet/pom.xml create mode 100644 camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletConfigurer.java create mode 100644 camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletEndpoint.java create mode 100644 camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletRegistration.java create mode 100644 camel-k-runtime-servlet/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener create mode 100644 camel-k-runtime-servlet/src/test/java/org/apache/camel/k/servlet/ServletConfigurerTest.java create mode 100644 camel-k-runtime-servlet/src/test/resources/log4j2-test.xml diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java index d9d2d9d45..cc1935787 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java @@ -18,16 +18,23 @@ public final class Constants { public static final String ENV_CAMEL_K_ROUTES = "CAMEL_K_ROUTES"; + public static final String PROPERTY_CAMEL_K_ROUTES = "camel.k.routes"; + public static final String ENV_CAMEL_K_CONF = "CAMEL_K_CONF"; + public static final String PROPERTY_CAMEL_K_CONF = "camel.k.conf"; + public static final String ENV_CAMEL_K_CONF_D = "CAMEL_K_CONF_D"; + public static final String PROPERTY_CAMEL_K_CONF_D = "camel.k.conf.d"; + public static final String ENV_CAMEL_K_CUSTOMIZERS = "CAMEL_K_CUSTOMIZERS"; + public static final String PROPERTY_CAMEL_K_CUSTOMIZER = "camel.k.customizer"; + public static final String SCHEME_CLASSPATH = "classpath:"; public static final String SCHEME_FILE = "file:"; public static final String SCHEME_ENV = "env:"; public static final String LOGGING_LEVEL_PREFIX = "logging.level."; public static final String ROUTES_LOADER_RESOURCE_PATH = "META-INF/services/org/apache/camel/k/loader/"; public static final String CONTEXT_CUSTOMIZER_RESOURCE_PATH = "META-INF/services/org/apache/camel/k/customizer/"; - public static final String PROPERTY_CAMEL_K_CUSTOMIZER = "camel.k.customizer"; private Constants() { } diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Runtime.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Runtime.java index f2f1f01cb..091b7ad2e 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Runtime.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Runtime.java @@ -19,6 +19,7 @@ import java.util.Properties; import org.apache.camel.CamelContext; +import org.apache.camel.Ordered; import org.apache.camel.component.properties.PropertiesComponent; public interface Runtime { @@ -42,17 +43,45 @@ default void setProperties(Properties properties) { enum Phase { Starting, ConfigureContext, + ContextConfigured, ConfigureRoutes, + RoutesConfigured, Started, Stopping, Stopped } @FunctionalInterface - interface Listener { - void accept(Phase phase, Runtime runtime); + interface Listener extends Ordered { + boolean accept(Phase phase, Runtime runtime); + + @Override + default int getOrder() { + return Ordered.LOWEST; + } } interface Registry extends org.apache.camel.k.adapter.Registry { } + + /** + * Helper to create a simple runtime from a given Camel Context and Runtime Registry. + * + * @param camelContext the camel context + * @param registry the runtime registry + * @return the runtime + */ + static Runtime of( CamelContext camelContext, Registry registry) { + return new Runtime() { + @Override + public CamelContext getContext() { + return camelContext; + } + + @Override + public Registry getRegistry() { + return registry; + } + }; + } } diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/AbstractPhaseListener.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/AbstractPhaseListener.java index 7de499171..7678acc68 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/AbstractPhaseListener.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/AbstractPhaseListener.java @@ -26,10 +26,13 @@ protected AbstractPhaseListener(Runtime.Phase phase) { } @Override - public void accept(Runtime.Phase phase, Runtime runtime) { - if (this.phase == phase) { + public boolean accept(Runtime.Phase phase, Runtime runtime) { + boolean run = this.phase == phase; + if (run) { accept(runtime); } + + return run; } protected abstract void accept(Runtime runtime); diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java index 2600c6325..3b2c89c44 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java @@ -36,10 +36,15 @@ public RoutesConfigurer() { @Override protected void accept(Runtime runtime) { - final String routes = System.getenv().getOrDefault(Constants.ENV_CAMEL_K_ROUTES, ""); + String routes = System.getProperty(Constants.PROPERTY_CAMEL_K_ROUTES); + + if (ObjectHelper.isEmpty(routes)) { + routes = System.getenv(Constants.ENV_CAMEL_K_ROUTES); + } if (ObjectHelper.isEmpty(routes)) { LOGGER.warn("No routes found in {} environment variable", Constants.ENV_CAMEL_K_ROUTES); + return; } load(runtime, routes.split(",", -1)); diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java index 597b9ccb4..92dc84441 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java @@ -134,6 +134,12 @@ public static void configureRest(CamelContext context) { } } + public static String resolvePropertyPlaceholders(CamelContext context, String text) throws Exception { + return context.resolvePropertyPlaceholders( + context.getPropertyPrefixToken() + text + context.getPropertySuffixToken() + ); + } + public static int bindProperties(CamelContext context, Object target, String prefix) { final PropertiesComponent component = context.getComponent("properties", PropertiesComponent.class); final Properties properties = component.getInitialProperties(); @@ -191,8 +197,8 @@ public static RoutesLoader lookupLoaderFromResource(CamelContext context, Source } public static Properties loadProperties() { - final String conf = System.getenv(Constants.ENV_CAMEL_K_CONF); - final String confd = System.getenv(Constants.ENV_CAMEL_K_CONF_D); + final String conf = System.getProperty(Constants.PROPERTY_CAMEL_K_CONF, System.getenv(Constants.ENV_CAMEL_K_CONF)); + final String confd = System.getProperty(Constants.PROPERTY_CAMEL_K_CONF_D, System.getenv(Constants.ENV_CAMEL_K_CONF_D)); return loadProperties(conf, confd); } diff --git a/camel-k-runtime-examples/camel-k-runtime-example-servlet/pom.xml b/camel-k-runtime-examples/camel-k-runtime-example-servlet/pom.xml new file mode 100644 index 000000000..2698c0056 --- /dev/null +++ b/camel-k-runtime-examples/camel-k-runtime-example-servlet/pom.xml @@ -0,0 +1,134 @@ + + + + + org.apache.camel.k + camel-k-runtime-examples + 0.3.3-SNAPSHOT + + 4.0.0 + + camel-k-runtime-example-servlet + + + + + + + + + + + org.apache.camel + camel-core + + + org.apache.camel + camel-servlet + + + org.apache.camel.k + camel-k-runtime-jvm + + + org.apache.camel.k + camel-k-runtime-groovy + + + org.apache.camel.k + camel-k-runtime-servlet + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j2.version} + + + org.codehaus.groovy + groovy + ${groovy.version} + + + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + + + java + + + + + org.apache.camel.k.jvm.Application + runtime + + + camel.k.conf + ${project.basedir}/src/main/resources/application.properties + + + camel.k.routes + file:${project.basedir}/src/main/resources/routes.groovy + + + + + + + + + + camel3 + + + camel3 + + + + + + org.apache.camel.k + camel-k-adapter-camel-3 + + + + + camel2 + + + !camel3 + + + + + + org.apache.camel.k + camel-k-adapter-camel-2 + + + + + + diff --git a/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/java/org/apache/camel/k/example/WebhookCustomizer.java b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/java/org/apache/camel/k/example/WebhookCustomizer.java new file mode 100644 index 000000000..b47419403 --- /dev/null +++ b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/java/org/apache/camel/k/example/WebhookCustomizer.java @@ -0,0 +1,33 @@ +/** + * 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.k.example; + +import org.apache.camel.CamelContext; +import org.apache.camel.component.servlet.CamelHttpTransportServlet; +import org.apache.camel.k.ContextCustomizer; +import org.apache.camel.k.Runtime; +import org.apache.camel.k.servlet.ServletRegistration; + +public class WebhookCustomizer implements ContextCustomizer { + @Override + public void apply(CamelContext camelContext, Runtime.Registry registry) { + registry.bind( + "webhook-servlet", + new ServletRegistration("CamelServlet", new CamelHttpTransportServlet(), "/webhook/*") + ); + } +} diff --git a/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/META-INF/services/org/apache/camel/k/customizer/webhook b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/META-INF/services/org/apache/camel/k/customizer/webhook new file mode 100644 index 000000000..6952fcfd0 --- /dev/null +++ b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/META-INF/services/org/apache/camel/k/customizer/webhook @@ -0,0 +1,18 @@ +# +# 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. +# + +class=org.apache.camel.k.example.WebhookCustomizer diff --git a/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/application.properties b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/application.properties new file mode 100644 index 000000000..09dd67b3c --- /dev/null +++ b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/application.properties @@ -0,0 +1,9 @@ +# +# Camel +# +camel.context.streamCaching = true + +# +# Camel K +# +camel.k.customizer = webhook \ No newline at end of file diff --git a/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/routes.groovy b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/routes.groovy new file mode 100644 index 000000000..439c62d49 --- /dev/null +++ b/camel-k-runtime-examples/camel-k-runtime-example-servlet/src/main/resources/routes.groovy @@ -0,0 +1,5 @@ + + +from('servlet:/test') + .convertBodyTo(String.class) + .to('log:info') \ No newline at end of file diff --git a/camel-k-runtime-examples/pom.xml b/camel-k-runtime-examples/pom.xml new file mode 100644 index 000000000..88e797923 --- /dev/null +++ b/camel-k-runtime-examples/pom.xml @@ -0,0 +1,36 @@ + + + + + org.apache.camel.k + camel-k-runtime-parent + 0.3.3-SNAPSHOT + + 4.0.0 + + pom + camel-k-runtime-examples + + + + camel-k-runtime-example-servlet + + + diff --git a/camel-k-runtime-health/src/main/java/org/apache/camel/k/health/HealthConfigurer.java b/camel-k-runtime-health/src/main/java/org/apache/camel/k/health/HealthConfigurer.java index cea94fe0b..4d3583b4a 100644 --- a/camel-k-runtime-health/src/main/java/org/apache/camel/k/health/HealthConfigurer.java +++ b/camel-k-runtime-health/src/main/java/org/apache/camel/k/health/HealthConfigurer.java @@ -16,6 +16,7 @@ */ package org.apache.camel.k.health; +import org.apache.camel.Ordered; import org.apache.camel.k.Runtime; import org.apache.camel.spi.HasId; @@ -62,19 +63,32 @@ public void setPath(String path) { } @Override - public void accept(Runtime.Phase phase, Runtime runtime) { + public int getOrder() { + return Ordered.LOWEST; + } + + @Override + public boolean accept(Runtime.Phase phase, Runtime runtime) { + boolean executed = false; + try { - if (phase == Runtime.Phase.Starting) { + if (phase == Runtime.Phase.ContextConfigured) { endpoint = new HealthEndpoint(runtime.getContext(), bindHost, bindPort, path); endpoint.start(); + + executed = true; } else if (phase == Runtime.Phase.Stopping) { if (endpoint != null) { endpoint.stop(); } + + executed = true; } } catch (Exception e) { throw new RuntimeException(e); } + + return executed; } @Override diff --git a/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/ApplicationRuntime.java b/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/ApplicationRuntime.java index 806608b84..57c43479a 100644 --- a/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/ApplicationRuntime.java +++ b/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/ApplicationRuntime.java @@ -16,6 +16,7 @@ */ package org.apache.camel.k.jvm; +import java.util.Comparator; import java.util.LinkedHashSet; import java.util.Set; @@ -94,38 +95,53 @@ public void addListener(Phase phase, ThrowingConsumer consum if (p == phase) { try { consumer.accept(runtime); + return true; } catch (Exception e) { throw Exceptions.wrapRuntimeCamelException(e); } } + + return false; }); } private class MainListenerAdapter implements org.apache.camel.main.MainListener { @Override public void beforeStart(MainSupport main) { - listeners.forEach(l -> l.accept(Phase.Starting, ApplicationRuntime.this)); + invokeListeners(Phase.Starting); } @Override public void configure(CamelContext context) { - listeners.forEach(l -> l.accept(Phase.ConfigureContext, ApplicationRuntime.this)); - listeners.forEach(l -> l.accept(Phase.ConfigureRoutes, ApplicationRuntime.this)); + invokeListeners(Phase.ConfigureContext); + invokeListeners(Phase.ContextConfigured); + invokeListeners(Phase.ConfigureRoutes); + invokeListeners(Phase.RoutesConfigured); } @Override public void afterStart(MainSupport main) { - listeners.forEach(l -> l.accept(Phase.Started, ApplicationRuntime.this)); + invokeListeners(Phase.Started); } @Override public void beforeStop(MainSupport main) { - listeners.forEach(l -> l.accept(Phase.Stopping, ApplicationRuntime.this)); + invokeListeners(Phase.Stopping); } @Override public void afterStop(MainSupport main) { - listeners.forEach(l -> l.accept(Phase.Stopped, ApplicationRuntime.this)); + invokeListeners(Phase.Stopped); + } + + private void invokeListeners(Phase phase) { + listeners.stream() + .sorted(Comparator.comparingInt(Listener::getOrder)) + .forEach(l -> { + if (l.accept(phase, ApplicationRuntime.this)) { + LOGGER.info("Listener {} executed in phase {}", l, phase); + } + }); } } } diff --git a/camel-k-runtime-servlet/pom.xml b/camel-k-runtime-servlet/pom.xml new file mode 100644 index 000000000..0317f8307 --- /dev/null +++ b/camel-k-runtime-servlet/pom.xml @@ -0,0 +1,159 @@ + + + + + org.apache.camel.k + camel-k-runtime-parent + 0.3.3-SNAPSHOT + + 4.0.0 + + camel-k-runtime-servlet + + + + + + + + + + + org.apache.camel + camel-core + provided + + + org.apache.camel + camel-servlet + provided + + + org.apache.camel.k + camel-k-runtime-core + + + + + io.undertow + undertow-core + ${undertow.version} + + + io.undertow + undertow-servlet + 1.4.26.Final + + + + + + + + + + org.apache.camel.k + camel-k-runtime-jvm + test + + + org.apache.camel + camel-test + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + + org.apache.logging.log4j + log4j-core + ${log4j2.version} + test + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j2.version} + test + + + + + + camel3 + + + camel3 + + + + + + org.apache.camel.k + camel-k-adapter-camel-3 + provided + + + + org.apache.camel + camel-properties + test + + + + + camel2 + + + !camel3 + + + + + + org.apache.camel.k + camel-k-adapter-camel-2 + provided + + + + + + diff --git a/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletConfigurer.java b/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletConfigurer.java new file mode 100644 index 000000000..ea1842b1a --- /dev/null +++ b/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletConfigurer.java @@ -0,0 +1,107 @@ +/** + * 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.k.servlet; + +import org.apache.camel.Ordered; +import org.apache.camel.k.Runtime; +import org.apache.camel.spi.HasId; + +public class ServletConfigurer implements Runtime.Listener, HasId { + public static final String ID = "endpoint.servlet"; + public static final String DEFAULT_BIND_HOST = "0.0.0.0"; + public static final int DEFAULT_BIND_PORT = 8080; + public static final String DEFAULT_PATH = "/"; + + private String bindHost; + private int bindPort; + private String path; + private ServletEndpoint endpoint; + + public ServletConfigurer() { + this.bindHost = DEFAULT_BIND_HOST; + this.bindPort = DEFAULT_BIND_PORT; + this.path = DEFAULT_PATH; + } + + public String getBindHost() { + return bindHost; + } + + public void setBindHost(String bindHost) { + this.bindHost = bindHost; + } + + public int getBindPort() { + return bindPort; + } + + public void setBindPort(int bindPort) { + this.bindPort = bindPort; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @Override + public int getOrder() { + return Ordered.HIGHEST; + } + + @Override + public boolean accept(Runtime.Phase phase, Runtime runtime) { + boolean executed = false; + + try { + if (phase == Runtime.Phase.ContextConfigured) { + endpoint = new ServletEndpoint(runtime.getContext(), bindHost, bindPort, path); + endpoint.start(); + + executed = true; + } else if (phase == Runtime.Phase.Stopping) { + if (endpoint != null) { + endpoint.stop(); + } + + executed = true; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + return executed; + } + + @Override + public String getId() { + return ID; + } + + // **************************** + // + // Exposed for testing purpose + // + // **************************** + + ServletEndpoint getEndpoint() { + return endpoint; + } +} diff --git a/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletEndpoint.java b/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletEndpoint.java new file mode 100644 index 000000000..f30ba761e --- /dev/null +++ b/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletEndpoint.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.k.servlet; + +import javax.servlet.http.HttpServlet; + +import io.undertow.Handlers; +import io.undertow.Undertow; +import io.undertow.server.handlers.PathHandler; +import io.undertow.servlet.Servlets; +import io.undertow.servlet.api.DeploymentInfo; +import io.undertow.servlet.api.DeploymentManager; +import io.undertow.servlet.util.ImmediateInstanceHandle; +import org.apache.camel.CamelContext; +import org.apache.camel.k.adapter.ServiceSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServletEndpoint extends ServiceSupport { + private static final Logger LOGGER = LoggerFactory.getLogger(ServletEndpoint.class); + + private final CamelContext context; + private final String bindHost; + private final int bindPort; + private final String path; + + private Undertow server; + private DeploymentManager manager; + + public ServletEndpoint(CamelContext context, String bindHost, int bindPort, String path) { + this.context = context; + this.bindHost = bindHost; + this.bindPort = bindPort; + this.path = path; + } + + @Override + protected void doStart() throws Exception { + DeploymentInfo servletBuilder = Servlets.deployment() + .setClassLoader(ServletEndpoint.class.getClassLoader()) + .setContextPath(path) + .setDeploymentName("camel-k.war"); + + context.getRegistry().findByType(ServletRegistration.class).forEach( r -> { + LOGGER.info("Registering servlet: {}", r); + + servletBuilder.addServlet( + Servlets.servlet(r.getName(), HttpServlet.class, () -> new ImmediateInstanceHandle(r.getServlet())).addMappings(r.getMappings()) + ); + } + ); + + this.manager = Servlets.defaultContainer().addDeployment(servletBuilder); + this.manager.deploy(); + + PathHandler path = Handlers.path(Handlers.redirect(this.path)).addPrefixPath(this.path, manager.start()); + + LOGGER.info("Starting servlet engine on {}:{}{}", this.bindHost, this.bindPort, this.path); + + this.server = Undertow.builder().addHttpListener(this.bindPort, this.bindHost).setHandler(path).build(); + this.server.start(); + } + + @Override + protected void doStop() throws Exception { + if (this.server != null) { + LOGGER.info("Stopping servlet engine"); + this.server.stop(); + } + } + + // **************************** + // + // Exposed for testing purpose + // + // **************************** + + DeploymentManager getManager() { + return manager; + } +} diff --git a/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletRegistration.java b/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletRegistration.java new file mode 100644 index 000000000..24ace5ae7 --- /dev/null +++ b/camel-k-runtime-servlet/src/main/java/org/apache/camel/k/servlet/ServletRegistration.java @@ -0,0 +1,81 @@ +/** + * 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.k.servlet; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; +import javax.servlet.Servlet; + +/** + * An helper class used to register servlets. + * + *
+ * public class WebhookCustomizer implements ContextCustomizer {
+ *     @Override
+ *     public void apply(CamelContext camelContext, Runtime.Registry registry) {
+ *         registry.bind(
+ *             "webhook-servlet",
+ *             new ServletRegistration("CamelServlet", new CamelHttpTransportServlet(), "/webhook/*")
+ *         );
+ *     }
+ * }
+ * 
+ */
+public final class ServletRegistration {
+    private final Servlet servlet;
+    private final String name;
+    private final Set mappings;
+
+    public ServletRegistration(String name, Servlet servlet, Collection mappings) {
+        this.name = name;
+        this.servlet = servlet;
+        this.mappings = new LinkedHashSet<>();
+        this.mappings.addAll(mappings);
+    }
+
+    public ServletRegistration(String name, Servlet servlet, String... mappings) {
+        this.name = name;
+        this.servlet = servlet;
+        this.mappings = new LinkedHashSet<>();
+
+        for (String mapping: mappings) {
+            this.mappings.add(mapping);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Servlet getServlet() {
+        return servlet;
+    }
+
+    public Collection getMappings() {
+        return mappings;
+    }
+
+    @Override
+    public String toString() {
+        return "ServletRegistration{" +
+            "servlet=" + servlet +
+            ", name='" + name + '\'' +
+            ", mappings=" + mappings +
+            '}';
+    }
+}
diff --git a/camel-k-runtime-servlet/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener b/camel-k-runtime-servlet/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
new file mode 100644
index 000000000..d86fdfec0
--- /dev/null
+++ b/camel-k-runtime-servlet/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+org.apache.camel.k.servlet.ServletConfigurer
+
diff --git a/camel-k-runtime-servlet/src/test/java/org/apache/camel/k/servlet/ServletConfigurerTest.java b/camel-k-runtime-servlet/src/test/java/org/apache/camel/k/servlet/ServletConfigurerTest.java
new file mode 100644
index 000000000..1f4d2a9a0
--- /dev/null
+++ b/camel-k-runtime-servlet/src/test/java/org/apache/camel/k/servlet/ServletConfigurerTest.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.k.servlet;
+
+import io.undertow.servlet.core.ManagedServlet;
+import io.undertow.servlet.core.ManagedServlets;
+import org.apache.camel.component.servlet.CamelHttpTransportServlet;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.k.InMemoryRegistry;
+import org.apache.camel.k.Runtime;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ServletConfigurerTest {
+
+    @Test
+    public void testServletConfigurer() {
+        Runtime.Registry registry = new InMemoryRegistry();
+        Runtime runtime = Runtime.of(new DefaultCamelContext(registry), registry);
+
+        runtime.getRegistry().bind(
+            "camel-servlet",
+            new ServletRegistration("CamelServlet", new CamelHttpTransportServlet(), "/webhook/*")
+        );
+
+        ServletConfigurer configurer = new ServletConfigurer();
+        configurer.setBindPort(AvailablePortFinder.getNextAvailable());
+        configurer.accept(Runtime.Phase.ContextConfigured, runtime);
+
+        ManagedServlets managedServlets = configurer.getEndpoint().getManager().getDeployment().getServlets();
+        ManagedServlet servlet = managedServlets.getManagedServlet("CamelServlet");
+
+        assertThat(servlet).isNotNull();
+        assertThat(servlet.getServletInfo().getMappings()).contains("/webhook/*");
+    }
+}
diff --git a/camel-k-runtime-servlet/src/test/resources/log4j2-test.xml b/camel-k-runtime-servlet/src/test/resources/log4j2-test.xml
new file mode 100644
index 000000000..6f811638a
--- /dev/null
+++ b/camel-k-runtime-servlet/src/test/resources/log4j2-test.xml
@@ -0,0 +1,18 @@
+
+
+  
+    
+      
+    
+    
+  
+
+  
+    
+    
+      
+      
+    
+  
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 20a48bba9..c228d0d95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,7 @@
         2.9.8
         2.7.5
         2.2.0
+        1.4.26.Final
         1.6.1
         3.5.42
         3.8.0
@@ -62,6 +63,7 @@
         1.5
         2.7
         0.3.1
+        1.6.0
     
 
      
@@ -147,6 +149,8 @@
         camel-k-runtime-kotlin
         camel-k-runtime-yaml
         camel-k-runtime-health
+        camel-k-runtime-servlet
+        camel-k-runtime-examples
         camel-knative-http
         camel-knative
         camel-k-runtime-bom
@@ -222,6 +226,11 @@
                 camel-k-runtime-health
                 ${project.version}
             
+            
+                org.apache.camel.k
+                camel-k-runtime-servlet
+                ${project.version}
+            
             
                 org.apache.camel.k
                 camel-knative