From e59f954dc21e56a3ef70e66b671916743ffc170e Mon Sep 17 00:00:00 2001 From: Gary Tully Date: Thu, 30 Jun 2022 16:09:00 +0100 Subject: [PATCH] ARTEMIS-3042 simple properties based, extensible broker image --- .../apache/activemq/artemis/cli/Artemis.java | 15 +- .../apache/activemq/cli/test/ArtemisTest.java | 3 +- artemis-image/README.md | 51 ++++++ artemis-image/TODO.md | 7 + artemis-image/examples/README.md | 61 ++++++++ .../amqp_sasl_scram.properties | 44 ++++++ .../amqp_sasl_scram_test__etc/login.config | 26 +++ .../examples/amqp_sasl_scram_test__etc/role | 18 +++ artemis-image/examples/byoc__etc/broker.xml | 46 ++++++ artemis-image/examples/pom.xml | 77 +++++++++ artemis-image/pom.xml | 105 +++++++++++++ .../src/main/jib/config/acceptor.properties | 21 +++ .../src/main/resources/log4j2.properties | 20 +++ .../artemis/ActiveMQImageExamplesTest.java | 87 ++++++++++ .../jms/server/embedded/EmbeddedJMS.java | 2 +- .../server/embedded/EmbeddedActiveMQ.java | 7 +- .../artemis/core/server/embedded/Main.java | 148 ++++++++++++++++++ .../core/server/impl/ActiveMQServerImpl.java | 1 + .../core/server/embedded/MainTest.java | 30 ++++ pom.xml | 8 + 20 files changed, 766 insertions(+), 11 deletions(-) create mode 100644 artemis-image/README.md create mode 100644 artemis-image/TODO.md create mode 100644 artemis-image/examples/README.md create mode 100644 artemis-image/examples/amqp_sasl_scram_test__etc/amqp_sasl_scram.properties create mode 100644 artemis-image/examples/amqp_sasl_scram_test__etc/login.config create mode 100644 artemis-image/examples/amqp_sasl_scram_test__etc/role create mode 100644 artemis-image/examples/byoc__etc/broker.xml create mode 100644 artemis-image/examples/pom.xml create mode 100644 artemis-image/pom.xml create mode 100644 artemis-image/src/main/jib/config/acceptor.properties create mode 100644 artemis-image/src/main/resources/log4j2.properties create mode 100644 artemis-image/src/test/java/org/apache/activemq/artemis/ActiveMQImageExamplesTest.java create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/Main.java create mode 100644 artemis-server/src/test/java/org/apache/activemq/artemis/core/server/embedded/MainTest.java diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java index 31bd72ffd6d3..4242e755266b 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/Artemis.java @@ -115,14 +115,15 @@ public static void main(String... args) throws Exception { public static void verifyManagementDTO(File etc) { if (etc != null) { File management = new File(etc, "management.xml"); - - try { - ManagementContextDTO managementContextDTO = XmlUtil.decode(ManagementContextDTO.class, management); - if (managementContextDTO != null && managementContextDTO.getAuthorisation() != null) { - System.setProperty("javax.management.builder.initial", "org.apache.activemq.artemis.core.server.management.ArtemisMBeanServerBuilder"); + if (management.exists()) { + try { + ManagementContextDTO managementContextDTO = XmlUtil.decode(ManagementContextDTO.class, management); + if (managementContextDTO != null && managementContextDTO.getAuthorisation() != null) { + System.setProperty("javax.management.builder.initial", "org.apache.activemq.artemis.core.server.management.ArtemisMBeanServerBuilder"); + } + } catch (Exception e) { + e.printStackTrace(); } - } catch (Exception e) { - e.printStackTrace(); } } } diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java index 9d62df9e5348..d3d0174e33e5 100644 --- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java +++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java @@ -37,6 +37,7 @@ import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -2143,7 +2144,7 @@ public void testRunPropertiesDudArgument() throws Exception { // verify error Object ret = Artemis.internalExecute("run", "--properties", "https://www.apache.org"); - assertTrue(ret instanceof IllegalStateException); + assertTrue(ret instanceof FileNotFoundException); } @Test diff --git a/artemis-image/README.md b/artemis-image/README.md new file mode 100644 index 000000000000..5d57a757cac5 --- /dev/null +++ b/artemis-image/README.md @@ -0,0 +1,51 @@ +###What is in the image + +An _empty_, _open_ broker with a default acceptor on port 61616 + + - by empty: has no addresses or queues but will auto create on demand + - by open: has no security; authentication or authorization, users or roles + +###How will the image behave + + 1) the image will use or create `/app/data` for persistence of data + + 2) the image will use any [.properties files](https://activemq.apache.org/components/artemis/documentation/latest/configuration-index.html#broker_properties) from `/app/etc` to augment broker configuration + + 3) the image will use `/app/etc/broker.xml` if present, to bootstrap configuration, the 'bring your own config' use case + +###Build and Use + +First build an OCI image tar file from this artemis project using mvn: + + `$> mvn compile jib:buildTar@now` + +An OCI image is created as a tar file. + +> *Note that any OCI compatible container runtime and registry can be used for the next steps, eg: docker, podman... I have used podman.* + +To load the image tar into the local container registry, use: + + `$> sudo podman image load --input target/jib-image.tar` + +To run the image detached with port 61616 bridged to the local host by podman, use: + + `$> sudo podman run --name=artemis -dp 61616:61616 localhost/target/activemq-artemis-image:<...>` + +To determine the IP of the bridged pod by inspecting the container by its name, use: +`$> export BRIDGE_IP=`sudo podman inspect --format='{{.NetworkSettings.IPAddress}}' artemis` + +Execute the artemis producer/consumer command line tools to interact with the broker. + + `$> ./bin/artemis producer --url tcp://$BRIDGE_IP:61616` + + `$> ./bin/artemis consumer --url tcp://$BRIDGE_IP:61616` + +###Intent + +The intent is that this image is useful as is. If one can trust users, having no access control or limits can work fine. + +If not, then this image can be configured by mounting an `/app/etc` directory with property files that augment default broker configuration. +This image could also be the base for a derived jib image, by simply adding more property files to the `src/main/jib/config` directory. + +see examples/README.md for some more detail. + diff --git a/artemis-image/TODO.md b/artemis-image/TODO.md new file mode 100644 index 000000000000..4a07a036430d --- /dev/null +++ b/artemis-image/TODO.md @@ -0,0 +1,7 @@ +TODO: + - replace @classpath_file with cp from libs/*.jar directory - more extensible for folks that want extra jars/plugins in derived images + - or figure out a mount point for extra jars + - currently running as root! + - maybe we need a base image with an 'app' or 'java' user preconfigured with rw permissions on /app + - or do that in a launch.sh + - this is a known and reasonable limitation of jib https://github.com/GoogleContainerTools/jib/issues/1029 \ No newline at end of file diff --git a/artemis-image/examples/README.md b/artemis-image/examples/README.md new file mode 100644 index 000000000000..05aae3e9ab3b --- /dev/null +++ b/artemis-image/examples/README.md @@ -0,0 +1,61 @@ +###Examples + +This directory contains examples of customising the image for particular use cases + +####amqp_sasl_scram_test__etc +In this example, you can run the image with configuration that locks the broker down to a single user on a single predefined +queue called `TEST`. The necessary configuration overrides: + - restricting the acceptor to AMQP/SASL-SCRAM + - providing RBAC for queue TEST + +are in:`./amqp_sasl_scram_test__etc/amqp_sasl_scram.properties` + +To exercise this example, you need to choose a password for the pre-configured user 'A'. +With SASL_SCRAM the broker retains a salted representation of that value, but not the plain text value. + +Register your chosen password by creating `./amqp_sasl_scram_test__etc/user` using mvn as follows: + + `$> mvn exec:exec -Dexample.pwd=` + +To see the result, cat the generated user file to see the stored representation: + + `$> cat ./amqp_sasl_scram_test__etc/user` + +You can then mount the `./amqp_sasl_scram_test__etc directory` as `/app/etc` for the container and initialize JAAS +via the `java.security.auth.login.config` system property, which is passed to the JVM via the ENV `JDK_JAVA_OPTIONS` as follows: + + `$> sudo podman run --name=artemis-amqp -dp 61616:61616 --env JDK_JAVA_OPTIONS=-Djava.security.auth.login.config=/app/etc/login.config --privileged -v ./amqp_sasl_scram_test__etc:/app/etc localhost/target/activemq-artemis-image:` + +To determine the IP of the bridged pod by inspecting the container by its name, use: +`$> export BRIDGE_IP=`sudo podman inspect --format='{{.NetworkSettings.IPAddress}}' artemis-amqp` + +Execute the artemis producer/consumer command line tools to validate secure access to the TEST queue using AMQP +SASL-SCRAM with your chosen password via: + + `$> ./bin/artemis producer --protocol amqp --url amqp://$BRIDGE_IP:61616 --user A --password ` + + `$> ./bin/artemis consumer --protocol amqp --url amqp://$BRIDGE_IP:61616 --user A --password ` + +####byoc__etc +This is an example of "Bring Your Own Config" or BYOC. The image will look for `/app/etc/broker.xml`. If that file exists +it will be treated as the broker xml configuration for the embedded broker. If your existing configuration is nicely +locked down or if you want to provide some custom defaults for your image, referencing an existing broker.xml makes sense. +Property files can still be used to augment the defaults or be used solely for more dynamic parts of configuration. + +To exercise the example, `./byoc__etc directory` as `/app/etc` for the container as follows: + + `$> sudo podman run --name=artemis-byoc -dp 61616:61616 --privileged -v ./byoc__etc:/app/etc localhost/target/activemq-artemis-image:` + +Peek at the broker logs to note the broker name 'byoc' configured from the broker.xml file + +`$> sudo podman logs artemis-byoc + +To determine the IP of the bridged pod by inspecting the container by its name, use: +`$> export BRIDGE_IP=`sudo podman inspect --format='{{.NetworkSettings.IPAddress}}' artemis-byoc` + +Execute the artemis producer/consumer command line tools to validate, it behaves like the bare image: + + `$> ./bin/artemis producer --url tcp://$BRIDGE_IP:61616` + + `$> ./bin/artemis consumer --url tcp://$BRIDGE_IP:61616` + diff --git a/artemis-image/examples/amqp_sasl_scram_test__etc/amqp_sasl_scram.properties b/artemis-image/examples/amqp_sasl_scram_test__etc/amqp_sasl_scram.properties new file mode 100644 index 000000000000..59fc7b5691e0 --- /dev/null +++ b/artemis-image/examples/amqp_sasl_scram_test__etc/amqp_sasl_scram.properties @@ -0,0 +1,44 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +## reset to default +securityEnabled=true + +## reference user role files with login.config, -Djava.security.auth.login.config=jaas/login.config +## the broker JAAS domain in login.config +acceptorConfigurations.tcp.params.securityDomain=broker + +## lock down broker acceptor +## to SCRAM AMQP +acceptorConfigurations.tcp.params.saslMechanisms=SCRAM-SHA-512 +acceptorConfigurations.tcp.params.protocols=AMQP +acceptorConfigurations.tcp.params.saslLoginConfigScope=amqp-sasl-scram + +## if over TLS, configure acceptor key and trust store +# acceptorConfigurations.tcp.params.sslEnabled=true +# acceptorConfigurations.tcp.params.keyStorePath=/app/etc/.keystore +# acceptorConfigurations.tcp.params.keyStorePassword= + + +## create TEST address and ANYCAST queue b/c we won't have createX permissions +## TEST is the default queue for ./bin/artemis producer +addressConfigurations.TEST.queueConfigs.TEST.routingType=ANYCAST + +## grant users role read/write +securityRoles.TEST.users.send=true +securityRoles.TEST.users.consume=true + diff --git a/artemis-image/examples/amqp_sasl_scram_test__etc/login.config b/artemis-image/examples/amqp_sasl_scram_test__etc/login.config new file mode 100644 index 000000000000..536414f9f1b3 --- /dev/null +++ b/artemis-image/examples/amqp_sasl_scram_test__etc/login.config @@ -0,0 +1,26 @@ +/* + * 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. + */ + +amqp-sasl-scram { + org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule required + ; +}; + +broker { + org.apache.activemq.artemis.spi.core.security.jaas.SCRAMLoginModule required + ; +}; diff --git a/artemis-image/examples/amqp_sasl_scram_test__etc/role b/artemis-image/examples/amqp_sasl_scram_test__etc/role new file mode 100644 index 000000000000..bff70b03f741 --- /dev/null +++ b/artemis-image/examples/amqp_sasl_scram_test__etc/role @@ -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. +## --------------------------------------------------------------------------- + +users=A diff --git a/artemis-image/examples/byoc__etc/broker.xml b/artemis-image/examples/byoc__etc/broker.xml new file mode 100644 index 000000000000..425624ed9962 --- /dev/null +++ b/artemis-image/examples/byoc__etc/broker.xml @@ -0,0 +1,46 @@ + + + + + + + + + byoc + false + + + + +
+ + + +
+
+
+
\ No newline at end of file diff --git a/artemis-image/examples/pom.xml b/artemis-image/examples/pom.xml new file mode 100644 index 000000000000..dee58330f87e --- /dev/null +++ b/artemis-image/examples/pom.xml @@ -0,0 +1,77 @@ + + + + 4.0.0 + + + org.apache.activemq + artemis-pom + 2.29.0-SNAPSHOT + ../../pom.xml + + + artemis-image-examples + Apache ActiveMQ Artemis Image Examples + pom + + + + ${project.basedir}/../.. + + + A + + + amqp_sasl_scram_test__etc/user + + + + + org.apache.activemq + artemis-server + ${project.parent.version} + + + + + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + java + + -classpath + + org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule + ${example.user} + ${example.pwd} + + ${example.user.file} + + + + + + diff --git a/artemis-image/pom.xml b/artemis-image/pom.xml new file mode 100644 index 000000000000..f58074512749 --- /dev/null +++ b/artemis-image/pom.xml @@ -0,0 +1,105 @@ + + + + 4.0.0 + + + org.apache.activemq + artemis-pom + 2.29.0-SNAPSHOT + + + artemis-image + Apache ActiveMQ Artemis Image + + + + + eclipse-temurin:20-jre@sha256:5340605ada8bee017109147c838a96a24ecec037bedac5f157b26817ab633e02 + + + ${project.basedir}/.. + + + + + + + + + org.apache.activemq + apache-artemis + ${project.version} + pom + + + junit + junit + ${junit.version} + test + + + + org.apache.johnzon + johnzon-core + test + + + jakarta.json + jakarta.json-api + test + + + + + + + com.google.cloud.tools + jib-maven-plugin + + + ${fromImage} + + + target/activemq-artemis-image:${project.version} + + + org.apache.activemq.artemis.core.server.embedded.Main + + 61616 + + OCI + + + + + now + + none + + buildTar + + + + + + + + diff --git a/artemis-image/src/main/jib/config/acceptor.properties b/artemis-image/src/main/jib/config/acceptor.properties new file mode 100644 index 000000000000..526af5e590e6 --- /dev/null +++ b/artemis-image/src/main/jib/config/acceptor.properties @@ -0,0 +1,21 @@ +# 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. + +acceptorConfigurations.tcp.factoryClassName=org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory +acceptorConfigurations.tcp.params.host=0.0.0.0 +acceptorConfigurations.tcp.params.port=61616 + +# free for all with all protocols - good for demos +securityEnabled=false diff --git a/artemis-image/src/main/resources/log4j2.properties b/artemis-image/src/main/resources/log4j2.properties new file mode 100644 index 000000000000..a0296fb6b00b --- /dev/null +++ b/artemis-image/src/main/resources/log4j2.properties @@ -0,0 +1,20 @@ +# 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.stdout.name = STDOUT +appender.stdout.type = Console + +# default config in log4j is error, we want info by default +rootLogger = info, STDOUT \ No newline at end of file diff --git a/artemis-image/src/test/java/org/apache/activemq/artemis/ActiveMQImageExamplesTest.java b/artemis-image/src/test/java/org/apache/activemq/artemis/ActiveMQImageExamplesTest.java new file mode 100644 index 000000000000..4a205845c1fc --- /dev/null +++ b/artemis-image/src/test/java/org/apache/activemq/artemis/ActiveMQImageExamplesTest.java @@ -0,0 +1,87 @@ +/* + * 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.activemq.artemis; + +import java.lang.invoke.MethodHandles; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; +import org.apache.activemq.artemis.core.server.embedded.Main; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.activemq.artemis.core.server.embedded.Main.configureDataDirectory; + +public class ActiveMQImageExamplesTest { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Test + public void testSamlScram_etc() throws Exception { + + ConfigurationImpl configuration = new ConfigurationImpl(); + + String dataDir = "./target/data"; + + configureDataDirectory(configuration, dataDir); + + EmbeddedActiveMQ server = new EmbeddedActiveMQ(); + // look for properties files to augment configuration + server.setPropertiesResourcePath("./src/main/resources/,./examples/amqp_sasl_scram_test__etc/"); + server.setConfiguration(configuration); + + server.start(); + + server.stop(); + + } + + @Test + public void testBYOC_etc() throws Exception { + + final CountDownLatch done = new CountDownLatch(1); + Thread thread = new Thread(() -> { + try { + // contents byoc__etc copied to ./target/ to satisfy etc/broker.xml + Main.main(new String[] {"./target"}); + done.countDown(); + } catch (Exception e) { + logger.info("unexpected", e); + } + }); + + thread.start(); + + // shut it down after it starts! + do { + if (Main.getEmbeddedServer() != null) { + if (Main.getEmbeddedServer().getActiveMQServer() != null) { + if (Main.getEmbeddedServer().getActiveMQServer().getState() == ActiveMQServer.SERVER_STATE.STARTED) { + logger.trace("stopping server, state={}", Main.getEmbeddedServer().getActiveMQServer().getState()); + Main.getEmbeddedServer().stop(); + } + } + } + } + while (!done.await(200, TimeUnit.MILLISECONDS)); + } +} \ No newline at end of file diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java index 4904242c7b1a..150988317ed7 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/server/embedded/EmbeddedJMS.java @@ -105,7 +105,7 @@ public Object lookup(String name) { @Override public EmbeddedJMS start() throws Exception { - super.initStart(); + super.createActiveMQServer(); if (jmsConfiguration != null) { serverManager = new JMSServerManagerImpl(activeMQServer, jmsConfiguration); } else { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java index 197134dd049c..26190d68d069 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/EmbeddedActiveMQ.java @@ -130,12 +130,15 @@ public ActiveMQServer getActiveMQServer() { } public EmbeddedActiveMQ start() throws Exception { - initStart(); + createActiveMQServer(); activeMQServer.start(); return this; } - protected void initStart() throws Exception { + public void createActiveMQServer() throws Exception { + if (activeMQServer != null) { + return; + } if (configuration == null) { if (configResourcePath == null) configResourcePath = "broker.xml"; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/Main.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/Main.java new file mode 100644 index 000000000000..cf3517d308f3 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/embedded/Main.java @@ -0,0 +1,148 @@ +/* + * 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.activemq.artemis.core.server.embedded; + +import java.io.File; +import java.lang.invoke.MethodHandles; +import java.util.concurrent.CountDownLatch; + +import org.apache.activemq.artemis.core.config.FileDeploymentManager; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.config.impl.FileConfiguration; +import org.apache.activemq.artemis.core.config.impl.LegacyJMSConfiguration; +import org.apache.activemq.artemis.core.server.ActivateCallback; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static String workDir = "/app"; + private static volatile EmbeddedActiveMQ embeddedServer; + + public static void main(String[] args) throws Exception { + + if (args.length == 1) { + workDir = args[0]; + logger.debug("User supplied work dir {}", workDir); + } + + String propertiesConfigPath = "/config/," + workDir + "/etc/"; + if (args.length == 2) { + propertiesConfigPath = args[1]; + logger.debug("User supplied properties config path {}", propertiesConfigPath); + } + + FileConfiguration configuration = new FileConfiguration(); + + String dataDir = workDir + "/data"; + configureDataDirectory(configuration, dataDir); + + File bringYourOwnXml = new File(workDir + "/etc/broker.xml"); + if (bringYourOwnXml.exists()) { + logger.debug("byo config found {}", bringYourOwnXml); + configuration = loadFromXmlFile(bringYourOwnXml, configuration); + } + + embeddedServer = new EmbeddedActiveMQ(); + // look for properties files to augment configuration + embeddedServer.setPropertiesResourcePath(propertiesConfigPath); + embeddedServer.setConfiguration(configuration); + embeddedServer.createActiveMQServer(); + + final ActiveMQServer activeMQServer = embeddedServer.getActiveMQServer(); + final CountDownLatch serverStopped = new CountDownLatch(1); + registerCallbackToTriggerLatchOnStopped(activeMQServer, serverStopped); + exitWithErrorOnStartFailure(activeMQServer); + addShutdownHookForServerStop(embeddedServer); + + logger.debug("starting server"); + embeddedServer.start(); + + logger.debug("await server stop"); + serverStopped.await(); + embeddedServer = null; + } + + private static void exitWithErrorOnStartFailure(ActiveMQServer activeMQServer) { + activeMQServer.registerActivationFailureListener(exception -> { + logger.error("server failed to start {}, exit(1) in thread", exception); + new Thread("exit(1)-on-start-failure") { + @Override + public void run() { + logger.error("exit(1)"); + Runtime.getRuntime().exit(1); + } + }.start(); + }); + } + + private static void registerCallbackToTriggerLatchOnStopped(ActiveMQServer activeMQServer, CountDownLatch serverStopped) { + activeMQServer.registerActivateCallback(new ActivateCallback() { + + @Override + public void stop(ActiveMQServer server) { + logger.trace("server stop, state {}", server.getState()); + serverStopped.countDown(); + } + + @Override + public void shutdown(ActiveMQServer server) { + logger.trace("server shutdown, state {}", server.getState()); + serverStopped.countDown(); + } + }); + } + + private static void addShutdownHookForServerStop(final EmbeddedActiveMQ server) { + Runtime.getRuntime().addShutdownHook(new Thread("shutdown-hook") { + @Override + public void run() { + try { + logger.trace("stop via shutdown hook"); + server.stop(); + } catch (Exception ignored) { + // we want to exit fast and silently + logger.trace("Error on stop {}", ignored); + } + } + }); + } + + public static FileConfiguration loadFromXmlFile(File bringYourOwnXml, FileConfiguration base) throws Exception { + FileDeploymentManager deploymentManager = new FileDeploymentManager(bringYourOwnXml.toURI().toASCIIString()); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(base); + deploymentManager.addDeployable(base).addDeployable(legacyJMSConfiguration); + deploymentManager.readConfiguration(); + return base; + } + + public static void configureDataDirectory(ConfigurationImpl configuration, String dataDir) { + // any provided value via xml config or properties will override + configuration.setJournalDirectory(dataDir); + // setting these gives a better log message, not necessary otherwise + configuration.setBindingsDirectory(dataDir + "/bindings"); + configuration.setLargeMessagesDirectory(dataDir + "/largemessages"); + configuration.setPagingDirectory(dataDir + "/paging"); + } + + public static EmbeddedActiveMQ getEmbeddedServer() { + return embeddedServer; + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java index 20690cb83570..448543373fd1 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java @@ -608,6 +608,7 @@ public final synchronized void start() throws Exception { internalStart(); } catch (Throwable t) { ActiveMQServerLogger.LOGGER.failedToStartServer(t); + throw t; } finally { if (originalState == SERVER_STATE.STOPPED) { reloadNetworkHealthCheck(); diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/embedded/MainTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/embedded/MainTest.java new file mode 100644 index 000000000000..92e1d532ac25 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/embedded/MainTest.java @@ -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. + */ +package org.apache.activemq.artemis.core.server.embedded; + +import java.io.IOException; + +import org.junit.Test; + + +public class MainTest { + + @Test(expected = IOException.class) + public void testNull() throws Exception { + Main.main(new String[]{}); + } +} diff --git a/pom.xml b/pom.xml index 3786094e5448..e00a03528063 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,8 @@ artemis-features artemis-quorum-api artemis-quorum-ri + artemis-image + artemis-image/examples ActiveMQ Artemis Parent @@ -105,6 +107,7 @@ 2.10.0 5.1.8 3.1.2 + 3.3.2 1.39.0 9.2.1 5.2.0 @@ -1890,6 +1893,11 @@ + + com.google.cloud.tools + jib-maven-plugin + ${jib.maven.plugin.version} +