Maven Jetty plugin, how to hot deploy code changs in dependent projects? - eclipse

I have a war project that depends on another jar project in the same directory.
Now I want to start the war project in jetty with maven, and want it to hot swap changes in the dependent project so that the chagnes take effect immediately. My plugin config looks like this:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<webAppSourceDirectory>${project.basedir}/src/main/resources</webAppSourceDirectory>
<classesDirectory>${project.basedir}/src/main/java</classesDirectory>
<webApp>
<contextPath>/</contextPath>
<descriptor>${basedir}/src/main/resources/WEB-INF/web.xml</descriptor>
</webApp>
</configuration>
</plugin>
How can i do it please? any pointer is appreciated.

First thing to point out...
You don't want to do this, as JAR reloading is poorly supported by Java itself, it will tend to want to cache the JAR and its contents on first use (standard behavior of the URLClassLoader). Replacing the JAR at runtime requires extra work, and sometimes even that is not possible without incurring some sort of memory leak (see the the various Leak Preventers that Jetty ships with for some hidden nasties in this area). Be prepared for OOM or other issues if you rely on this.
Now, with that out of the way ...
Hot Deployment on Jetty works by detecting/scanning for changes in the webapp and performing a webapp restart.
This works great with content, like html, js, css, etc.
Project JAR dependencies, which will wind up as part of the WEB-INF/lib structure, are part of the Jetty WebAppContext's [WebAppClassLoader].
This WebAppClassLoader is populated from information provided by Maven's artifact resolution system, which on a normal command line build resolves to content via the Maven local repository system. If you are using maven from Eclipse, the resolution of these artifacts might be other projects in your Eclipse workspace (more on this later)
To demonstrate, this is the minimal configuration for scanning for changes every 2 seconds on your webapp.
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.1.0.v20131115</version>
<configuration>
<scanIntervalSeconds>2</scanIntervalSeconds>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
Lets say you have a dependant project, a jar file under the maven artifact coordinates of com.company:component:jar:1.0-SNAPSHOT.
That means the WebAppContext was loaded with a WebAppClassLoader reference to a URL of file://${user.dir}/.m2/repository/com/company/component/1.0-SNAPSHOT/component-1.0-SNAPSHOT.jar and only changes to that file will cause a hot deploy / reload.
Its simple enough on a command line in your /component/ directory to just issue a mvn clean install and see your webapp reload, but under Eclipse and its m2e plugin, a code change to the component project does not automatically represent a change to the jar file in your local repository.
In fact, m2e can be configured to swap out maven artifact references via the local repository for ones in your eclipse workspace. You need to be aware of theses settings to have success with your hot deploy / reload efforts.
Tip for figuring out your active WebAppClassloader contents
Add the following file to your war project.
src/main/jetty/dump.xml
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
"http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Set name="dumpAfterStart">true</Set>
</Configure>
And change your jetty-maven-plugin configuration to add ...
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<configuration>
...
<jettyXml>src/main/jetty/dump.xml</jettyXml>
</configuration>
</plugin>
Now when your server starts, look for the extra output, particularly the WebAppClassLoader references.
...(snip)...
| | |
| | +> WebAppClassLoader=Web App Live#39714203
| | | +- file:/home/joakim/code/stackoverflow/samples/webapp-live/target/classes/
| | | +- file:/home/joakim/.m2/repository/org/eclipse/jetty/demo/component/1.0-SNAPSHOT/component-1.0-SNAPSHOT.jar
| | | +- ClassRealm[plugin>org.eclipse.jetty:jetty-maven-plugin:9.1.0.v20131115, parent: sun.misc.Launcher$AppClassLoader#77fe0d66]
...(/snip)...
You can see that my jetty:run has a reference to the component via the local repository.

Related

Eclipse searching for the wrong surefire dependency

On building my workspace, my Java 6 Maven project is marked with an error (a Maven problem):
Could not calculate build plan: The repository system is offline but the artifact org.apache.maven.surefire:surefire:pom:2.7.1 is not available in the local repository.
What strikes me as odd is that it is searching for org.apache.maven.surefire:surefire while the true dependency is org.apache.maven.surefire:maven-surefire-plugin.
My effective pom is showing:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.1</version>
<executions>
I'm using Eclipse Indigo with the m2eclipse plugin. And it compiles correctly when running any Maven goal. I tried cleaning the project, reimporting it, clearing the .metadata file.
Where does this behavior come from? Thanks
The mentioned dependency is the parent project for the maven-surefire-plugin and should usually not given directly only via the maven-surefire-plugin itself.
Furthermore What strikes me as odd is that it is searching for org.apache.maven.surefire:surefire while the true dependency is org.apache.maven.surefire:maven-surefire-plugin. which is simply wrong, cause the correct groupId and artifactId for the maven-surefire-plugin is:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version>
...
</plugin>
It could be possible having problems while accessing maven central. Apart from the above you should update the maven-surefire-plugin cause the current up-to-date version is 2.15.

Eclipse error with osgi + maven + maven-pax-plugin

I am trying to create an OSGi bundle and integrate it into eclipse. I am using the maven-pax-plugin to create the bundles. These are the steps I follow
I create an osgi project using pax
mvn org.ops4j:maven-pax-plugin:create-project -DgroupId=org.sonatype.mcookbook -DartifactId=osgi-project -Dversion=1.0-SNAPSHOT
and then create a bundle
mvn pax:create-bundle -Dpackage=org.sonatype.mcookbook -Dname=osgi-bundle -Dversion=1.0-SNAPSHOT
and then try to import the maven project into eclipse (file/import/existing maven project) the bundle project created in the second step always gives me this error
maven-pax-plugin:1.5:compile (1 error)
Execution default-compile, in org.sonatype.mcookbook/pom.xml
maven-pax-plugin:1.5:testCompile (1 error)
Execution default-testCompile, in org.sonatype.mcookbook/pom.xml
When I select one of the errors the description says
No marketplace entries found to handle Execution default-compile, in org.sonatype.mcookbook/pom.xml in Eclipse. Please see Help for more information.
If i ignore the error and import the project anyway this is what eclipse complains about
Plugin execution not covered by lifecycle configuration: org.ops4j:maven-pax-plugin:1.5:compile (execution: default-compile, phase: compile)
Has anyone seen this? any ideas how to fix it?
I am following this tutorial but adding integration with eclipse. Note however that if I build it with maven and don't use eclipse at all it all works fine, the problem is in eclipse/m2e
I am using Eclipse Indigo SR2 and m2e 1.0.200
I get rid of this problem by following the comment in the generated POM and move the <extensions>true</extensions> down to the maven-bundle-plugin below giving:
...
<plugins>
<plugin>
<groupId>org.ops4j</groupId>
<artifactId>maven-pax-plugin</artifactId>
<version>1.4</version>
<!--
| enable improved OSGi compilation support for the bundle life-cycle.
| to switch back to the standard bundle life-cycle, move this setting
| down to the maven-bundle-plugin section
-->
<!-- WAS HERE -->
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.4.3</version>
<!--
| the following instructions build a simple set of public/private
| classes into an OSGi bundle
-->
<extensions>true</extensions> <!-- MOVED HERE :-) -->
<configuration>
...
Then update the project (Right click on project name in Project Explorer: Maven -> Update Project...), wait for the build to complete and the error is gone.
Hope that helps!
The new m2eclipse versions require that every plugin that affects the build is supported using a m2eclipse plugin. So the maven-pax-plugin is not yet supported. As this basically happens with most maven plugins out there I still use the old m2eclipse version.
Unfortunately the old version 0.12 download seems to have been removed recently. So probably you will have to wait till maven-pax-plugin is supported.

