Wicket WebApplication/WebPage circular dependency using spring - java

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
}

Related

Spring integration test with custom application context

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));
}
}

Jersey: how to access the global application object?

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.

How to access Spring Bean from JerseyTest subclass

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."

EJB cannot inject other EJB

I do have a EJB ActionService which I can inject into other EJBs, that is working fine.
Now I created another EJB:
#Stateless
public class ActionsPerDateDataSet extends ScriptedDataSetEventAdapter {
#EJB
ActionService actionService;
#Override
public void open(IDataSetInstance dataSet) {
actionService.foo() // However actionService is null here!
}
}
Where the ScriptedDataSetEventAdapter comes from another framework (BIRT).
However now my actionService is always null. I can not understand why
You should introduce the lib as ejbModule in ear file , so that container search the jar file and deploy it and inject it whenever it needs
ActionService has an interface with the #local annotation or if this is a class it has to have the annotation #LocalBean.
(this to be able to access the instance of it at runtime)
In case it is an interface and if it has multiple implementations you will have to reference the implementation you need using #EJB (beanName = "nameOfImplementation") in case it is a class where #LocalBean is used to use #EJB (name = "nameEjb")
Interface with #Local
Class with #LocalBean
In the aggregation class
Interface with multiple implementations #EJB(beanName="nameOfImplementation")
Class #EJB(name="nameEjb")
note: implements an interface for ActionService with #Local and test
note: add trace the console log to know if the class is being initialized as an ejb:ActionService
Did you try using CDI? I think it is worth a shot. You need to place an empty beans.xml inside your meta-inf folder and change #EJB to #Inject. But the only way this could work is if you have the external lib and your war/jar file in the same deployment unit.
if this does not work you will need to use JNDI for looking up your bean:
http://docs.oracle.com/javaee/6/tutorial/doc/gipjf.html
It is possible that the class ScriptedDataSetEventAdapter
can not be initialized in the EJB Container (First part of the cycle)
and as the initialization is not correct, the dependency injection (#EJB and #Inject) is not made.
What you could do is change the Design of your EJB and instead it's extends "ScriptedDataSetEventAdapter"
change it to a composition.
#Stateless
public class ActionsPerDateDataSet {
ScriptedDataSetEventAdapter scriptedDataSetEventAdapter;
#EJB
ActionService actionService;
#PostConstruct
public void init (){
try {
scriptedDataSetEventAdapter = new ScriptedDataSetEventAdapter();
} catch( AppException e){
}
}
#Override
public void open(IDataSetInstance dataSet) {
actionService.foo() // However actionService is null here!
}
}

JAX-RS Application subclass injection

I'm writing custom JAX-RS 2.0 application (under Jersey 2.3.1) which holds some data for use by all the resources.
public class WebApp extends org.glassfish.jersey.server.ResourceConfig {
public WebApp() {
packages("my.resources.package");
}
}
(I could use API's javax.ws.rs.core.Application as well, the described result is the same)
Then I inject the object into a resource
#Path("test")
public class Test {
#Context
Application app;
#GET
#Path("test")
public String test() {
return "Application class: " + app.getClass();
}
}
However, the result of a call is
Application class: class org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig
which makes me use some ugly tricks like
if (app instanceof WebApp) {
return (WebApp) app;
} else if (app instanceof ResourceConfig) {
return (WebApp) ((ResourceConfig) app).getApplication();
}
My understanding of JAX-RS 2.0 spec section 9.2.1:
The instance of the application-supplied Application subclass can be injected into a class field or method parameter using the #Context annotation. Access to the Application subclass instance allows configuration information to be centralized in that class. Note that this cannot be injected into the Application subclass itself since this would create a circular dependency.
is that application-supplied Application subclass is mine WebApp, not JAX-RS implementation-specific wrapper.
Also, changing this fragment
#Context
Application app;
to this
#Context
WebApp app;
causes app to be null, due to ClassCastException during context injection, so the declared type doesn't matter.
Is it a bug in Jersey or my misunderstanding?
UPDATE: I checked the behaviour under RESTEasy 3.0. The injected object is my WebApp, without any wrappers. I'd call it a bug in Jersey.
This doesn't seem like a bug. According to JAX-RS 2.0 spec you can inject Application into your resource classes (for example) but it does not say anything about directly injecting custom extensions of the Application. Not sure what your use-case is but you can register custom HK2 binder that will allow you to inject directly WebApp into resources:
public class WebApp extends org.glassfish.jersey.server.ResourceConfig {
public WebApp() {
packages("my.resources.package");
register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
#Override
protected void configure() {
bind(WebApp.this);
}
});
}
}
I too have encountered this using Jersey 2.4.1.
FWIW: I agree it seems like a bug according to the spec para 8.2.1. The statement "The instance of the application-supplied Application subclass" seems perfectly clear.
I have an alternative workaround that doesn't involve glassfish.hk2 but still concentrates the Jersey-specific code in the Application-derived class.
public class MyApp extends ResourceConfig {
...
static MyApp getInstance( Application application) {
try {
// for a conformant implementation
return (MyApp) application;
} catch (ClassCastException e) {
// Jersey 2.4.1 workaround
ResourceConfig rc = (ResourceConfig) application;
return (MyApp) rc.getApplication();
}
}
...
}
public class MyResource {
...
#Context Application application;
...
SomeMethod() {
... MyApp.getInstance( application);
}
}
Hope this is useful.
This appears to be fixed in a later version og Jersey. The same approach works for me with Jersey 2.16 at least. My injected Application object is of the correct subclass without any wrapping whatsoever.
Edit: Or maybe the version is irrelevant after all. Please see the comments to this answer.

Categories