I have a resource defined at the ear level, such as {ear}/META-INF/somefile.txt.
I would like to load that file from a war in that ear using classpath resources (getResourceAsStream). I assume you can not do this, but wanted to check if there was a way to do that that i was unaware of.
Related
I have a dependency utilities.jar which has a dev.properties file in the root directory when compiled (in src/main/resources when uncompiled). The jar has a class (PropertiesUtil.java) that loads the properties with:
PropertiesUtil.class.getResourceAsStream("/dev.properties");
This jar is included as a dependency in my webapp. The webapp has its own dev.properties file in its root directory when compiled (in uncompiled form its in the src/main/resources folder).
The utilities.jar does not load its own internal dev.properties file but instead the webapp's dev.properties file.
I tried different methods like
PropertiesUtil.class.getClassLoader.getResourceAsStream("dev.properties");
without success.
I am using Gradle to compile the utilities.jar and the webapp into a war. The properties filename need to be the same because I pass in a JVM property
-Dproperty.filename=[dev|qa|prd].properties
when starting up the webapp. This system property is used to load the correct properties files for the webapp and the utilities.jar.
The utilities.jar is a separate project and packaged with gradle clean build and uploaded to an artifact server. The webapp pulls the utilities.jar from the artifact server when building the war.
I think this problem is occurring because you have two different files with exactly the same path (even though one is inside a jar) inside your classpath. It's very possible one dev.properties is getting overwritten.
I recommend changing the path of at least one of the properties files to be outside of the project root. For example, put the uncompiled dev.properties for Utilities at:
/src/main/resources/utilities/dev.properties
Then access it with:
PropertiesUtil.class.getClassLoader.getResourceAsStream("/utilities/dev.properties");
Then the two properties files won't stomp on each other anymore, and that should fix your problem.
I am majorly confused about where the classpath is. I understand when we create a spring mvc, resources folder, or inside web-inf is considered classpath. And we can use "classpath:" inside xml files to declare the folder. However, where is this classpath exactly? How is it set ? I have been reading about it for a long time, i still couldnt manage to get a real clear image in my head how the classpath is initially determined etc.
For example when we create a war file, and deploy it on a tomcat server, all the resource files can still be read via given paths with "classpath:" in the xml files. How does this work?
Thanks.
Ok, if it's web application, the classpath begins in WEB-INF/classes. Also, jar files in WEB-INF/lib are also on the classpath.
The Classpath is where the JVM will look for class files and other resources. Since you are using Spring MVC, I assume you are deploying a Web application (ie WAR file). This means that the classpath is set by the container which is following the Servlet spec.
The classpath for a WAR file includes the WEB-INF/classes and WEB-INF/lib folders. The Java EE/Servlet container where the WAR file is deployed will also include other common folders in the classpath.
Here is how Tomcat works.
You might also want to try this StackOverflow article/answer
In my application I have a directory that is in CLASS_PATH and where I store jar files. I use ATG so have to use my .class in properie file to have a way to init components (in this situation it's servlet). So I need my webapp.war in my CLASS_PATH direcory. I put this war file but it does'nt work. How can I put my compiled servlet classes to this directory?
Of course it's not good idea to put only this compiled classes from archive :)
I think what you are looking for is a custom class loader, adapted to handle WAR files. Such a class loader would unpack or peek into the WAR file, to extract the class files.
I have never heard of anyone loading from within WAR files, but it should not be that hard to implement.
Your classloader can not find the servlet classes in war because are in WEB-INF/classes.
Try to unzip and copy WEB-INF/classes to a classpath location
In ATG, Classpath setting start from the way in which you build the ear. Every module in ATG has a MANIFEST file, which specifies an attribute called "ATG Classpath". The value of this attribute is used to construuct the classpath at runtime. So, if you include the path to your class files as a value to this attribute, ATG automatically sets this in the classpath (runAssembler, to be more specific) when creating the ear. Jboss when deploying the ear will then pick it up.
Recently I separated the core functionality of my core servlets application into a jar file. This jar file is now deployed in tomcat's lib folder and the application(i.e. servlets, jsps, properties files..etc) is deployed independently as a war file.
The jar files needs specific properties files. I place these properties files right under the "src" folder(i.e. in the top of the classes hierarchy) in the war file.
In the past when everything was in the same project and deployed in one war file. The properties files were accessible by the related classes. Now when these classes are deployed in a jar, they can't see the properties files located in the war file (i.e. deployed web application).
What could I be missing here ?
An example how I load my proeprties files:
properties.load(getClass().getResourceAsStream("/appconfig.properties"));
Thank you for your time.
You shouldn't get it by the class' own classloader. As the class is now managed by Tomcat, it only knows about Tomcat's internal resources, not about webapp-specific resources. You should get it by the context classloader of the current thread. That classloader knows about all resources dedicated to the current webapp.
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("appconfig.properties"));
Regardless of this, the proper location of such a properties file representing the global app configuration would not be inside the WAR file. You should rather place the properties file on a fixed path outside Tomcat and the WAR file and then add that fixed path to the shared.loader property of Tomcat's /conf/catalina.properties so that it becomes part of the classpath. This allows you for freely editing the configuration file without the need to rebuild/redeploy the whole WAR. Note that you still need to use the context classloader to load it.
Yes, that won't work because the jars in tomcat lib folder are loaded by a different classloader that knows nothing about your webapp's classes. In other words, tomcat lib is the parent class loader of the webapp classloader (which are created one per each webapp).
If you want to make it work, you can either place the properties file in an external location and make it known to the jar inside tomcat lib via absolute file path or place the jar back inside webapp's lib.
Here's some reference from the Tomcat site.
http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html
Typically, environment variables are used to identify $CATALINA_HOME which will then be prefixed to the relative file path in order to avoid hardcoding the absolute paths.
String catalinaHome = System.getProperty("CATALINA_HOME");
properties.load(new FileInputStream(new File(catalinaHome + "/path/to/appconfig.properties")));
My goal is pretty simple: to use ant to build an EAR which contains 1 EJB and 1 jar containing all of the dependencies. This jar, called common.jar for the sake of example has vendor jar files in it as well as other xml files that the EJB depends on and will need to be able to see during runtime....
So far I have everything packaged correctly as an EAR like this:
EARFILE.ear
-EJBFILE.jar
/META-INF
-MANIFEST.MF
-common.jar
/META-INF
-MANIFEST.MF
/lib
-(all vendor jars inside here)
-(All the xml config files are inside the root of the common.jar)
Inside the MANIFEST.MF for the EJBFILE.jar is...
Class-path: ../../common.jar
Inside the MANIFEST.MF for the common.jar is...
Class-path: ../lib/some_common.jar
When I deploy this the appserver (websphere) cannot find the JAR file when I try to start the server. I am getting the ClassDefNotFoundError because the classes inside the EJB cant find the vendor JAR files when I try to start the instance. However I know that common.jar is setup correctly though, else the EJB wouldn't have compiled since it needed to have those vendor jars on the classpath for javac.
So what I want to know is this:
How can I get the runtime to correctly see the Vendor jar files.
Will the EJB be able to see the xml files at run-time? I am concerned about this because these xml files are located outside of the EJB inside of a jar that is just in the EAR, it isn't even a module its just a jar inside the EAR.
Does it even matter when using websphere? From what I gather some containers don't even care what is in the Class-path of MANIFEST.MF.
There are several improvements I can suggest, based on running into similar problems.
First and most importantly, use the appxml attribute of the Ant ear task to specify your deployment descriptor (usually named application.xml); also include references to the vendor JAR files bundled as defined below
I would recommend you not put your vendor JAR files into another JAR - instead, just copy them into the EAR at the same level as EJBFILE.jar
The configuration XML files can go in a sub-directory of the EJBFILE.jar (such as config), and then you can reference them as /config/filename.xml.
The application.xml file will tell WebSphere where to find your JAR files. Classpath traversal in an application server is not the same as that of a compiler, which JBoss has taught me the hard way.
I am using all of the above patterns, and my in-container code (deployed in the EAR) can see all my XML files, as well as find all my dependencies.