I'm creating a REST web application with Java, Tomcat and Jersey. I'm using annotations (no web.xml!) I ended up using this application configuration:
package com.my_own.server;
import java.util.Properties;
import javax.ws.rs.ApplicationPath;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.my_own.db.PostgreSQLDb;
#ApplicationPath("/api")
public class Application extends javax.ws.rs.core.Application {
private static Logger logger = LogManager.getLogger(Application.class);
public static Application application = null;
public final Properties properties;
public final PostgreSQLDb adminDb;
public Application() throws Exception {
logger.debug("Loading properties from ",
getClass().getClassLoader().getResource("config.properties"));
properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("config.properties"));
adminDb = new PostgreSQLDb(properties, "admin");
application = this; // Setting the global application object here
}
}
Here is my problem. There is a single global application objects for the web container. I'm saving it into a static field, from the application constructor. I need to access it from other classes later (it holds the global configuration, a global database connection factory, and probably other things.)
Am I doing this right? I suspect that there must be a better way: save a reference to the application when annotations are processed. But I'm not sure how. Can I be sure that the Application's constructor will be called exactly once, and the Application.application reference can be accessed later, from any REST call?
Use dependency injection in jersey, bind your application when initializing:
public class MyApplication extends ResourceConfig {
public MyApplication() {
super(MyApplication.class);
register(new MyBinder());
packages(true, "location.of.my.jersey.classes");
}
/* Bind is used to let jersey know what can be injected */
private static class MyBinder extends AbstractBinder {
#Override
protected void configure() {
bind(MyApplication.class).to(MyApplication.class);
}
}
}
Then in your code:
#Path("myapi")
#Produces(MediaType.APPLICATION_JSON)
public class ServerRoutes {
#Inject
MyApplication application;
//... your rest code here
}
Now you can access MyApplication from within your code without needing any statics or singletons, jersey handles it.
Let me share my opinion: you can use of course a well-known Singleton pattern to store a "global" static object, however, it's really an antipattern these days.
If it's not a homework or something then storing global "static" objects is always a bad idea design-wise. If you want to know why there are many sources that answer this question, like this discussion for example
So I suggest considering using a Dependency Injection container instead, There are many really good containers out there: Spring, Guice to name a few.
These containers can store these objects as beans and if your "pieces" of functionality are also managed by these containers, you'll be able to "inject" application beans right into the controller.
It effectively solves all the issues introduced by singleton pattern.
Related
For my application I created my own type of ApplicationContext that allows me to interact in specific manners that are needed for may application. As the application is a desktop application, I create the context like this:
#SpringBootApplication
#Import(StandaloneConfiguration.class)
#PropertySource(value = {"application.properties", "server.properties"})
public class OpenPatricianApplication extends Application {
private ApplicationContext context;
#Override
public void init() {
SpringApplicationBuilder builder = new SpringApplicationBuilder(OpenPatricianApplication.class);
context = builder.contextClass(DependentAnnotationConfigApplicationContext.class).run(getParameters().getRaw().toArray(new String[0]));
// more initialisation
}
}
}
Now I want to create a Spring Boot integration test that actually relies on the functionality of my own ApplicationConext implementation.
#SpringBootTest(classes = {ServerTestConfiguration.class})
public class ServerIntegrationTest {
private DependentAnnotationConfigApplicationContext context;
}
How do I go about initializing my context in the test? The context must be created in order to start the spring application for this to work, but with the SpringBootTest annotation this already happened, when the constructor is entered.
Are there any additional annotations or parameter for existing ones that can be applied? Should tests of these nature not be annotated with SpringBootTest at all and the application created manually?
The approach that I found to solve this issue is to forgo the SpringBootTest annotation altogether and construct the context as part of the constructor. Alternatively you could also do it in the BeforeAll or BeforeEach method, but as my test class extends a base class that needs some beans injected, the constructor seemed the right choice.
However what does not work is injecting the beans in the super class by way of constructor injection, as the call to the super constructor has to be the first call in the constructor and that would necessitate to have a static initializer block for the context and I want to avoid static stuff as much as possible, especially if the context is not properly cleaned up at the end of the test, it would live on as part of the loaded class in memory and potentially consume lot of memory.
So here is the code:
public class ServerIntegrationTest extends SaveLoadBase<CityWall> {
public CityWallSerializationTest() {
SpringApplicationBuilder builder = new SpringApplicationBuilder(ServerTestConfiguration.class);
DependentAnnotationConfigApplicationContext context = (DependentAnnotationConfigApplicationContext) builder.contextClass(DependentAnnotationConfigApplicationContext.class).run();
setContext(context);
setClientServerEventBus((AsyncEventBus) context.getBean("clientServerEventBus"));
setLoadAndSaveService(context.getBean(TestableLoadAndSaveService.class));
}
}
I have a logger class that makes use of a service. Each time a new logger is created, I expect to have access to the singleton scoped logging service.
I autowire the logging service into the logger however, a null pointer exception is returned. I have tried a few solutions:
manually defining the bean in the application context,
Trying to get the logger to be spring managed but that just resulted in more issues.
I am trying to get this to work in my junit tests, and I do specify the context file to make use of a different application context. However even if kept identical it does not resolve the issue.
Please find code below:
The following is an excerpt from the application context.
<context:component-scan base-package="com.platform"/>
<bean id="asyncLoggingService" class="com.platform.services.AsyncLoggingServiceImplementation" scope="prototype"/>
The following is the Logger class.
package com.platform.utils;
import com.platform.services.AsyncLoggingService;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class OurLogger
{
private static Logger logger;
#Autowired
private AsyncLoggingervice asyncLoggingService;
public OurLogger(Class Clazz)
{
logger = LoggerFactory.getLogger(Clazz);
}
public void trace(TraceableObject object, String message)
{
//Do nothing yet
}
}
I then make use of the Logger in another service in order to log whats going on. (The reason I am writing another logger is to make use of an RabbitMQ server) In the service I instantiate a new instance of the Logger and then use it accordingly.
#Service
public class AsyncAccountServiceImplementation implements AsyncAccountService
{
private static final String GATEWAY_IP_BLOCK = "1";
private static OurLogger logger = new OurLogger(AsyncAccountServiceImplementation.class);
...
}
The null pointer occurs in the OurLogger when I try to call any method on the asyncLoggingService.
I then am trying to test the AsyncAccountService using JUnit. I make sure I add the different application context but it still seems to result in the null pointer exception.
If you need further information please let me know. I have seen ways to fix this but they don't seem to work so perhaps I have made a mistake somewhere or I am not understanding this all quite correctly.
When you create an object by new, autowire\inject don't work...
See this link and this link for some workaround.
Anyway if you would inject a logger i suggest you this my answer to another topic.
Just want to add my 2 cents.
I once encountered the same issue when I was not quite used to the life in the IoC world. The #Autowired field of one of my beans is null at runtime.
The root cause is, instead of using the auto-created bean maintained by the Spring IoC container (whose #Autowired field is indeed properly injected), I am newing my own instance of that bean and using it. Of course this one's #Autowired field is null because Spring has no chance to inject it.
If you are using AspectJ you can use #Configurable:
#Configurable
public class OurLogger {
..
}
See: Using AspectJ to dependency inject domain objects with Spring
Use spring framework Unit test instead of JUnit test to inject your spring bean.
May be that will help you.
Within your application context you have to do reference your Logger :
<context:component-scan base-package="com.platform"/>
<bean id="asyncLoggingService" class="com.platform.services.AsyncLoggingServiceImplementation" scope="prototype"/>
<bean id="ourLogger" class="com.platform.utils.OurLogger"/>
Then you've to inject it into your service :
#Service
public class AsyncAccountServiceImplementation implements AsyncAccountService
{
private static final String GATEWAY_IP_BLOCK = "1";
#Autowired
private OurLogger logger;
}
Here is my abstract class which starts Jersey with given Spring context:
public abstract class AbstractJerseyTest extends JerseyTest {
public void setUp() throws Exception {
super.setUp();
}
#AfterClass
public void destroy() throws Exception {
tearDown();
}
#Override
protected URI getBaseUri() {
return URI.create("http://localhost:9993");
}
#Override
protected Application configure() {
RestApplication application = new RestApplication();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
properties.put("contextConfigLocation", "classpath:spring-context-test.xml");
application.setProperties(properties);
application.register(this);
return application;
}
}
So, the problem is that I need to access Spring bean from my test to populate database with some data.
Jersey version is 2.6
Also I found a similar question here
But it's related to Jersey 1.x so it doesn't work for Jersey 2.x
Could anyone point me in the right direction?
Solution was really simple.
I added:
#Autowired
private Repository repository;
to the AbstractJerseyTest and this field was automatically autowired during test startup. I don't know details about how it works, but it seems that when I register instance of the test in REST application
application.register(this);
it automatically autowires all beans in the test.
Normally in your case, I'd just say work with mocks, but there are cases where you may need to expose the services in the test class.
To do this without any "ugly hacks", you will need to get a handle on the ServiceLocator (which is analogous to Spring's ApplicationContext). When the Jersey app boots up, all the Spring services from the ApplicationContext are put into the ServiceLocator through HK2's Spring bridge.
The problem is JerseyTest does not expose the ServiceLocator in any way. The only way I can think of to get a hold of it, is to create your own TestContainerFactory, and create the ApplicationHandler, which exposes the ServiceLocator.
Trying to implement your own TestContainerFactory is not a walk in the park, if you don't know what you're doing. The easiest thing to do is just look at the source code for Jersey's InMemoryTestContainerFactory. If you look at the constructor for the inner class InMemoryTestContainer, you will see it creating the ApplicationHandler. This is how you can expose the ServiceLocator, through the appHandler.getServiceLocator().
So if you copied that class, and exposed the ServiceLocator, you could create your JerseyTest extension, and call the ServiceLocator.inject(Object) method to inject the test class.
public abstract class AbstractServiceLocatorAwareJerseyTest extends JerseyTest {
private final ServiceLocatorAwareInMemoryTestContainerFactory factory
= new ServiceLocatorAwareInMemoryTestContainerFactory();
private ServiceLocator locator;
#Override
public TestContainerFactory getTestContainerFactory() {
return factory;
}
#Before
#Override
public void setUp() throws Exception {
super.setUp();
this.locator = factory.getServiceLocator();
if (injectTestClass()) {
this.locator.inject(this);
}
}
public boolean injectTestClass() {
return true;
}
public ServiceLocator getServiceLocator() {
return locator;
}
}
And if for any reason you needed it, the ServiceLocator also has the ApplicationContext, which you could also expose to your test class if needed.
I put together a GitHub project, with a complete implementation, with tests if you want to take a look at it.
UPDATE
Though the OP's answer to this question works, I believe the fact that it works, is a bug. I originally deleted this answer, after the OP posted their answer, but after some testing, I believe that solution is a bug, so I've undeleted this post for anyone who doesn't like the warning1 you get when you use that solution
1. "WARNING: A provider SimpleTest registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider SimpleTest will be ignored."
I have a utility class which I want to initialize when the application starts in Spring MVC. So I am implementing InitializingBean. Now I have to create an object for the same and save it in Application scope so that I can access the same instance everywhere. But I am not able to get hold of this.
Here is my try:
public class DashboardInitializer implements InitializingBean, ApplicationContextAware {
private ApplicationContext mApplication;
#Override
public void afterPropertiesSet() throws Exception {
initializeConfigurationUtil();
ConfigurationUtil util = ConfigurationUtil.getInstance();
/* Save the util to application scope */
}
#Override
public void setApplicationContext(ApplicationContext pApplication) throws BeansException {
this.mApplication = pApplication;
}
}
Is this approach correct or there is a better way to do that?
I think you need to simplify this a little bit.
You want the utility class to be initialized after your application context is loaded, but you also want your util class to be in the application context?
Seems the util class has some dependency objects configured in the application context, and the util class is in turn a dependency of some classes in the application context.
If you can express these dependencies in the form of beans (util is a bean, which has its dependency beans injected into it, and beans that need util have util injected into them), Spring will ensure that all dependencies of util are initialized first, then util is initialized and then it is injected into classes that need util.
You should not try to add something to an initialized context.. Its not possible.
If you cannot express util and its dependencies as beans, you can also take this approach:
1. Configure util as a bean in the application context, add a default constructor that does nothing. So this object would be created, but not initialized when the spring context is loaded.
In the ApplicationContextAware implementation you have, modify the setApplicationContext method. Get the util bean you configured earlier from the context.
You can now initialize (execute some code that you want to execute) the util instance, just make sure you do not try to reassign the bean to some other instance of util.
Hope this helps.
You can use #postconstruct annotation on methods to perform business logic immediately after the application has been initilized. And properties can simply be injected using placeholder in config and #Value annotation on java fields.
In my wicket spring based applications, I have this method to inject the spring manager to the WebApplication class:
private void initManager() {
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
this.manager = (MyManager) applicationContext.getBean("manager");
}
I usually setup the internal error page inside the init method of my WebApplication class. Sometimes I also mount some bookmarkable pages:
public class MyApplication extends WebApplication {
#Override
protected void init() {
IApplicationSettings applicationSettings = getApplicationSettings();
applicationSettings.setInternalErrorPage(ErrorPage.class);
mountBookmarkablePage("privacy", PrivacyPage.class);
}
//............
}
My WebPage classes usually depend on my manager class, for instance:
public class ErrorPage extends WebPage {
public ErrorPage() {
MyApplication application = (MyApplication) getApplication();
add(new EmailLink(application.getManager().getMailSupport()));
}
}
So, my WebApplication class refers to one or more pages, and my pages refer to the WebApplication class. Is this a circular dependency? If yes, how can I avoid it?
I would say it is not a circular dependency but it is a configuration.
However, I think you can always inject your manager bean to the web page class as well with autowiring.
EDIT:
You may also need to enable spring annotations in applicationContext.xml as well and add some new dependencies if not already in classpath
see applicationContext.xml sample at this address and your will be pretty much similar except the scan package name. Update those values accordingly.
public class ErrorPage extends WebPage {
#Autowired
private MyManager myManager;
//setter getter methods as well
}