JRebel do not detect web resource changes over multiple webapp folders - java

I have a modular web project and thus I am allowing modules to be a war archive including webapp folder. Using the following rebel.xml works fine on detecting class changes over all modules. But for some reason jrebel does not move when a html or js is changed.
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com"
xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
<classpath>
<!-- appserver -->
<dir name="/home/xx/data/appserver/target/classes/main"/>
<dir name="/home/xx/data/appserver/target/resources/main"/>
<!-- module -->
<dir name="/home/xx/data/as.module.core/target/classes/main"/>
<dir name="/home/xx/data/as.module.core/target/resources/main"/>
<dir name="/home/xx/data/as.module.mqlcore/target/classes/main"/>
<dir name="/home/xx/data/as.module.mqlcore/target/resources/main"/>
</classpath>
<!-- web>
<link target="/">
<dir name="/home/xx/data/appserver/src/main/webapp"/>
<dir name="/home/xx/data/as.module.core/src/main/webapp"/>
<dir name="/home/xx/data/as.module.mqlcore/src/main/webapp"/>
</link>
</web -->
<web>
<link target="/">
<dirset dir="/home/xx/data">
<include name="**/src/main/webapp"/>
</dirset>
</link>
</web>
</application>
EDIT:
Interesting fact is. When I use the commented part of web configuration all three webapp folders are in the log and will be monitored for changes. But the application server can not find all of the webapp files. When I use the second <web> configuration all files are seen by the application server but are not observed by jrebel. I think it is not possible to have multiple directories linked to "/"

Each of the modules eg .war or .jar files need to have their own rebel.xml files. Otherwise all of them will reload same resources and when having different classloaders all kind of weird things can happen.
It is possible to check which instance of the file JRebel actually uses by searching "found resource" from jrebel.log. It should be written something like this
sun.misc.Launcher$AppClassLoader#184be29 found resource: 'cfg/log4j.xml' from 'file:/C:/Projects/testproject/Trunk/edm/target/cfg/log4j.xml'.
It is also seen which of the file changed by loking up lines with Event like this:
[IntelliJFSNotify] Event 'CHANGE' on: 'C:/Projects/testproject/Trunk/edm/target/cfg/log4j.xml'
Usually found resource and changed file paths do not match if the file is not reloaded. If they do match then it is recommended to send absolute path of the file and jrebel.log to support#zeroturnaround.com for investigation.

Ok, the answer is: indeed it is not possible to configure more than one directory under the <web><link target="/"></link></web> configuration. I have now a folder ./target/all-webapp where I smylink (cp -sR) all files via gradle task ... not nice but works ... and thank god jrebel is following symlinks!

Related

Jrebel not reloading JSON file in resources

I'm working on a Spring Multi Module Project.
One of the projects contains some JSON file in a folder called drivers, located
in: <Project>/src/main/resources.
When I first launch the app all the JSON files are correctly loaded, but if I make a change to one of them JRebel keeps on using the old one.
Is there a way to configure it to solve this issue?
Thank you.
Here is the rebel.xml for this project:
<?xml version="1.0" encoding="UTF-8"?>
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">
<classpath>
<dir name="C:/PathToProject/target/classes">
</dir>
<dir name="C:/PathToProject/src/main/resources/drivers">
</dir>
</classpath>
<web>
<link target="/">
<dir name="C:/PathToProject/src/main/webapp">
</dir>
</link>
</web>
</application>
Change the classpath part to
<classpath>
<dir name="C:/PathToProject/src/main/resources/"/>
<dir name="C:/PathToProject/target/classes"/>
</classpath>
If the application is trying to load a classpath resource, its full name is important(e.g. drivers/myfile.json) as this is what is passed to the classloader. The rebel.xml must specify paths that are classpath roots, not subfolders. For finer control you can use includes/excludes for the dir entry.
Also the paths are checked in order and if it exists in the first one, that is returned even if it's older.
So in your current configuration JRebel would first search for
C:/PathToProject/target/classes/drivers/myfile.json and find the old version there from the last run of maven-resources-plugin copy-resources goal or project build in IDE.
Now if only the order was fixed, the second path is still incorrect as it would look for C:/PathToProject/src/main/resources/drivers/drivers/myfile.json where it doesn't exist.

JRebel will not hot deploy source files with embedded tomcat in intellij

