I have tried to follow the instructions to get drop wizard AspectJ metrics working, see https://github.com/astefanutti/metrics-aspectj however when I follow these simple instructions and view the metrics through JConsole mbeans there are no metrics reported for my annotated service, just metrics for the resource as is the default drop wizard behaviour.
I am using an old version of drop wizard so that there are no jar dependancy clashes with com.codahale.metrics artefacts
When the application is built hitting the following url produces a random string http://localhost:8080/string
Here is my simple example code:
pom.xml
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>test-project</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>test project</name>
<description>try and get aspect j metrics working in dropwizard following instructions on https://github.com/astefanutti/metrics-aspectj</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>0.7.1</version>
</dependency>
<dependency>
<groupId>io.astefanutti.metrics.aspectj</groupId>
<artifactId>metrics-aspectj</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-annotation</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
<!--
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>io.astefanutti.metrics.aspectj</groupId>
<artifactId>metrics-aspectj</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Premain-Class>org.aspectj.weaver.loadtime.Agent</Premain-Class>
<Main-Class>com.example.MyApplication</Main-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
MyApplication.java
package com.example;
import com.example.service.MyService;
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
public class MyApplication extends Application<MyConfiguration> {
public static void main(String[] args) throws Exception {
MyApplication app = new MyApplication();
app.run(args);
}
#Override
public void initialize(Bootstrap<MyConfiguration> arg0) {
}
#Override
public void run(MyConfiguration configuration, Environment environment) throws Exception {
environment.jersey().register(new MyResource(new MyService()));
}
}
MyConfiguration.java
package com.example;
import io.dropwizard.Configuration;
public class MyConfiguration extends Configuration {
}
MyResource.java
package com.example;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.codahale.metrics.annotation.Timed;
import com.example.service.MyService;
#Path("/string")
#Produces(MediaType.APPLICATION_JSON)
public class MyResource {
private final MyService service;
public MyResource(MyService service) {
this.service = service;
}
#GET
#Timed
public String getString() {
return "string is [" + this.service.constructString() + "]";
}
}
MyService.java
package com.example.service;
import java.util.Random;
import com.codahale.metrics.annotation.Timed;
import io.astefanutti.metrics.aspectj.Metrics;
#Metrics(registry = "myRegistry")
public class MyService {
private final Random random = new Random();
#Timed(name = "myTimedMethod")
public String constructString() {
byte[] bytes = new byte[20];
this.random.nextBytes(bytes);
return new String(bytes);
}
}
example.yml
logging:
level: INFO
I build the shaded jar on the command line with standard maven command:
mvn clean install
And run the jar with the following command:
java -jar -javaagent:<path-to-maven-repo>/org/aspectj/aspectjweaver/1.8.7/aspectjweaver-1.8.7.jar target/test-project-0.0.1-SNAPSHOT.jar server example.yml
I am not quite sure what I am missing but a day spent searching didn't yield any useful insights.
So it turns out that what you have to do is register your MetricRegistry with a JmxReporter for the metrics to come out on JMX.
It seems that in the latest version of drop wizard you should be able to create a MetricRegistry and add this to the bootstrap metrics in the initialize method of your application
#Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
MetricRegistry myRegistry = SharedMetricRegistries.getOrCreate("myRegistry");
MetricRegistry bootstrapMetricRegistry = bootstrap.getMetricRegistry();
bootstrapMetricRegistry.register("myRegistry", myRegistry);
}
and then when this gets automatically put in a JMX reporter in the registerMetrics method of the bootstrap, however this doesn't seem to work.
My current solution is as follows, in the run method of the application grab the registry from the shared registry and build another JmxReporter. Metrics happily now appear when connecting through JMX.
#Override
public void run(MyConfiguration configuration, Environment environment) throws Exception {
...
JmxReporter.forRegistry(SharedMetricRegistries.getOrCreate("myRegistry")).build().start();
...
}
Related
I wanted to use AOP-styled annotations for #Around advice in a non-Spring project. I was able to make up some code, but it's been giving me trouble - instead of seeing the console output as coded in the Advice I can only see the SampleClass method's printout.
Providing a minimal, reproductible example of my code. Hoping to get some hint on how to get it working. Thanks!
Main.java
package pl.bart;
public class Main {
public static void main(String[] args) {
System.out.println(new SampleClass().a());
}
}
SampleClass.java
package pl.bart;
public class SampleClass {
#Annotation
public String a() {
return "Hello from sample class";
}
}
Annotation.java
package pl.bart;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Annotation {
}
Advice.java
package pl.bart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class Advice {
#Pointcut("#annotation(Annotation)")
public void callAt() {
}
#Around("callAt()")
public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Hello from advice");
return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
}
}
META-INF/aop.xml
<aspectj>
<aspects>
<aspect name="pl.bart.Advice"/>
<weaver options="-verbose -showWeaveInfo">
<include within="pl.bart.*"/>
</weaver>
</aspects>
</aspectj>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.bart</groupId>
<artifactId>aspectj-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>
-javaagent:"${settings.localRepository}"/org/aspectj/
aspectjweaver/1.8.9/
aspectjweaver-1.8.9.jar
</argLine>
<useSystemClassLoader>true</useSystemClassLoader>
<forkMode>always</forkMode>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
The are three mistakes in you project.
1.
#Pointcut("#annotation(Annotation)")
public void callAt() {
}
use fully qualified names of classes, i.e. pl.bart.Annotation instead of Annotation - it costs you nothing but saves a lot of time, in particular aspectj 1.5.4 "does not support" simple names.
2.
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
aspectj does some magic with bytecode, and it is too naive to expect the library released 12 years ago supports java 17, switch to the recent aspectj 1.9.9, also note you need to add --add-opens java.base/java.lang=ALL-UNNAMED to JVM arguments.
3.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
if you are going to use load time weaving and you don't use native aspectJ (.aj) syntax, you do not need aspectj-maven-plugin - it compiles .aj files and performs compile time weaving.
Is there a way to obfuscate exploded/packaged output of jib-maven-plugin with yGuard (or some other obfuscator)?
I can think of a way using other tools such as exec-maven-plugin + jib cli.
Another possible way can be to devise a 3rd party jib-extension or even fork/hack jib-maven-plugin all together.
Maybe someone can share their experience with that.
For context I am trying to ship a Spring Boot application build using Maven and AntRun for yGuard.
I managed to figure it out myself.
Here is my exec-maven-plug + jib cli solution for anyone out there.
To test paste it, adapt it to your environment, and run mvn clean package -P local.
This pom.xml is from a multi-module setup, so you may have to refactor or omit the <parent></parent> tag to suit you needs.
What it does:
Cleans target directory
Compiles your source files into the target directory
Obfuscates classes in the target directory
Repackages classes, libs, resources in exploded form (for better layer caching) correctly into Spring Boot's non-standard BOOT-INF output directory
Performs docker build, docker push under the hood through jib cli
Performs docker pull at the end (for debug purposes)
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>redacted</artifactId>
<groupId>com.redacted</groupId>
<version>0.0.1</version>
</parent>
<artifactId>sm-test</artifactId>
<version>0.0.1</version>
<name>test</name>
<description>test</description>
<packaging>jar</packaging>
<properties>
<profile.name>default</profile.name>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<mainClass>com.redacted.smtest.SmTestApplication</mainClass>
<java.server.user>0:0</java.server.user>
<java.to.image.tag>ghcr.io/redacted/${project.artifactId}:${project.version}-${profile.name}</java.to.image.tag>
<java.from.image.tag>openjdk:11.0.14-jre#sha256:e2e90ec68d3eee5a526603a3160de353a178c80b05926f83d2f77db1d3440826</java.from.image.tag>
<java.from.classpath>../target/${project.name}/${profile.name}/${project.build.finalName}.jar</java.from.classpath>
</properties>
<profiles>
<!-- local -->
<profile>
<id>local</id>
<properties>
<profile.name>local</profile.name>
</properties>
<activation>
<property>
<name>noTest</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>jib jar</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>jib</executable>
<workingDirectory>.</workingDirectory>
<skip>false</skip>
<arguments>
<argument>jar</argument>
<argument>--mode=exploded</argument>
<argument>--target=${java.to.image.tag}</argument>
<argument>--from=${java.from.image.tag}</argument>
<argument>--user=${java.server.user}</argument>
<argument>--creation-time=${maven.build.timestamp}</argument>
<argument>--jvm-flags=-Xms32m,-Xmx128m,-Dspring.profiles.active=default</argument>
<argument>${java.from.classpath}</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>docker pull</id>
<phase>install</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>docker</executable>
<workingDirectory>.</workingDirectory>
<skip>false</skip>
<arguments>
<argument>pull</argument>
<argument>${java.to.image.tag}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.yworks</groupId>
<artifactId>yguard</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<directory>../target/${project.name}/${profile.name}/</directory>
<outputDirectory>../target/${project.name}/${profile.name}/classes</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>obfuscate</id>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<skip>false</skip>
<tasks>
<property name="runtime_classpath" refid="maven.runtime.classpath"/>
<!--suppress UnresolvedMavenProperty -->
<taskdef name="yguard" classname="com.yworks.yguard.YGuardTask" classpath="${runtime_classpath}"/>
<mkdir dir="${project.build.directory}/obfuscated"/>
<yguard>
<inoutpair in="${project.build.directory}/classes"
out="${project.build.directory}/obfuscated"/>
<externalclasses>
<!--suppress UnresolvedMavenProperty -->
<pathelement path="${runtime_classpath}"/>
</externalclasses>
<rename mainclass="${mainClass}"
logfile="${project.build.directory}/rename.log.xml"
scramble="true"
replaceClassNameStrings="true">
<property name="error-checking" value="pedantic"/>
<property name="naming-scheme" value="best"/>
<property name="language-conformity" value="compatible"/>
<property name="overload-enabled" value="true"/>
<!-- Generated by sm-test -->
<map>
<class map="d1e6064d$5a15$449b$a632$b2d967a61021" name="com.redacted.smtest.YGuardMappingRunner"/>
</map>
</rename>
</yguard>
<delete dir="${project.build.directory}/classes/com"/>
<copy todir="${project.build.directory}/classes/com/" overwrite="true">
<fileset dir="${project.build.directory}/obfuscated/com/" includes="**"/>
</copy>
<delete dir="${project.build.directory}/obfuscated"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${mainClass}</mainClass>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Here is an associate jib.yaml file for jib cli to work:
apiVersion: jib/v1alpha1
kind: BuildFile
workingDirectory: "/app"
entrypoint: ["java","-cp","/app/resources:/app/classes:/app/libs/*"]
layers:
entries:
- name: classes
files:
- properties:
filePermissions: 755
src: /classes
dest: /app/classes
I also had to write a small throw-away class to generate custom mapping for unique class and package names for yGuard (apparently it cannot manage to do it on its own):
package com.redacted.smtest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
#Slf4j
#Component
public class YGuardMappingRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
if (args == null || args.length == 0)
return;
generateYGuardMapping(Path.of(args[0]));
}
void generateYGuardMapping(Path path) throws IOException {
var packageSb = new StringBuilder();
var classSb = new StringBuilder();
mapPackages(path, packageSb);
mapClasses(path, classSb);
if (packageSb.length() > 0 && classSb.length() > 0)
log.info(
"\n<!-- Generated by sm-test -->\n<map>\n{}\n{}</map>",
packageSb.toString(),
classSb.toString());
else if (packageSb.length() > 0 && classSb.length() == 0)
log.info("\n<!-- Generated by sm-test -->\n<map>\n{}</map>", packageSb.toString());
else if (packageSb.length() == 0 && classSb.length() > 0)
log.info("\n<!-- Generated by sm-test -->\n<map>\n{}</map>", classSb.toString());
}
private void mapClasses(Path path, StringBuilder classSb) throws IOException {
try (var stream = Files.walk(path, Integer.MAX_VALUE)) {
stream
.distinct()
.filter(o -> o.getNameCount() >= 12)
.filter(Files::isRegularFile)
.map(o -> o.subpath(8, o.getNameCount()))
.map(o -> o.toString().replace("\\", ".").replace(".java", ""))
.filter(o -> !o.contains("Sm"))
.sorted()
.forEach(
o ->
classSb.append(
String.format("%2s<class map=\"%s\" name=\"%s\"/>%n", "", getRandStr(), o)));
}
}
private void mapPackages(Path path, StringBuilder packageSb) throws IOException {
try (var stream = Files.walk(path, Integer.MAX_VALUE)) {
stream
.map(Path::getParent)
.distinct()
.filter(o -> o.getNameCount() >= 12)
.map(o -> o.subpath(8, o.getNameCount()))
.map(o -> o.toString().replace("\\", "."))
.sorted()
.forEach(
o ->
packageSb.append(
String.format(
"%2s<package map=\"%s\" name=\"%s\"/>%n", "", getRandStr(), o)));
}
}
private String getRandStr() {
return UUID.randomUUID().toString().replaceAll("[-]+", "\\$");
}
}
This might be a weird question but I am curious about this:
I want to create a Java EE Web Project, that I can package into a JAR file (<packaging>jar</packaging> instead of <packaging>war</packaging>).
In other words, I want to include the Java EE web server inside a JAR (built by maven).
Inside the WebServer I want to use Servlets like I can use them when I package it to a WAR file but without requireing the devices that execute the JAR to have a Web server installed where they can deploy my JAR.
I want something like an executeable JAR that contains the server and runs it without the need to install something else.
Is there a (ideally light-weight) server that works within a JAR file or any other possibility to create a JAR file like this?
If you want to use vanilla Java EE, you can use an Embedded Jetty Server or Embedded Tomcat Server:
Here is an example with Embedded Tomcat and Maven:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.company</groupId>
<artifactId>app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>12</maven.compiler.source>
<maven.compiler.target>12</maven.compiler.target>
<servlet.version>3.1.0</servlet.version>
<jsf.version>2.2.19</jsf.version>
<tomcat.version>9.0.21</tomcat.version>
</properties>
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>${jsf.version}</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>${jsf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>12</source>
<target>12</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>app-${project.version}</finalName>
<archive>
<manifest>
<mainClass>org.company.app.Application</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
mainClass:
package org.company.app;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import java.io.File;
public class Application {
public static void main(final String[] args) throws Exception {
final String webappPath = new File("src/main/webapp").getAbsolutePath();
final Tomcat tomcat = new Tomcat();
final StandardContext ctx = (StandardContext) tomcat.addWebapp("/", webappPath);
System.out.println(ctx
);
// Declare an alternative location for your "WEB-INF/classes" dir
// Servlet 3.0 annotation will work
final String targetClassesPath = new File("target/classes").getAbsolutePath();
final WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(//
resources, "/WEB-INF/classes", //
targetClassesPath, "/"));
ctx.setResources(resources);
tomcat.start();
tomcat.getServer().await();
}
}
and the rest as usual java ee development
I'm using Maven and Vert.x to create an application.
Everything is working fine while I run it in my IDE (IntelliJ) but I can't make it work using command line.
I have a Launcher class that deploy some verticles but the problem is the same with all my verticles.
So far here is what I've tried:
vertx run Launcher.java
vertx run com.packagename.Launcher.java
// user-content-service-0.1.jar create via: mvn clean package
run com.packagename.Launcher.java -cp target\user-content-service-0.1.jar
As error I get this:
.../path/Launcher.java:8: error: cannot find symbol
private static final Logger logger = logManager.getLogger(Launcher.class);
symbol: variable LogManager
location: class com.packagename.Launcher
java.lang.RuntimeException: Compilation failed
...
The issue seems to come from the fact that the compiler is not able to find dependancies
Here is what my pom.xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.packagename</groupId>
<artifactId>user-content-service</artifactId>
<packaging>jar</packaging>
<version>0.1</version>
<name>Project - user-content-service</name>
<url>http://maven.apache.org</url>
<properties>
<vertx.version>[3.5.0,3.6)</vertx.version>
<java.version>1.8</java.version>
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
<log4j.version>[2.10.0,2.11)</log4j.version>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<version>${vertx.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-config</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>guru.nidi.raml</groupId>
<artifactId>raml-tester</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
And that's my Launcher.java file:
package com.packagename;
import io.vertx.core.AbstractVerticle; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger;
public class Launcher extends AbstractVerticle {
private static final Logger logger = LogManager.getLogger(Launcher.class);
#Override
public void start() {
vertx.deployVerticle("com.packagename.DynamoDBVerticle", logRes -> {
if (logRes.succeeded()) {
vertx.deployVerticle("com.packagename.UploaderVerticle", uploaderRes -> {
if (uploaderRes.succeeded()) {
vertx.deployVerticle("com.packagename.ServerVerticle", serverRes -> {
if (!serverRes.succeeded()) {
logger.error("Could not start server");
}
});
}
else {
logger.error("Could not start uploader");
}
});
}
else {
logger.error("Could not start Dynamo");
}
});
} }
Any clue what could I be doing wrong here ?
Thanks !
You are having a classpath issue; because you package your jarfile with maven-jar in the package phase, you are only getting that in the build directory; if you want to run on the command-line with java -jar, you should create a fat-jar (aka uber-jar, a jar file specifically packaged in order for it to contain your classes and all dependencies declared in the pom file).
You can add the maven-shade-plugin to do that in the package phase, ie:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>io.vertx.core.Launcher</Main-Class>
<Main-Verticle>com.packagename.Launcher</Main-Verticle>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource>
</transformer>
</transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>
Note: you have a class name clash between the vert.x Launcher (used as Main-Class, in the fat-jar), and your own Launcher (which is actually a Verticle): I'd suggest to rename it.
After maven package, you will be then able to do:
java -jar target/user-content-service-0.1-fat.jar
Check in the vert.x samples for more information.
Is it possible in Java to set up a default listener called when any object is created ?
Something like :
public static void main(String[] args) {
setInstanceListener(listener); // this method doesn't exists
MyObject obj = new MyObject(); // the new keyword now call listener()
OtherObject oo = new OtherObject(); // same here
}
public static void listener(Object newObject) {
// do something with the created object
}
If your are ready to use some framework as AspectJ, it's quite straightforward.
Herewith I'll give an example using Maven.
First the pom:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dummy.listener</groupId>
<artifactId>dummy.listener</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>
Is it possible in java to listen all objects creation?
http://stackoverflow.com/questions/34839701/is-it-possible-in-java-to-listen-all-objects-creation
</description>
<properties>
<main.class>dummy.listener.MainApp</main.class>
<jdk.version>1.7</jdk.version>
<project.encoding>UTF-8</project.encoding>
<project.build.sourceEncoding>${project.encoding}</project.build.sourceEncoding>
<project.reporting.outputEncoding>${project.encoding}</project.reporting.outputEncoding>
<maven.compiler.source>${jdk.version}</maven.compiler.source>
<maven.compiler.target>${jdk.version}</maven.compiler.target>
<maven.compiler.compilerVersion>${jdk.version}</maven.compiler.compilerVersion>
<maven.compiler.fork>true</maven.compiler.fork>
<maven.compiler.verbose>true</maven.compiler.verbose>
<maven.compiler.optimize>true</maven.compiler.optimize>
<maven.compiler.debug>true</maven.compiler.debug>
<maven.jar.plugin.version>2.6</maven.jar.plugin.version>
<maven.antrun.plugin.version>1.8</maven.antrun.plugin.version>
<aspectj.maven.plugin.version>1.8</aspectj.maven.plugin.version>
<aspectj.version>1.8.7</aspectj.version>
<slf4j.version>1.7.13</slf4j.version>
</properties>
<dependencies>
<!-- compile time weaving -->
<!-- required to avoid warning from aspectj-maven-plugin,
even if aspectjweaver is also a dependency -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- aspectjrt is only a subset of aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>${aspectj.maven.plugin.version}</version>
<configuration>
<complianceLevel>${jdk.version}</complianceLevel>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>class-antrun</id>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>${maven.antrun.plugin.version}</version>
<configuration>
<target>
<java fork="true" classname="${main.class}">
<classpath refid="maven.compile.classpath" />
</java>
</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>only-under-eclipse</id>
<activation>
<property>
<name>m2e.version</name>
</property>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<versionRange>[${aspectj.maven.plugin.version},)</versionRange>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>
Then the main class:
package dummy.listener;
import dummy.listener.model.MyObject1;
import dummy.listener.model.MyObject2;
public class MainApp {
public static void main(final String[] args) {
new MyObject1();
new MyObject2();
}
}
The weaving job is done by:
package dummy.listener;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dummy.listener.model.MyObject1;
import dummy.listener.model.MyObject2;
#Aspect
public class AspectjDemo
{
private final Logger log = LoggerFactory.getLogger(AspectjDemo.class);
#AfterReturning(pointcut="call(dummy.listener.model.*.new(..))", returning="result")
public void doSomethingAfterNewModelCreation(JoinPoint joinPoint , Object result) {
log.info("[AspectJ] Log after creation of " + result.getClass());
if (/*result instanceof MyObject1*/ MyObject1.class.isAssignableFrom(result.getClass())) {
MyObject1 mo = (MyObject1) result;
log.info("Name: " + mo.getName());
} else if (/*result instanceof MyObject2*/ MyObject2.class.isAssignableFrom(result.getClass())) {
MyObject2 mo = (MyObject2) result;
log.info("Location: " + mo.getLocation());
}
}
}
Now add some guinea pig classes:
package dummy.listener.model;
public class MyObject1 {
public String getName() {
return "myObject1 name";
}
}
package dummy.listener.model;
public class MyObject2 {
public String getLocation() {
return "myObject2 location";
}
}
To finish, compile and launch the main class with Maven:
mvn clean compile antrun:run -Pclass-antrun
You should have something like:
[...]
[INFO] --- aspectj-maven-plugin:1.8:compile (default) # dummy.listener ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
[INFO] Join point 'constructor-call(void dummy.listener.model.MyObject1.<init>())' in Type 'dummy.listener.MainApp' (MainApp.java:8) advised by afterReturning advice from 'dummy.listener.AspectjDemo' (AspectjDemo.java:18)
[INFO] Join point 'constructor-call(void dummy.listener.model.MyObject2.<init>())' in Type 'dummy.listener.MainApp' (MainApp.java:9) advised by afterReturning advice from 'dummy.listener.AspectjDemo' (AspectjDemo.java:18)
[INFO]
[INFO] --- maven-antrun-plugin:1.8:run (default-cli) # dummy.listener ---
[INFO] Executing tasks
main:
[java] [main] INFO dummy.listener.AspectjDemo - [AspectJ] Log after creation of class dummy.listener.model.MyObject1
[java] [main] INFO dummy.listener.AspectjDemo - Name: myObject1 name
[java] [main] INFO dummy.listener.AspectjDemo - [AspectJ] Log after creation of class dummy.listener.model.MyObject2
[java] [main] INFO dummy.listener.AspectjDemo - Location: myObject2 location
[INFO] Executed tasks
[...]
That's it. Hope it'll help.