How Can I create Spring Bean outside of Spring Application Context - java

I'm developing some kind of plugin which should be called by external java app.
my Plugin is using Spring and of cause I tried to simplify my as I can:
Let's consider that this is 3d party app and it's calling my plugin in its main function.
public class ThirdPartyClass {
public static void main(String[] args) {
GeneralPlugin plugin = new MyPlugin();
plugin.init();
//calling ext. plugin functionality.
plugin.method1();
}
}
Now this is my plugin
package com.vanilla.spring;
#Component
public class MyPlugin implements GeneralPlugin{
#Autowired
Dao mydao;
public void init(){
//some initiation logic goes here...
}
public void method1(){
dao.delete();
}
}
Now my Dao
package com.vanilla.spring;
Component(value="dao")
public class MyDao {
public void delete(){
//SOME DATABASE LOGIC GOES HERE!!!
}
}
now my XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
<context:annotation-config />
<context:component-scan base-package="com.vanilla.spring"></context:component-scan>
</beans>
My problem is that my dao is null and I'm getting NullPointerException when accessing dao object.
I
m believe it happens because I'm initiation bean out of Application context and as the result my Autowiring is not working.
Is there any other way to make autowiring work?

"Spring beans" are just that: Java beans. They have no intrinsic abilities other than those given to them by inheritance or object instantiation.
The Spring Application Context is responsible for creating the bean and "wiring" it, which is the process of creating other beans in the context and calling the bean's setters (and constructors) with the results to configure them. To do this is uses the XML configuration file and annotations to decide what to create and where to put it.
If you aren't going to use an actual Application Context, then you have to do all of that work yourself, manually. That is, create the DAO with the proper data source, create the plugin bean, and set the DAO on the plugin bean.
In this specific example, since the 3rd party application controls the instantiation of your plugin bean, you will likely have to either a) create the DAO in the plugin constructor (which is what you're using Spring to avoid in the first place), or b) create an Application Context in the plugin constructor and reference the beans the plugin needs by querying the context. This isn't quite as useful as letting the context do everything, but at least you don't have to configure the rest of the beans your application uses manually (with user names, connection URLs, etc).
If you go the second route you would then need the Spring configuration file somewhere in the classpath or somehow otherwise able to be referenced by the plugin bean.

Related

Understanding "globalValidator" in Spring MVC

I have custom validator and I register it in my controller
#Controller
public class MyController {
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new FooValidator());
}
#RequestMapping("/foo", method=RequestMethod.POST)
public void processFoo(#Valid Foo foo) { ... }
}
but I want to register in other controllers also,so to be able just to write #Valid and the Foo object to be validated. From what I see I understand that I can use #ControllerAdviced class which to register the validator on every controller, or to use
<mvc:annotation-driven validator="globalValidator"/>
But how to register my validator, how Spring understand which Validator I want to make global one? Scans for every implementing Validator class? Can I do it with xml configuration? How to use this approach?
I do not understand the Spring's description:
The alternative is to call setValidator(Validator) on the global
WebBindingInitializer. This approach allows you to configure a
Validator instance across all annotated controllers. This can be
achieved by using the SpringMVC namespace:
xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xss
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:annotation-driven validator="globalValidator"/>
The documentation is quite clear on the Validation section:
In Spring MVC you can configure it for use as a global Validator
instance, to be used whenever an #Valid or #Validated controller
method argument is encountered, and/or as a local Validator within a
controller through an #InitBinder method. Global and local validator
instances can be combined to provide composite validation
If I understand correctly in your example the FooValidator you want to use it upon every validation as global Validator so define it as a bean and inject it as you show directly in the mvc:annotation-driven XML entry as you are showing already.
On top of that per-Controller you can have custom (applied on top only on that Controller-responsible forms) via the #InitBinder annotation.
As a side note, in your #RequestMapping method receiving the POST request where your #Valid parameter is: You can have a BindingResult entry right after that to take decisions on routes etc. In your example:
#RequestMapping("/foo", method=RequestMethod.POST)
public String processFoo(#Valid Foo foo, BindingResult result) {
if(result.hasErrors()) {
return "go/that/way";
}
//..
}

Make ServletContextListener spring aware

