How should I invoke a packaged spring project from another spring project? - java

I have two spring projects both using maven. The first is a client for some api and the second is a console program that, in part, utilises that client.
I have packaged up the client into a jar and referenced it in the pom for the console program.
I have managed to get this working, just about, but I am not very happy with the solution:
1) The first problem I ran into was that each of the context xml files were named "applicationContext.xml". Therefore, I couldn't work out any way to reference the context file in the client, without renaming it to something else e.g. clientContext.xml. This works but is there any other way to reference it explicitly?
2) The next issue was how to invoke the clientContext.xml from within the console program. To do this, I have added <import resource="osrdClientContext.xml"/> to the applicationContext.xml of the console program and this seems to allow it to correctly find all of the defined beans. I'm not sure if this is best practice though?
3) Within clientContext.xml, I need to reference a properties file and so have the line <context:property-placeholder location="classpath:api.properties" />. This works when running the client on its own but appears to get ignored (or fails to find the file) when running the console program. The api.properties file is in the root of the packaged jar for the client and the jar is in the classpath of the console program. The only workaround I have found is to manually copy the properties file into the console program, at which point it is found without any problem.
4) Both projects have a resources directory with sub-directories "dev", "beta" and "prod". This allows me to define different properties depending on the maven profile I want to run against. This works fine for the individual projects but when I package the client, it only includes the properties files for the profile I am running against (which makes sense). However, that then means if I run the console project against profile "beta", it will still run the client against whatever profile it was packaged against. It would be handy to be able to package all of the properties files and get the client to run in the same profile as whatever is depending on it. Is that possible/a good idea?

Ad 1: The common place to put your JAR-based XML contexts is inside META-INF/your/project/name folder. You can check for example spring-batch-admin project. Also nowdays it is more common to name the context files {name}-context.xml (e.g. central-context.xml).
By following the advice above, you should not have problems with name conflicts. However it should be possible to overcome such problem by using classpath* pseudo protocol in your import definition:
<import resource="classpath*:do/not/put/in/root/this-can-be-duplicate.xml"/>
Ad 2: This is completely legit. You can see the same practice in the Spring Batch Admin example as linked above. Just add the classpath: or classpath*: to the resource path.
Ad 3: That is very strange and I have no clue to what is going on there.
Ad 4: This is possible to achieve via Spring profiles (not Maven profiles):
<beans profile="dev">
<context:property-placeholder location="classpath:META-INF/dev/my.properties"/>
</beans>
or via new SpEL support:
<context:property-placeholder location="classpath:META-INF/#{systemProperties['my.jvm.property']}/my.properties"/>
However what I like is to have a default properties and then let the main application be able to override them. This means, that your configuration will be on a single place and not inside the JAR. You can achieve this via properties hierarchy:
<context:property-placeholder location="classpath:META-INF/my-default.properties,classpath*:META-INF/my-optional-overrides.properties"/>
UPDATE just discovered that <context:property-placeholder> has problems with SPeL. However you can still use SPeL (and even other property configurers) when defining the PropertyPlaceholderConfigurer manually (i.e. via <bean>).

Related

Is ApplicationContext file necessary if I want to run the unit test?

everyone,
I'm new on web development. Recently, I start a tutorial to build Registration and Login with Spring Boot and MySQL Database. When I run the unit test, there are errors showing up. I cannot find ApplicationContext file in the tutorial, and also try serval methods on stack overflow e.g. change plug setting in pom.xml but isn't working.
Can anyone answer this problem?
Errors on IDE
The application context is not a file – it's a term Spring uses to describe the entire state of your application. So the error means that Spring is not able to start your application because of missing database configuration.
Looks like you are using maven (src/main/java). In this case put the applicationContext.xml file in the src/main/resources directory. It will be copied in the classpath directory and you should be able to access it with
#ContextConfiguration("/applicationContext.xml")
A plain path, for example, "context.xml", will be treated as a classpath resource from the same package in which the test class is defined. A path starting with a slash is treated as a fully qualified classpath location, for example "/org/example/config.xml".
So, it's important that you add the slash when referencing the file in the root directory of the classpath.

