We have a existing system with Q2 Server and Spring MVC with following configuration. Q2 server was created when a Httpservlet is initiated and It worked perfectly and spring beans can be autowired withn ISORequestlistner. This is now being converted to Spring boot 2.3. Once I initiated the same Httpservlet using ServletRegistrationBean in Spring boot, Q2 server is initiated and can send requst to it. But auto-wiring is not working. Once I check. Once he request is processing inside the ISORequest listner, Spring context is not visible since Q2 server is using different class loader.
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="DownloadServer-A">
<attr name="port" type="java.lang.Integer">6400</attr>
<attr name="minSessions" type="java.lang.Integer">10</attr>
<attr name="maxSessions" type="java.lang.Integer">1100</attr>
<channel name="DownloadServer-A-Channel" class="org.jpos.iso.channel.NACChannel" logger="Q2"
packager="org.jpos.iso.packager.GenericPackager" header="6000010000">
<property name="packager-config" value="/app/repository/q2serverconfig/resources/Download_generic.xml" />
</channel>
<request-listener class="DownloadServerAListener" logger="Q2">
<property name="space" value="transient:default" />
<property name="queue" value="TransactionQueue" />
<property name="timeout" value="35000" />
</request-listener>
</server>
1st Try
Tried creating static ApplicationContext using ApplicationContextAware and tried it in the ISORequestListner. But It becomes null when TCP request received to Q2 server.
2nd Try
I tried several solutions like below github repo. But I didn't work.
https://github.com/vmantek/chimera
Have anyone tried to start ISO Server inside the Spring Application context as a bean? What I mean is that start ISO Server in #Configuration class with using Q2.start(). Q2.start will start in a separate class loader. I don't want it to happen.
I was looking for these few days and I was trying several ways.
The issue is that Spring is starting in a Specific class loader. But when you start Q2 Server as
Q2 q2Server = new Q2(<deploydir>);
q2Server.start();
Q2 server is starting in a different classloader. Therefore SpringContext was not available for auto wiring. SpringBeanAutowiringSupport depends on ContextLoader to retrieve the current application context, and always get null.
Workaround
You could register a bean that implements org.springframework.boot.context.embedded.ServletContextInitializer to retrieve the application context during startup().
#Configuration
public class WebApplicationContextLocator implements ServletContextInitializer {
private static WebApplicationContext webApplicationContext;
public static WebApplicationContext getCurrentWebApplicationContext() {
return webApplicationContext;
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
webApplicationContext =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
}
}
Then you could implement self-autowiring in your ISORequestListener class.
#Component
public class ServiceImpl implements ISORequestListener {
#Autowired
private BackendService backendService;
public ServiceImpl() {
AutowiredAnnotationBeanPostProcessor bpp = new
AutowiredAnnotationBeanPostProcessor();
WebApplicationContext currentContext =
WebApplicationContextLocator.getCurrentWebApplicationContext();
bpp.setBeanFactory(currentContext.getAutowireCapableBeanFactory());
bpp.processInjection(this);
}
}
Then auto wiring working perfectly. I was inspired by following answer.
Spring Boot register JAX-WS webservice as bean
Related
We have a Spring managed application that uses another jar as a dependency, which contains a Spring managed service class, that need to use some value injected from property file
The main application with the Spring context setup
public static void main(String[] args) {
GenericXmlApplicationContext appContext = new GenericXmlApplicationContext("applicationContext.xml");
SomeClass someClass = (SomeClass) appContext.getBean("someClass");
someClass.someMethod();
...
The class which calls the service from the dependent jar
public class SomeClass {
private ServiceFromTheOtherJar serviceFromTheOtherJar;
public SomeClass(ServiceFromTheOtherJar serviceFromTheOtherJar) {
this.serviceFromTheOtherJar = serviceFromTheOtherJar;
}
public void someMethod() {
serviceFromTheOtherJar.call();
...
The applicationContext.xml of the main app
<bean name="serviceFromTheOtherJar" class="com...ServiceFromTheOtherJar"/>
<bean name="someClass" class="com...SomeClass">
<constructor-arg ref="serviceFromTheOtherJar"/>
</bean>
The service class in the dependent jar
public class ServiceFromTheOtherJar {
private String someFieldWeWantToFillFromPropertyFile;
public void setSomeFieldWeWantToFillFromPropertyFile(String someFieldWeWantToFillFromPropertyFile) {
this.someFieldWeWantToFillFromPropertyFile = someFieldWeWantToFillFromPropertyFile;
}
public void call() {
//we would like to use the filled someFieldWeWantToFillFromPropertyFile here
...
And of course we have an application.properties file in the dependent jar, that contains the property value that we would like to inject into someFieldWeWantToFillFromPropertyFile
Now we can add the dependent jar as a dependency to the main app; when the main app is being executed then its Spring context is getting set up all right, and ServiceFromTheOtherJar.call() method gets called as expected; however someFieldWeWantToFillFromPropertyFile does not get filled from the property file whatever we tried so far (e.g. #PropertySource({"application.properties"}), Environment.getProperty(...) etc.)
Restrictions
We have Spring 3 version in both jars, and that has to remain so due to the deployment environment; so Spring 4 solutions are out of question
As you see above, the main app currently uses GenericXmlApplicationContext and changing that seem to indicate a significant rewriting of the application. Therefore e.g. it seemed to be not possible to use #Service annotation on ServiceFromTheOtherJar because it caused BeanCreationException during the execution and context setup
To read values from property file you have to add the following bean to applicationContext.xml.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:application.properties" />
</bean>
Assuming the application.properties-file contains a definition like this
myValue=Hello World
the definition of the service-bean should by extended like this
<bean name="serviceFromTheOtherJar" class="com...ServiceFromTheOtherJar">
<property name="someFieldWeWantToFillFromPropertyFile" value="${myValue}" />
</bean>
Now Spring will look for the application.properties-file in classpath and set the attribute of the service bean according to myValue.
I have a spring rest application which has a Rest Controller as below
#RestController
public class IngestorController
{
#Autowired
private IngestorService ingestorService;
#RequestMapping( value = "/ingestor/createAndDeploy/{ingestorName}", method = RequestMethod.POST )
public void createAndDeploy( #PathVariable String ingestorName )
{
ingestorService.createAndDeploy( ingestorName );
}
}
Simlilarly I have a Service Bean as below
#Service
public class IngestorService
{
#Autowired
private IngestorCommandBuilder ingestorCommandBuilder;
private String uri;
private DeployTemplate deployTemplate;
public void init() throws URISyntaxException
{
deployTemplate = new DeployTemplate( new URI( uri ) );
}
#Transactional
public void createAndDeploy( Ingestor ingestor )
{
//.....
}
}
I have the Spring config as show below
<bean id="ingestorCommandBuilder" class="org.amaze.server.ingestor.IngestorCommandBuilder" />
<bean id="ingestorService" class="org.amaze.server.service.IngestorService" init-method="init">
<property name="uri" value="http://localhost:15217" />
</bean>
<bean id="ingestorController" class="org.amaze.server.controller.IngestorController"/>
When ever I try to start the application context the application context starts and it hits the init method in the IngestorService, deployTemplate object also initilized for the service bean.
But this bean is not autowired for the IngestorController. When I hit the rest endpoint from postman, the service bean has deployTemplate property as null.. The object that is assigned to the ingestorService variable in the Controller is a different object not the one which was called for the init method...
I tried making the service bean singleton (Even if the default scope is singleton) but dint work...
I am not able to find out the mistake I am doing.. Any suggestions appreciated...
If you use annotation-based configuration, you mostly dont need to describe all your beans in application context xml file. Annotations are all you need to autowire service.
To define your init method properly, use #PostConstruct annotation. Properties can be easily moved to externat .properties file and injected to your code with #Value annotation.
Alternatively, use #Qualifier with #Autowired.
First ensure that you have:
<context:annotation-config />
In your Spring config. Now you have severael alternatives:
<context:component-scan base-package="your.package.com" />
to scan for components, or
<!-- autowiring byName, bean name should be same as the property name annotate your ingestorservice with #Service("ingestorServiceByName") -->
<bean name="ingestorServiceByName" class="your.package.com.IngestorService" autowire="byName" />
<!-- autowiring byType, tif there are more bean of the same "general" type, this will not work and you will have to use qualifier or mark one bean as #Primary -->
<bean name="ingestorServiceByType" class="your.package.com.IngestorService" autowire="byType" />
<!-- autowiring by constructor is similar to type, but the DI will be done by constructor -->
<bean name="ingestorServiceConstructor" class="your.package.com.IngestorService" autowire="constructor" />
Please include your full Spring configuration to make it easier to analyze your problem.
When you are using Annotation based DI, you need not define the beans in XML.
#PostConstruct can be used to replace your init-method of xml config.
Just use
<context:component-scan base-package="..."/> <mvc:annotation-driven/>
I'm newbie to Spring JMX. And i want to monitor the prototype beans in my project through Spring JMX, I created a sample project to register a bean(Singleton) with Spring's MbeanExporter that is working. Then i googled to register the Non-Singleton bean with Spring JMX and monitor it but i didn't found any thing helpful.
I came across a Spring forum post that describes my problem but that answer is not to the point.
I kept googling for this issue and i found few posts on the stackoverlow itself that really helped me. Just copying the code here:-
#Component("MyPrototypeScopedBeanName")
#Scope(value = "prototype")
#ManagedResource
public class MyPrototypeScopedBeanName implements SelfNaming
#Autowired
MBeanExporter exporter;
.
.
#PostConstruct
private void init() throws Exception {
exporter.registerManagedResource(this);
}
.
.
.
#Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return new ObjectName("com.foobar", "name", this.toString());
}
Also, you may want to configure your exporter to ignore this during autodetect, because the way autodetect works with prototypes, it will create yet another instance for itself that will add an additional instance to your JMX console.
<property name="autodetect" value="true"/>
<!-- Done to prevent creation of additional prototype during autodetect routine -->
<property name="excludedBeans">
<list>
<value>MyPrototypeScopedBeanName</value>
</list>
</property>
Stackoverflow link
Another link
Courtesy:- #theJC
I have spring based multimodule application. And in my DAO module the DB (embedded derby) is started and created by the class the implements ApplicationListener.
Problem that in the logs the huge stacktrace from Spring which say that there is no db(couldn't get connection).
Still, my application works without any problems. This stacktrace appeared before the ApplicationListener invoked and the db is created. Actually, I see it only when I am starting the application the first time on the machine, because the db created only this time, than it just used.
So my question is whow to avoid this exception in logs? Maybe there is spring or hibenate setup not connect to the db before the application context fully loaded? Or invoke the code that creates db by some other listener?
Well here is the way I do : the ROOT context contains the datasource, the dao, the service and the transaction manager. In XML config, the declaration of the database is :
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:url="jdbc:derby:/path/to/database;create=TRUE"
p:username="user" p:password="pwd"
p:driverClassName="org.apache.derby.jdbc.EmbeddedDriver"/>
it can then be used to declare a session factory for hibernate and an associated DAO as :
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
id="sessionFactory" p:dataSource-ref="datasource">
<!-- hibernate config -->
...
</bean>
<bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
name="transactionManager" p:sessionFactory-ref="sessionFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="myDao" class="... .myDaoImpl" p:sessionFactory-ref="sessionFactory" .../>
That way all is created by spring, that ensures that the creation order is correct. Of course the same is possible in Java config with the same logic.
I suppose you are fetching some data from database from inside spring beans that are being created. Perhaps thru #PostConstruct or other way. Remember that until spring context is fully loaded some beans can have injected uninitialized beans.
So do not use DB, do not call any DAOs until you are sure that spring context is fully initialized.
To do such initial calls to DAOs try such patter that guarantees spring context completness:
#Component
public class SpringContextMonitor implements ApplicationListener<ApplicationEvent> {
#Autowired
private SomeDao dao;
...
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
onStart((ContextRefreshedEvent) event);
}
}
private void onStart(ContextRefreshedEvent event) {
// do your initialization here
dao.getSomething();
dao2.getSomething();
...
}
...
}
The onStart method in above example is place where you are sure that all beans are fully initialized
I want to write a simple servlet in JBoss which will call a method on a Spring bean. The purpose is to allow a user to kick off an internal job by hitting a URL.
What is the easiest way to get hold of a reference to my Spring bean in the servlet?
JBoss web services allow you to inject a WebServiceContext into your service class using an #Resource annotation. Is there anything comparable that works in plain servlets? A web service to solve this particular problem would be using a sledgehammer to crush a nut.
There is a much more sophisticated way to do that. There is SpringBeanAutowiringSupportinside org.springframework.web.context.support that allows you building something like this:
public class MyServlet extends HttpServlet {
#Autowired
private MyService myService;
public void init(ServletConfig config) {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
}
This will cause Spring to lookup the ApplicationContext tied to that ServletContext (e.g. created via ContextLoaderListener) and inject the Spring beans available in that ApplicationContext.
Your servlet can use WebApplicationContextUtils to get the application context, but then your servlet code will have a direct dependency on the Spring Framework.
Another solution is configure the application context to export the Spring bean to the servlet context as an attribute:
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="jobbie" value-ref="springifiedJobbie"/>
</map>
</property>
</bean>
Your servlet can retrieve the bean from the servlet context using
SpringifiedJobbie jobbie = (SpringifiedJobbie) getServletContext().getAttribute("jobbie");
I've found one way to do it:
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
SpringifiedJobbie jobbie = (SpringifiedJobbie)context.getBean("springifiedJobbie");