Exclude directory from src/main/webapp from copying to target/app-SNAPSHOT directory

I have project which builds .war file with maven. There is standard src/main/webapp directory. I have also some GWT code there which is compiled by GWT maven plugin. Sometimes I run GWT application from Eclipse, then GWT Eclipse plugin compiles it to src/main/webapp/app.policy (app.policy is name of my GWT module). This is OK.
app
|-src/main/webapp
| |-app.policy (directory created by Eclipse GWT plugin)
|target
|-app-1.0-SNAPSHOT (directory created by Maven while building war)
| |-app.policy
|-app-1.0-SNAPSHOT.war
If src/main/webapp/app.policy does not exist, gwt-maven-plugin creates target/app-1.0-SNAPSHOT/app.policy and it is included in WAR. This is desired behavior.
The problem is if src/main/webapp/app.policy exists. Then it is copied to target/app-1.0-SNAPSHOT/app.policy and is not fully overwritten by what gwt-maven-plugin creates. Can I somehow exclude src/main/webapp/app.policy from being copied to target/app-1.0-SNAPSHOT directory?
I tried <warSourceExcludes>, but it doesn't work. It makes app.policy not go into .war, which is not what I want. I want it in .war, but I want it to be created by maven-gwt-plugin, not copied from src/main/webapp.
Try to exclude the file from maven resources:
<build>
...
<resources>
<resource>
<directory>src/main/webapp</directory>
<excludes>
<exclude>app.policy</exclude>
</excludes>
</resource>
...
</resources>
...
</build>
If this works then you can add this configuration to a maven profile.