Can i set relative path to java.security.auth.login.config?

In my Spring Boot app i need to call to:
System.setProperty("java.security.auth.login.config", authConf);
where authConf seems to be expected as an absolute path to the file.
the problem is, my Spring Boot app is packaged and executed as a jar and i want to package the file inside the jar.
The answer provided in this question might work only when a WAR is deployed in a server. It doesn't seem to work when we run JARs with embedded container.
is there way i could set a relative path to java.security.auth.login.config to refer to my conf file packaged within my jar ?
I know that the answer is about 6 years too late, but I recently faced the same issue and found and answer. Maybe my answer will at least help the next dev who will face this issue. I was able to solve it is by first creating a javax.security.auth.login.Configuration of type JavaLoginConfig and then use the Configuration.setConfiguration(configuration method) of the same Configuration class. Using this method I was able to bundle our jaas.conf inside the jar and use a relative path to it.
To give a concrete example:
Get the URL of the security configuration file you want to use
URL configLocation=YourClass.getClassLoader().getResource("resources/jaas.conf);"
Create the JavaLoginConfig configuration
Configuration secConfig=Configuration.getInstance("JavaLoginConf", new URIParameter(configLocation.toURI());
Set the configuration to the one you created in step 2
Configuration.setConfiguration(secConfig);

How to get property file in Tomcat

I use Neatbeans to create a Java Class Library Project. This class (name: TestDB.java) is to do some DataBase jobs. I wrote a "config.properties" file to config some properties. The config.properties is located in the Project folder. In my class TestDB.java:
Properties properties = new Properties(System.getProperties());
String configFile = "config.properties";
properties.load(new FileInputStream(configFile));
String param1 = properties.getProperty("Postgre.Driver1");
I can use a test case to get the property, That's OK.
When I build this project and create a jar file. I want another web application to use this jar file to do some DB jobs. I already include the jar in my webapp and bulid a webproj.war. When I place this webproj.war to Tomcat and run the webapp. I can't get the property because I don't know which folder in the Tomcat webapp to place the config.properties. Anyone can help me to solve this question ?
Sorry for the long answer, but I'm trying to cover the background as I think your question is a little bit confused.
This answer tries to clarify your understanding of web-apps and reusability a little more generally.
First, it's important to understand that applications typically have to run in multiple environments - dev, sit, uat, production, etc. If you include environment specifics in your binary (jar or war) that means you can only use that binary in a single environment.
The original J2EE assumption was that these environment specifics would be managed by the container (Tomcat in this case). This is what the answer referenced in the first comment of your question is doing. Basically the container provides resources registered in JNDI, which your application can look up.
Now, I think it's fair to say, the J2EE folks where wide of the mark with some of the features, the JNDI resource mechanism being one instance of this, which isn't commonly used these days (at least not in the applications I'm used to).
Now, to try and answer your question.
The first important thing to recognise is that the reason for putting code into a seperate jar is to make it reusable. Since Spring burst onto the scene, one of the fundimental ways to achieve reusability is with dependency injection. Spring aside, dependency injection is nothing more complicated that passing the resources a particular object needs at the time you create that object.
For example, your TestDB class might be instantiated as follows:
TestDB testDB = new TestDB(dataSource);
This still doesn't solve the problem of instantiating the DataSource, however it does place this responsiblity with the code using TestDB rather than TestDB itself.
The simple way to instantiate the DataSource would be include a properties file in the war's classpath (in WEB-INF/classes), which would allow you to do the following (note the DataSource instantiation will be database specific):
Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("config.properties"))
DataSource dataSource = new PGSimpleDataSource();
dataSource.setServerName(properties.get("database.server.name"));
...
So this gets you to the point where you're loading a properties file from within your war. However as I stated, this makes the war environment specific.
The best way I know to solve this is to use an exploded war with a symlink to the environment configuration. To do this, you use a context file which you would place in [Tomcat root]/conf/Catalina/localhost.
For example, the given context file:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/my-app" docBase="/local/apps/my-app/war" distributable="false" allowLinking="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
You would have a directory structure like this:
local
+ apps
+ my-app
+ war
+ WEB-INF
+ classes
+ env -> /local/apps/my-app/env
+ env
Where /local/apps/my-app/war/WEB-INF/classes/env is a symlink to /local/apps/my-app/env.
Your properties would now be loaded like this:
Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("env/config.properties"))
This gives you two releaseable components - the application and the configuration that are simply unpacked to install them.

How can I set hibernate database config from outside config file?

I need to have hibernate database config set from outside text file, how can I do it? Is there some kind of method for this, or do I have to make my own?
You can programaticaly configure hibernate within your app: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-programmatic
HTH
Hibernate configuration file (*.cfg.xml) have to be in the class path of your application's war.
But, you can maintain file like 'install.properties' in deployment folder of your application.
Inside it you can maintain database related properties.For eg.
database.name =
database.hostname=
database.username=
databse.password=
Use some predefined constant string inside 'hibernate.cfg.xml' that you can replace during time of application deployment task. For eg.
<property name="hibernate.connection.url">jdbc:mysql://$database.hostname/$database.name</property>
you can write an 'ant' task which 'unwar' the war then replace constant strings with values as mentioned inside 'install.properties' and then make 'war' again.
In this way you can separate out configuration settings from application code structure.
Hibernate configuration files have to be on the class path but can be outside your war. The exact way to add a directory or files to the class path will depend on your app server so you'll have to mention which one you're using (for Tomcat, see this previous question for example).

Packaging a Spring managed Axis2 web service client

I've created a web service client using Axis2. I would like to package the client into a jar, so I can use it in several other projects. The client uses the Axis2 WS-Security module 'rampart'. This module, rampart.mar (not a typo!) has to be present on the Axis 'repository path', in a directory called 'modules'. The client also requires a security policy file, policy.xml. The locations of these last two are injected via Spring, but as they are not subject to change, I would like to package them into the jar.
If you inject a File object like:
<bean id="webserviceStubFactory" class="com.company.WebserviceStubFactory">
<constructor-arg value="classpath:policy.xml"/>
</bean>
it will work just fine when running/testing the project directly. However, when you package it into a jar, you get
java.io.FileNotFoundException: class path resource [policy.xml] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/path/to/webservice-0.8.jar!/policy.xml
This is a well known Spring 'problem': the constructor-arg is resolved into a Spring ClasspathResource instance and it doesn't support getFile(), because File instances cannot refer to something inside a jar. The solution to this is pretty simple: change the constructor to take a Resource instead of a file and use the InputStream directly. However, this doesn't work for the Axis2 repository path, as I can only specify the full path to the repository and the Axis2 internals figure out where the rampart.mar is located.
So, the question basically boils down to: is it possible to use Spring to inject a path inside a jar (even if it's only /) and have other libraries read from that path as if it were a regular filesystem? I'm guessing it isn't, but I would like to be sure, before installing external 'axis repositories' on the dev/test/production environments and having several copies of the rampart.mar lying around.
There's only so much that Spring can do to hide the inconsistencies of the underlying JavaSE API. The various getXYZ methods on the Resource interface come with caveats in the documentation, e.g. for getFile():
Throws:
IOException - if the resource cannot be resolved as absolute file path,
i.e. if the resource is not available in a file system
A resource inside a JAR is not "on a filesystem", and so this will never work.
In your case you're going to have to work around Axis2's requirement to operate on files, but manually making sure a file exists. In other words, use Resource.getInputStream() to extract the content of the JAR resource and copy it to a temporary File on the local filesystem, then pass that File to Axis2.

Categories

Resources