I am plugging in Spring to existing Java EE web Application. I have following lines in my web.xml:
<listener>
<listener-class>com.MyContextListener</listener-class>
</listener>
And Following MyContextListener class?
public class MyContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
//...
}
}
What should I do to make MyContextListener be managed by Spring?
Edited:
My assumption is: Spring should create all servlets and all web app infrastructure so everything happened in contextInitialized method of MyContextListener should be somehow handled by Spring. How can I achieve, by implementing some interface I suppose. Correct me if I am wrong. Thanks!
Well,
We had a similar scenario of configuring an exiting Jersey web services app to use Spring for dependency injection. Our Jersey webapp had extended ContextLoaderListener as follow
public class XServletContextListener extends ContextLoaderListener {
...
#Override
public void contextInitialized(ServletContextEvent arg0) {
super.contextInitialized(arg0);
....
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
super.contextDestroyed(arg0);
....
}
}
where ContextLoaderListener is
import org.springframework.web.context.ContextLoaderListener;
We included the jersey-spring bridge with all spring dependencies including applicationContext.xml as follow
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.xxx.*" />
....
....
</beans>
And obviously needed to make sure that XServletContextListener is included in the web.xml as follow
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>com.xxx.**.XServletContextListener</listener-class>
</listener>
Followed by servlet and its init-param values and servlet mapping. You can obviously adopt annotation config in place of xml confib in which case you would need to use WebListener annotation.
We use a variety of annotations such as
#Component for objects
#Service for services
#Repository for DAOs
#Controller for controllers/resources
#ContextConfiguration for tests
Everything is loaded and autowired by Spring framework.
What should I do to make MyContextListener be managed by Spring?
It depends on which configuration way you are using. Anyway, you should tell directly Spring to use the class you have declared. That could be done by the following way:
#WebListener
public class MyContextListener implements ServletContextListener { ... }
A class marked with this annotation (the Servlet 3.0 specification, 8.1.4) must implement one of these interfaces
HttpSessionAttributeListener
HttpSessionListener
ServletContextAttributeListener
ServletContextListener (+)
ServletRequestAttributeListener
ServletRequestListener
HttpSessionIdListener
that it actually does.
Personally, I prefer a meta-annotation based approach which makes my configuration shorter and more concise.
Spring should create all servlets and all web app infrastructure so everything happened in contextInitialized method of MyContextListener should be somehow handled by Spring.
Yes, Spring will do it for you if you provide some information which could help it to register / configure / create / manage an instance.
The information may be either meta-information (a template that tells how to create an instance, like BeanDefinitions) or a completed instance itself (usually, it gets passed programmatically that, in turn, leads to writing a huge amount of code).
How can I achieve, by implementing some interface I suppose.
You are implementing an interface to make your listener a listener (a class that describes specific methods which will be called at some points of time). Spring, itself, is responsible for guaranteeing such calls at those points of time, placing an object in the existing web infrastructure before.
Either annotate the class with #WebListener or the method with #Bean
Annotate where you create a new instance of MyContextListener with #Bean if using Java Configs with Spring Boot.

Hippo CMS Component (Spring managed) in a catalog (Howto use a spring managed catalog component)

I am having problems setting up Spring in Hippo CMS in combination with a catalog.
I had a catalog configured with a componentclassname:ServiceLinkListComponent so I could drag & drop the component into a placeholder on a page in the channel manager.
After following the example at http://svn.onehippo.org/repos/hippo/hippo-cms7/testsuite/trunk/ I added Spring support so I had to change the componentclassname to componentclassname:SpringBridgeHstComponent
Unfortunately I can't link any Document with the component anymore cause the #ParametersInfo is not recognized cause it resides within the bean in the SpringBridgeHstComponent.
How can I use a spring managed catalog component?
Component Class
#ParametersInfo(type = ServiceLinkListComponentInfo.class)
#Component
#Scope(value = "prototype")
public class ServiceLinkListComponent extends CommonComponent {
private static Logger log = LoggerFactory.getLogger(ServiceLinkListComponent.class);
#Autowired
public TestService testService;
#Override
public void doBeforeRender(final HstRequest request, final HstResponse response) {
super.doBeforeRender(request, response);
final ServiceLinkListComponentInfo paramInfo = getComponentParametersInfo(request);
final String documentPath = paramInfo.getDocument();
log.debug("Calling EssentialsDocumentComponent for document path: [{}]", documentPath);
//String test = testService.test();
setContentBeanForPath(documentPath, request, response);
request.setAttribute(REQUEST_ATTR_PARAM_INFO, paramInfo);
}
}
Service Class
#Component("testService")
public class TestService {
public String test(){
return "hello";
}
}
applicationContext.xml (in resources/META-INF.hst-assembly.overrides)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxx.yyy" />
</beans>
Repo
I found a way to do what I want but I consider it a dirty workaround. Maybe some people from HippoCMS can help here.
I created another class:
#ParametersInfo(type = ServiceLinkListComponentInfo.class)
public class ServiceLinkListSpringHstComponent extends SpringBridgeHstComponent {}
This class needs to be set in the catalog (instead the SpringBridgeHstComponent). I also tried a nested class to get rid of the second file but hippo doesn't find it.
I'm the original writer of the bridge class. I think you already found the current limitation and a reasonable workaround. :-)
I agree that it's not so ideal with the workaround solution because you need to add as many child class of SpringBridgeHstComponent as component configurations.
Could you please file a JIRA ticket here to imporove this?
- https://issues.onehippo.com/projects/HSTTWO
By the way, I originally thought this can be improved only in the bridge class level, but it seems to require improvements in the container level (especially with relevance module (for targeting)). So, it needs more investigation with that module.

Autowiring a DAO into a Service