I have the following project structure
project-root
--core
---build.gradle
---project.gradle
--web
---build.gradle
---project.gradle
I configured jRebel to run with gradle, so I can start my embedded tomcat 8 with the command gradle tomcatRun
This shows log-output of jrebel and my application starts on the embedded server, but there is no hot deployment when i change java files.
With the IntelliJ JRebel Plugin I created the rebel.xml files for each of the project project-root, core and web. Also I added a rebel.xml inside the web project in the folder web/build/classes/main as described here
Normally jrebel should show at startup that its wachting some folders, but this is not the case on my site, so i guess there is something wrong with my configuration
here the rebel.xml files
project-root (Path c:/project-root/rebel.xml)
<?xml xsd....">
<classpath>
<dir name="c:/project-root/out/production/classes">
</dir>
</classpath>
</application>
core (Path c:/project-root/core/src/main/resources/rebel.xml)
<?xml version="1.0" encoding="UTF-8"?>
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">
<classpath>
<dir name="c:/project-root/core/out/production/resources">
</dir>
<dir name="c:/project-root/core/out/production/classes">
</dir>
</classpath>
</application>
web (Path c:/project-root/web/src/main/resources/rebel.xml)
<?xml version="1.0" encoding="UTF-8"?>
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">
<classpath>
<dir name="c:/project-root/web/out/production/classes">
</dir>
</classpath>
</application>
web (Path c:/project-root/web/build/classes/main/rebel.xml)
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com"
xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
<classpath>
<dir name="c:/project-root/web/build/classes/main">
</dir>
</classpath>
<web>
<link target="/">
<dir name="c:/project-root/web/src/main/webapp">
</dir>
</link>
</web>
</application>
To attach jrebel to my gradle i did the following batch calls before starting gradle tomcatRun
set REBEL_HOME=C:\Users\myuser\.IdeaIC2017.3\config\plugins\jr-ide-idea\lib\jrebel6
set JAVA_OPTS="-agentpath:%REBEL_HOME%\lib\jrebel64.dll"
then calling gradlew tomcatRun starts the tomcat and jrebel
gradlew tomcatRun
2018-01-09 10:24:09 JRebel: Starting logging to file: C:\Users\myuser\.jrebel\jrebel.log
2018-01-09 10:24:09 JRebel:
2018-01-09 10:24:09 JRebel: #############################################################
2018-01-09 10:24:09 JRebel:
2018-01-09 10:24:09 JRebel: JRebel Agent 7.1.3 (201711301108)
2018-01-09 10:24:09 JRebel: (c) Copyright ZeroTurnaround AS, Estonia, Tartu.
2018-01-09 10:24:09 JRebel:
2018-01-09 10:24:09 JRebel: Over the last 2 days JRebel prevented
2018-01-09 10:24:09 JRebel: at least 0 redeploys/restarts saving you about 0 hours.
But when I change some source files, nothing is detected by jrebel, also when i manually called gradlew compileJava
I think that the problem is that JRebel is not attached to the actual process running the embedded Tomcat server. Instead, it gets attached to the Gradle's wrapper process. Try attaching JRebel's JVM argument via the org.gradle.jvmargs option instead.
gradle tomcatRun -Dorg.gradle.jvmargs="-agentpath:/path/to/jrebel/lib/libjrebel64.so"
Additionally, since tomcatRun task makes use of exploded deployment, rebel.xml files will not be necessary. This is because the build output directory is the same as the deployment directory and the changes made in that directory are automatically picked up by JRebel and reloaded.
Make sure to also compile the changes that you made using the gradle compileJava command to do so.
I tried it out personally and it seems to work well. Although it does not output the JRebel banner to the console output, it does let you know when a class file was reloaded.
While the answer by Tiit is correct, I would like to add that when using Gretty, you need to specify the -agentpath:/path/to/jrebel/lib/libjrebel64.so argument in your build.gradle file like so:
gretty {
. . .
jvmArgs = [
'-agentpath:/path/to/jrebel/lib/libjrebel64.so'
]
}
otherwise, by using -Dorg.gradle.jvmargs option, JRebel agent seems to attach to a wrong process when using Gretty launcher.

How can I build an EAR file from a Dynamic Web Project in Eclipse?

