This is an automated email from the ASF dual-hosted git repository. gtully pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
The following commit(s) were added to refs/heads/main by this push: new 587ffb223c ARTEMIS-3042 simple properties based, extensible broker image 587ffb223c is described below commit 587ffb223cd2461a9da5c53e5ac189b9aa24640c Author: Gary Tully <gary.tu...@gmail.com> AuthorDate: Thu Jun 30 16:09:00 2022 +0100 ARTEMIS-3042 simple properties based, extensible broker image --- .../org/apache/activemq/artemis/cli/Artemis.java | 15 ++- .../org/apache/activemq/cli/test/ArtemisTest.java | 3 +- artemis-image/README.md | 57 ++++++++ artemis-image/TODO.md | 7 + artemis-image/examples/README.md | 55 ++++++++ .../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 +++ artemis-image/src/main/resources/log4j2.properties | 20 +++ .../artemis/ActiveMQImageExamplesTest.java | 87 ++++++++++++ .../artemis/jms/server/embedded/EmbeddedJMS.java | 2 +- .../core/server/embedded/EmbeddedActiveMQ.java | 7 +- .../artemis/core/server/embedded/Main.java | 148 +++++++++++++++++++++ .../core/server/impl/ActiveMQServerImpl.java | 1 + .../artemis/core/server/embedded/MainTest.java | 30 +++++ pom.xml | 8 ++ 20 files changed, 766 insertions(+), 11 deletions(-) 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 31bd72ffd6..4242e75526 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 class Artemis { 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 d534224b9c..6d1f994b12 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.BufferedReader; 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; @@ -2145,7 +2146,7 @@ public class ArtemisTest extends CliTestBase { // 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 0000000000..cb959c6213 --- /dev/null +++ b/artemis-image/README.md @@ -0,0 +1,57 @@ +###What is in the image + +An _empty_, _open_, _default_ broker with an 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 + - by default: has no configuration, it is dependent on the hard coded defaults of the embedded broker service + +###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. +The image wraps a plain java application based on a jre image. You should note the default `fromImage` property in the pom.xml and potentially override using `-DfromImage=<image url>` with your choice, as it may be out of date. + +To build the image, use: + + `$> 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: + + `$> podman image load --input target/jib-image.tar` + +To run the image detached* and rootless with port 61616 exposed to localhost by podman, use: + + `$> podman run --name=artemis -dp 61616:61616 localhost/target/activemq-artemis-image:<version>` + +The `:<version>` part of the image name is the maven ${project.version} from the pom.xml. You can use tab completion to have podman help you pick that exact container. + +> **Note that you can later stop the detached container with: `$> podman stop artemis`* + +Execute the artemis producer/consumer command line tools to interact with the broker. + + `$> ./bin/artemis producer --url tcp://localhost:61616` + + `$> ./bin/artemis consumer --url tcp://localhost:61616` + +###Intent + +The intent is that this image is useful as is. If one can trust users and is happy with configuration defaults, having no access control or limits can work fine. + +If more control is necessary 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 0000000000..4a07a03643 --- /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 0000000000..36e7078c45 --- /dev/null +++ b/artemis-image/examples/README.md @@ -0,0 +1,55 @@ +###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=<some value>` + +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: + + `$> 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:<version>` + +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://localhost:61616 --user A --password <some value>` + + `$> ./bin/artemis consumer --protocol amqp --url amqp://localhost:61616 --user A --password <some value>` + +####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: + + `$> podman run --name=artemis-byoc -dp 61616:61616 --privileged -v ./byoc__etc:/app/etc localhost/target/activemq-artemis-image:<version>` + +Peek at the broker logs to note the broker name 'byoc' configured from the broker.xml file + +`$> podman logs artemis-byoc + +Execute the artemis producer/consumer command line tools to validate, it behaves like the bare image: + + `$> ./bin/artemis producer --url tcp://localhost:61616` + + `$> ./bin/artemis consumer --url tcp://localhost: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 0000000000..59fc7b5691 --- /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>.keystore +# acceptorConfigurations.tcp.params.keyStorePassword=<password> + + +## 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 0000000000..536414f9f1 --- /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 0000000000..bff70b03f7 --- /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 0000000000..425624ed99 --- /dev/null +++ b/artemis-image/examples/byoc__etc/broker.xml @@ -0,0 +1,46 @@ +<?xml version='1.0'?> +<!-- +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. +--> +<configuration + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="urn:activemq" + xsi:schemaLocation="urn:activemq:core schema/artemis-configuration.xsd"> + <core xmlns="urn:activemq:core"> + + <!-- + Configure an image in the traditional way. + Providing boilerplate xml can form the well known configuration for an organisational base image that can be further extended with properties + --> + + <!-- set an example specific name, it appears in the started/stopped logging messages --> + <name>byoc</name> + <security-enabled>false</security-enabled> + + <!-- broker properties in the image are still in play so there will be an acceptor + configured from src/main/resources/acceptors.properties --> + + <addresses> + <address name="TEST"> + <anycast> + <queue name="TEST"/> + </anycast> + </address> + </addresses> + </core> +</configuration> \ 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 0000000000..dee58330f8 --- /dev/null +++ b/artemis-image/examples/pom.xml @@ -0,0 +1,77 @@ +<!-- + ~ 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.activemq</groupId> + <artifactId>artemis-pom</artifactId> + <version>2.29.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>artemis-image-examples</artifactId> + <name>Apache ActiveMQ Artemis Image Examples</name> + <packaging>pom</packaging> + + <properties> + <!-- for checkstyle plugin --> + <activemq.basedir>${project.basedir}/../..</activemq.basedir> + + <!-- for the locked down example user A is referenced in the amqp_sasl_scram_test__etc/role file, + so don't just change it here! --> + <example.user>A</example.user> + <!-- a password must be provided to generate the user credential data. + use: mvn exec:exec -Dexample.pwd=xyz on the command line to register your value in the example.user.file --> + <example.pwd></example.pwd> + <example.user.file>amqp_sasl_scram_test__etc/user</example.user.file> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-server</artifactId> + <version>${project.parent.version}</version> + </dependency> + </dependencies> + + + <build> + <plugins> + <!-- to easily create a single salted credential for our ${example.user.file}--> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>${exec-maven-plugin.version}</version> + <configuration> + <executable>java</executable> + <arguments> + <argument>-classpath</argument> + <classpath/> + <argument>org.apache.activemq.artemis.spi.core.security.jaas.SCRAMPropertiesLoginModule</argument> + <argument>${example.user}</argument> + <argument>${example.pwd}</argument> + </arguments> + <outputFile>${example.user.file}</outputFile> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/artemis-image/pom.xml b/artemis-image/pom.xml new file mode 100644 index 0000000000..f580745127 --- /dev/null +++ b/artemis-image/pom.xml @@ -0,0 +1,105 @@ +<!-- + ~ 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.activemq</groupId> + <artifactId>artemis-pom</artifactId> + <version>2.29.0-SNAPSHOT</version> + </parent> + + <artifactId>artemis-image</artifactId> + <name>Apache ActiveMQ Artemis Image</name> + + <properties> + + <!-- base jre image, reproducible build and single pull with @sha reference --> + <fromImage>eclipse-temurin:20-jre@sha256:5340605ada8bee017109147c838a96a24ecec037bedac5f157b26817ab633e02</fromImage> + + <!-- for checkstyle plugin --> + <activemq.basedir>${project.basedir}/..</activemq.basedir> + <!-- none of the surefire project defaults are relevant to the tests here --> + <activemq-surefire-argline></activemq-surefire-argline> + + </properties> + + + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>apache-artemis</artifactId> + <version>${project.version}</version> + <type>pom</type> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <!-- The johnzon-core and json-api contents are repackaged in -commons, + However maven can still need them during tests that don't see the shaded bits during build --> + <dependency> + <groupId>org.apache.johnzon</groupId> + <artifactId>johnzon-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>jakarta.json</groupId> + <artifactId>jakarta.json-api</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.google.cloud.tools</groupId> + <artifactId>jib-maven-plugin</artifactId> + <configuration> + <from> + <image>${fromImage}</image> + </from> + <to> + <image>target/activemq-artemis-image:${project.version}</image> + </to> + <container> + <mainClass>org.apache.activemq.artemis.core.server.embedded.Main</mainClass> + <ports> + <port>61616</port> + </ports> + <format>OCI</format> + </container> + </configuration> + <executions> + <execution> + <id>now</id> + <!-- build from command line with: mvn jib:buildTar@now --> + <phase>none</phase> + <goals> + <goal>buildTar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> 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 0000000000..526af5e590 --- /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 0000000000..a0296fb6b0 --- /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 0000000000..4a205845c1 --- /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 4904242c7b..150988317e 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 class EmbeddedJMS extends EmbeddedActiveMQ { @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 197134dd04..26190d68d0 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 class EmbeddedActiveMQ { } 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 0000000000..cf3517d308 --- /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 b01a3ca759..425069f49c 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 class ActiveMQServerImpl implements ActiveMQServer { 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 0000000000..92e1d532ac --- /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 dbaf2b603e..1d45a24f9a 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,8 @@ <module>artemis-features</module> <module>artemis-quorum-api</module> <module>artemis-quorum-ri</module> + <module>artemis-image</module> + <module>artemis-image/examples</module> </modules> <name>ActiveMQ Artemis Parent</name> @@ -105,6 +107,7 @@ <errorprone.version>2.10.0</errorprone.version> <maven.bundle.plugin.version>5.1.9</maven.bundle.plugin.version> <maven.checkstyle.plugin.version>3.1.2</maven.checkstyle.plugin.version> + <jib.maven.plugin.version>3.3.2</jib.maven.plugin.version> <sevntu.checks.version>1.39.0</sevntu.checks.version> <checkstyle.version>9.2.1</checkstyle.version> <mockito.version>5.2.0</mockito.version> @@ -1892,6 +1895,11 @@ </artifactItems> </configuration> </plugin> + <plugin> + <groupId>com.google.cloud.tools</groupId> + <artifactId>jib-maven-plugin</artifactId> + <version>${jib.maven.plugin.version}</version> + </plugin> </plugins> </build>