GWT Maven and web.xml

I'm using the GWT Maven plugin from Codehaus with m2eclipse. Where is my web.xml file supposed to end up? Isn't the Maven build supposed to copy it to the /war directory? I can't see it there. Or does Jetty pick it up automatically from src/main/webapp/WEB-INF/?
Here's a relevant section from my pom.xml.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<warSourceDirectory>war</warSourceDirectory>
<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
</configuration>
</plugin>
I believe web.xml (and everything else under src/main/webapp/) gets copied into target/<projectname>-<version>/ during the normal maven lifecycle (For example, when you run mvn install).
If you're running any of the gwt-maven plugin goals, then check out this link.
When running gwt:run, if you want to run the full web app just as if you have built and deployed a war, I found the best way is to add the following to the configuration for the gwt-maven plugin:
<hostedWebapp>
${project.build.directory}/${project.build.finalName}
</hostedWebapp>
This tells gwt-maven plugin to look for the web.xml (and all the other parts of the war file) under target/<projectname>-<version>/. So make sure to either run mvn install first (or mvn war:exploded), then run mvn gwt:run and you should be set.

Why does the maven eclipse plugin break the maven jetty plugin?

I would like to have the maven eclipse plugin regenerate my .classpath whenever a build is run, and I did so by using the following configuration:
<!--
Generate a new .classpath each time the build is run, but don't try
to download sources or javadocs
-->
<profile>
<id>elipse-update</id>
<activation>
<file>
<exists>.classpath</exists>
</file>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>eclipse</goal>
</goals>
<configuration>
<downloadSources>false</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
For some reason, this causes the maven jetty plugin to fail with ClassNotFoundException errors (it complains about all sorts of Spring classes not being there). Of course, it worked without a hit when I didn't have the maven eclipse plugin installed. Here's an example of what I'm talking about:
$ mvn jetty:run
[INFO] Scanning for projects...
...
[INFO] Starting jetty 6.1.22 ...
2010-02-11 20:53:08.984:INFO::jetty-6.1.22
2010-02-11 20:53:09.109:WARN::Could not instantiate listener org.springframework.web.context.ContextLoaderListener
java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at org.codehaus.classworlds.RealmClassLoader.loadClassDirect(RealmClassLoader.java:195)
at org.codehaus.classworlds.DefaultClassRealm.loadClass(DefaultClassRealm.java:255)
at org.codehaus.classworlds.DefaultClassRealm.loadClass(DefaultClassRealm.java:274)
at org.codehaus.classworlds.RealmClassLoader.loadClass(RealmClassLoader.java:214)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
Of course, if I delete that eclipse plugin section, I can run jetty as expected:
$ mvn jetty:run
[INFO] Scanning for projects...
...
Feb 11, 2010 8:55:28 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 1672 ms
2010-02-11 20:55:28.687:INFO::Started SelectChannelConnector#0.0.0.0:8080
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 5 seconds.
Some things I know:
Yes, I only activate this profile if the .classpath is present. This seems counter-intuitive, but I have a reason: I have another profile that activates when the .classpath is absent which runs the eclipse plugin with the options for downloading source code and javadocs set to true. I don't want this to happen each build, so I created a separate plugin config for when the classpath is already there.
Yes, I could simply create properties that held the values of the options I wish to change instead of specifying the entire plugin config again. In that case, I would just set a eclipse.downloadSources property to true or false depending on the presence of the classpath and have a single plugin definition in the regular build section.
Any advice? This is a strange problem.
Thanks,
LES
I suspect the Maven Eclipse Plugin to mess do some classpath woodo that affects the jetty plugin later.
But to be honest, this is not a very common way to use the Eclipse plugin, at least not to my knowledge. Most people don't include it in their build, they just run it when the POM has changed and this generally doesn't happen every build. Moreover, touching the .classpath confuses Eclipse if I remember well and forces a clean rebuild of the project which is pretty annoying.
So at the end, and I'm sorry for that, this seems to introduce more annoyances than benefits. You can try to open a Jira issue though.
You really don't want to run the maven-eclipse-plugin on every build when there's a .classpath present. I can't tell you exactly what it does to the classpaths, but this is not how it's intended to be used. The assumption is that when you run it you only run eclipse:eclipse (or some other goal).
Why do you want to keep re-running it?