I'm looking at deploying a web service which I've written in Eclipse to an EAR file. I'm able to export it as a WAR and deploy it on Tomcat all fine and dandy, but the final product won't be on Tomcat, and won't be a WAR file. I'll need to use Websphere as a server, which I have access to and can deploy valid EAR files... if I had an EAR file to deploy.
So long story short, how do I export an EAR file from a Dynamic Web Project in Eclipse?
You need to create an Enterprise Application Project (basically, an EAR) in Eclipse, add the Dynamic Web Project to the EAR project, and then export the whole thing.
for this I use an Ant build script, (you can create a new Ant file in Eclipse, store it in your dynamic web project root and then right click > run on it when you want to run it from eclipse. Something like below. Basically copy your war to a directory, ${earDir} in the script below, and build that to an EAR (an EAR is just a type of JAR) :
<target name="buildEar" depends="init">
<copy tofile="${earDir}/" file="yourWarFile"/>
<copy tofile="${earDir}/META-INF/application.xml" file="localDirectory/application.xml"/>
<copy todir="${earDir}/META-INF">
<fileset dir="localDirectory" includes="was.policy" />
</copy>
<jar jarfile="localDir/something.ear" basedir="${earDir}">
<!-- Define the properties for the Manifest file. -->
<manifest>
<attribute name="Implementation-Vendor" value="Company name"/>
<attribute name="Implementation-Title" value="Application Title"/>
<attribute name="Implementation-Version" value="version and build number"/>
</manifest>
</jar>
</target>
Your was.policy file will be something like this (not good to give all permissions but you can change it later once you have it up and running):
//
// Template policy file for enterprise application.
// Extra permissions can be added if required by the enterprise application.
//
// NOTE: Syntax errors in the policy files will cause the enterprise application FAIL to start.
// Extreme care should be taken when editing these policy files. It is advised to use
// the policytool provided by the JDK for editing the policy files
// (WAS_HOME/java/jre/bin/policytool).
//
grant codeBase "file:${jars}" {
};
grant codeBase "file:${connectorComponent}" {
};
grant codeBase "file:${webComponent}" {
};
grant codeBase "file:${ejbComponent}" {
};
grant codeBase "file:${application}" {
permission java.security.AllPermission;
};
Your application.xml file will be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<application id="Application_ID" version="1.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/application_1_4.xsd">
<display-name>yourAppName</display-name>
<module id="WebModule_1240219352859">
<web>
<web-uri>yourWarFile.war</web-uri>
<context-root>urlToApplication</context-root>
</web>
</module>
</application>
The ids there should be ok they need to be unique per EAR I believe (or is it server cant remember).
I hope this helps.

Beanshell in Ant yielding, "Unable to create javax script engine for beanshell"

Greeting, I'm trying to put some Beanshell script in my Ant build.xml file. I've followed the Ant manual as well as I can but I keep getting "Unable to create javax script engine for beanshell" when I run Ant. Here is the test target I wrote mostly from examples in the Ant manual:
<target name="test-target">
<script language="beanshell" setbeans="true">
<classpath>
<fileset dir="c:\TEMP" includes="*.jar" />
</classpath>
System.out.println("Hello world");
</script>
</target>
My beanshell "bsh-2.0b4.jar" file is on the script task's classpath the way the manual recommended. Hope I have the right file. I'm working in c:\TEMP right now.
I've been googling and trying for a while now. Any ideas would be greatly appreciated. Thanks.
First, you need jsr-engines.zip from here:
https://scripting.dev.java.net/servlets/ProjectDocumentList
Inside, you'll find jsr223/beanshell/build/bsh-engine.jar. Some searching implied that you need to download bsh-2.05b.jar. I found it here:
http://beanshell.org/bsh-2.0b5.jar
The more easily findable bsh-2.0b4.jar also seemed to work, but it printed a message that implied it was experimental.
Currently (2012) you need only 1 jar to fire the script task for BeanShell:
bsh-2.0b5.jar
Previously I also thought of the following, as mentioned by Ant Manual, Library Dependencies chapter:
bsf-2.4.0.jar
commons-logging-api-1.1.jar
But it looks like bsf is not needed for bsh, at least in my environment.
Once the jar is given to ant, the script task runs smoothly. There are 2 possible scenarios for getting the jars and making them available to ant.
Manual download way
Download the jars above. I provided the links from maven repository. Once you have all the jars downloaded, make them available to ant. There are
at least 3 ways to do it:
Put it in java library path
Put it in ant library directory
Give the correct classpath to script task.
I find the last method the best, because it is most easily ported between
different systems. The ant file for the script task could look as follows:
<project default="t1" >
<property name="bsh.path"
location="/mnt/q/jarek/lang/java/ant/stackoverflow/bsh-2.0b5.jar" />
<target name="t1">
<script language="beanshell" classpath="${bsh.path}">
javax.swing.JOptionPane.showMessageDialog(null, "Hello, Script!");
</script>
</target>
</project>
Automatic download method, employing Ivy
The manual method is not perfect when you want to distribute your build script. Then you would like a way to make sure the jars are present in the destination system. For distributing builds there's no better tool than ivy. Ivy will download the jars and put them in classpath for you. The problem is that there appears another dependency, which is ivy itself. But providing ivy.jar is quite easy and that is the last dependency we need to supply explicitly.
One may ask why to provide ivy.jar, while we could simply download bsh.jar in the same way. The answer is flexibility. When you have the ivy.jar, you get any jar you wish with a single step being adding it to the ivy.xml file. And there is an agreed universal location for the ivy.jar file, while for other file we would have to think of a suitable directory.
Below comes the full example that downloads ivy and then all the necessary dependencies. Ivy download script is based on Installation chapter of Ivy reference.
Then a simple ivy.xml file is needed, which is given after the sample build.xml.
Original auto-download ivy script has a disadvantage of always checking the ivy url, even if ivy.jar is already in the expected location. This may be overriden by specifying -Doffline=true. I prefer to add another target to the build file and to do the http check only if we don't already have the ivy.jar. This is the way the script here works. To observe what ivy actually downloaded, set IVY_HOME environment variable to a directory of your choice. It will be created and filled with ivy stuff.
build.xml:
<project default="t1"
xmlns:ivy="antlib:org.apache.ivy.ant" >
<property name="ivy.install.version" value="2.2.0" />
<property environment="env" />
<condition property="ivy.home" value="${env.IVY_HOME}">
<isset property="env.IVY_HOME" />
</condition>
<property name="ivy.home" value="${user.home}/.ant" />
<property name="ivy.jar.dir" value="${ivy.home}/lib" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
<target name="check-ivy">
<condition property="ivy.present">
<available file="${ivy.jar.file}" type="file" />
</condition>
</target>
<target name="download-ivy" unless="ivy.present">
<mkdir dir="${ivy.jar.dir}"/>
<!-- download Ivy from web site so that it can be used even without any special installation -->
<get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>
<target name="init-ivy" depends="check-ivy, download-ivy">
<!-- try to load ivy here from ivy home, in case the user has not already dropped
it into ant's lib dir (note that the latter copy will always take precedence).
We will not fail as long as local lib dir exists (it may be empty) and
ivy is in at least one of ant's lib dir or the local lib dir. -->
<path id="ivy.lib.path">
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
</path>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
</target>
<target name="ivy-libs" depends="init-ivy" >
<ivy:cachepath pathid="path.from.ivy" log="download-only" />
</target>
<target name="t1" depends="ivy-libs" >
<script language="beanshell" classpathref="path.from.ivy">
javax.swing.JOptionPane.showMessageDialog(null, "Hello, Script!");
</script>
</target>
</project>
ivy.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="example.com" module="testing-script-task" />
<dependencies>
<dependency org="org.beanshell" name="bsh" rev="2.0b5" />
<!-- <dependency org="bsf" name="bsf" rev="2.4.0" /> -->
</dependencies>
</ivy-module>
The Ant plug-in "org.apache.ant_1.7.0.v200803061910" have all the jar files needed
Don't use beanshell language. Use javascript instead, as it runs on jdk6 without any additional jars. Rebse told me that.
Javascript is also allowed to use java classes, for example java.lang.System.out.println()

Getting JAX-WS client work on Weblogic 9.2 with ant

I've recently had lots of issues trying to deploy a JAX-WS web servcie client on Weblogic 9.2. It turns out there is no straightforward guide on how to achieve this, so I decided to put together this short wiki entry hoping it might be useful for others.
Firstly, Weblogic 9.2 does not support web servcies using JAX-WS in general. It comes with old versions of XML-related java libraries that are incompatible with the latest JAX-WS (similar issues occur with Axis2, only Axis1 seems to be working flawlessly with Weblogic 9.x but that's a very old and unsupported library).
So, in order to get it working, some hacking is required. This is how I did it (note that we're using ant in our legacy corporate project, you probably should be using maven which should eliminate 50% of those steps below):
1.Download the most recent JAX-WS distribution from https://jax-ws.dev.java.net/ (The exact version I got was JAXWS2.2-20091203.zip)
2.Place the JAX-WS jars with the dependencies in a separate folder like lib/webservices.
3.Create a patternset in ant to reference those jars:
<?xml version="1.0"?>
<patternset id="jaxws.classpath">
<include name="webservices/jsr173_api.jar" />
<include name="webservices/jsr181-api.jar" />
<include name="webservices/jaxb-api.jar" />
<include name="webservices/jaxb-impl.jar" />
<include name="webservices/jaxb-xjc.jar" />
<include name="webservices/jaxws-tools.jar" />
<include name="webservices/jaxws-rt.jar" />
<include name="webservices/jaxws-api.jar" />
<include name="webservices/policy.jar" />
<include name="webservices/woodstox.jar" />
<include name="webservices/streambuffer.jar" />
<include name="webservices/stax-ex.jar" />
<include name="webservices/saaj-api.jar" />
<include name="webservices/saaj-impl.jar" />
<include name="webservices/gmbal-api-only.jar" />
</patternset>
4.Include the patternset in your WAR-related goal. This could look something like:
<?xml version="1.0"?>
<copy todir="${wardir.lib}" includeEmptyDirs="false" flatten="true">
<fileset dir="${libs}">
<!--lots of libs here, related to your project -->
<patternset refid="jaxws.classpath"/>
</fileset>
</copy>
(not the flatten="true" parameter - it's important as Weblogic 9.x is by default not smart enough to access jars located in a different lcoation than WEB-INF/lib inside your WAR file)
5.In case of clashes, Weblogic uses its own jars by default. We want it to use the JAX-WS jars from our application instead. This is achieved by preparing a weblogic-application.xml file and placing it in META-INF folder of the deplotyed EAR file. It should look like this:
<?xml version="1.0"?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<prefer-application-packages>
<package-name>javax.jws.*</package-name>
<package-name>javax.xml.bind.*</package-name>
<package-name>javax.xml.crypto.*</package-name>
<package-name>javax.xml.registry.*</package-name>
<package-name>javax.xml.rpc.*</package-name>
<package-name>javax.xml.soap.*</package-name>
<package-name>javax.xml.stream.*</package-name>
<package-name>javax.xml.ws.*</package-name>
<package-name>com.sun.xml.api.streaming.*</package-name>
</prefer-application-packages>
</weblogic-application>
6.Remember to place that weblogic-application.xml file in your EAR! The ant goal for that may look similar to:
<?xml version="1.0"?>
<target name="build-ear" depends="war, manifest">
<delete dir="${dist}"/>
<mkdir dir="${dist}"/>
<jar destfile="${warfile}" basedir="${wardir}"/>
<ear destfile="${earfile}" appxml="resources/${app.name}/application.xml">
<fileset dir="${dist}" includes="${app.name}.war"/>
<metainf dir="resources/META-INF"/>
</ear>
</target>
7.Also you need to tell weblogic to prefer your WEB-INF classes to those in distribution. You do that by placing the following lines in your WEB-INF/weblogic.xml file:
<?xml version="1.0"?>
<container-descriptor>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
8.And that's it for the weblogic-related configuration. Now only set up your JAX-WS goal. The one below is going to simply generate the web service stubs and classes based on a locally deployed WSDL file and place them in a folder in your app:
<?xml version="1.0"?>
<target name="generate-jaxws-client">
<taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
<classpath path="classpath.main"/>
</taskdef>
<wsimport
destdir="${src}"
package="acme.somewhere.in.your.package.tree.webservices."
keep="true"
wsdl="http://localhost:8088/mockWebService?WSDL">
</wsimport>
</target>
Remember about the keep="true" parameter. Without it, wsimport generates the classes and... deletes them, believe it or not!
For mocking a web service I suggest using SOAPUI, an open source project. Very easy to deploy, crucial for web servcies intergation testing.
9.We're almost there. The final thing is to write a Java class for testing the web service, try to run it as a standalone app first (or as part of your unit tests)
10.And then try to run the same code from withing Weblogic. It should work. It worked for me. After some 3 days of frustration.
And yes, I know I should've put 9 and 10 under a single bullet-point, but the title "10 steps to deploy a JAX-WS web service under Web logic 9.2 using ant" sounds just so much better.
Please, edit this post and improve it if you find something missing!
This is not really a question but a guide so I'm answering it myself just to mark it as done.
Another way of dealing with web services on Weblogic 9.2 is using Apache CXF. This particularly well integrates with Spring as each web service is exposed as a bean and the actual classes don't even need to know that they are web services, it's all configuration driven.
A great guide about setting up Apache CXF on Weblogic can be found here: http://wheelersoftware.com/articles/spring-cxf-web-services.html
This works on Weblogic 9.2 as well and if you need to expose web services, not just connect to existing ones, this is by far better approach than using plain JAXWS (which is used by CXF anyway).

Categories