I'm using Spring and am trying to autowire (using annotations) a DAO into a Service, which is then wired into a controller. Having
#Autowired
Movie movieDao;
on its own doesn't work, as I think the new method gets called, so that DAO isn't managed by Spring. The following does work, but it will look messy if I have to copy and paste that context configuration into each method
#Autowired
MovieDao movieDao;
#Override
public List<Movie> findAll() {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load("classpath:app-context.xml");
context.refresh();
MovieDao movieDao = (MovieDao) context.getBean("movieDao", MovieDao.class);
return movieDao.findAll();
}
where this code is in my Service class. Is there a more elegant way to ensure that my DAO is initialised properly, rather than copying and pasting the first 4 lines of that method into each Service method?
[edit] The class that contains the code above is a class called MovieServiceImpl, and it essentially corresponds to the DataServicesImpl class in the architecture described on this page. (I'll add a summary/description of that architecture and what I'm trying to do soon). This is the code: http://pastebin.com/EiTC3bkj
I think that the main problem is that you want to instantiate your service directly (with new) and not with Spring:
MovieService movieService = new MovieServiceImpl();
When you do this, your MovieServiceImpl instance is constructed but not initialised (the field #Autowired MovieDao is null).
If you want to instantiate properly your object with field injection, you need to use Spring. As explained in the documentation or in this example, you can automatically detect all your annotated beans and initialize them in your context with the component scanning.
Example
In your case, using annotiations on (#Component, #Service, etc) and in (#Autowired, #Inject, etc) your beans, your project could look like this:
Spring configuration app-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Use component scanning to auto-discover your beans (by annotation) and initialize them -->
<context:component-scan base-package="com.se325.a01" />
<!-- No need to declare manually your beans, because beans are auto-discovered thanks to <context:component-scan/> -->
</beans>
Entry point of your application App.java
package com.se325.a01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.se325.a01.model.Movie;
import com.se325.a01.service.MovieService;
public class App {
public static void main(String[] args) {
// Let's create the Spring context based on your app-context.xml
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"app-context.xml"});
// Now your context is ready. All beans are initialised.
// You can retrieve and use your MovieService
MovieService movieService = context.getBean("movieService");
Movie matrixMovie = new Movie("Matrix");
movieService.create(matrixMovie);
}
}
In fact, when you are using Spring, it is really important to understand how the context is initialized. In the example above, it can be sum up as:
Your entry point App#main is called.
The configuration app-context.xml is loaded by ClassPathXmlApplicationContext.
The package com.se325.a01 is scanned thanks to the line <context:component-scan base-package="com.se325.a01" />. All annotated beans (#Component, #Service, etc) are contructed but not yet initialised.
When all the beans are constructed, Spring initialises them by injecting dependencies. In the example, the #Autowired annotations which mark the dependencies are also discovered thanks to the line <context:component-scan ... \>.
The context is ready with all beans :)
Notes
All this answer explains how you can use component scanning and annotations to use Spring in a main entry point. However, if you are developing a server application, the entry point is the WEB-INF/web.xml.
As #chrylis said, field injection is error prone. Prefer using constructor-based injection.

Customizing Seam3 internationalization Messages

I am using Seam 3 Internationalization packages to implement messaging in my application.
In short, this is what I am doing:
Importing/Injecting required classes:
import org.jboss.seam.international.status.Messages;
import javax.inject.Inject;
#Inject
private Messages messages;
When an error occurs, I create a Message in my backing bean:
messages.error(new BundleKey("AppMsgResources", "errorMsgKey")).defaults("Error: Something bad happened!");
Lastly I display the message in my faces page like so:
<h:messages />
Very standard so far I think ...
The custom logic I want to implement is to be able to first check a database table (lets call this table MessageBundleOverride) for a matching message key. If it exists, I want to use the value from the MessageBundleOverride table and not the property file. If it doesnt exist or is empty, I want to use the value found in the property file.
I'm thinking there is a Weld/CDI way of doing this where I can implement the Messages interface and register it with seam somehow so that it picks up my messages implementation during "inject" and not the default MessagesImpl implementation that comes with Seam Internationalization package. I am a little new to Seam / Weld so not sure if this is a simple thing to do.
any help is much appreciated,
thanks!
Figured out one way of getting this done after reading Weld docs:
http://docs.jboss.org/weld/reference/latest/en-US/html/injection.html#alternatives
#Alternative
#RequestScoped
public class MyMessages extends MessagesImpl {
/*
* Override a method that you want to customize or write new code here
*/
#Override
public Set<Message> getAll() {
Set<Message> allMessages = super.getAll();
// do some custom logic here
applyOverrides(allMessages);
return allMessages;
}
...
// override any other method as needed
// You will probably have to override everything so it probably
// wouldnt make sense to extend the existing implementation)
...
}
In the beans.xml file, you will have to declare this new class as an alternative to the default:
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>com.company.project.view.messages.MyMessages</class>
</alternatives>
</beans>
And that should do it so long as weld is picking up the classes in the package you have MyMessages defined in.

Categories