#Service
public class LogProcessorServiceImpl {
#Autowired
private static ApplicationConfigurationService applicationConfigurationService;
public static void processPageRequestsLogs() {
if(applicationConfigurationService==null) {
System.out.println("autowire failed");
}
I have the ApplicationConfigurationService service autowired like this all over the place and it works fine. The package of this class is being scanned so that's not the problem. It might be related to the way this particular method is called. I have a servlet that is loaded after all other servlets and it fires of a timer that executes the method above with 60 second delay. I assume all autowiring should be completed.
public class ProcessSchedulerServlet implements javax.servlet.Servlet {
Timer timer=new Timer();
#Override
public void init(ServletConfig arg0) throws ServletException {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
LogProcessorServiceImpl.processPageRequestsLogs();
}
}, 60*1000, 120*1000);
}
Here's what happens as soon as I true to use ApplicationConfigurationService:
autowire failed
Exception in thread "Timer-1" java.lang.NullPointerException
at com.siteadmin.services.impl.LogProcessorServiceImpl.processPageRequestsLogs(LogProcessorServiceImpl.java:39)
at com.siteadmin.servlets.ProcessSchedulerServlet$1.run(ProcessSchedulerServlet.java:20)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
=== 2012-11-18 ============================================================
See also: How to go about Spring autowiring?
You can't autowire static fields in Spring, this is discussed here
As alternative, if your LogProcessorServiceresides in the root web application context, you can
autowire it with Spring WebApplicationContextUtils utility class.
public class ProcessSchedulerServlet implements javax.servlet.Servlet {
Timer timer=new Timer();
#Autowired
LogProcessorService logProcessorService;
#Override
public void init(ServletConfig arg0) throws ServletException {
WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext())
.getAutowireCapableBeanFactory().autowireBean(this);
final LogProcessorService svc = this.logProcessorService;
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
svc.processPageRequestsLogs();
}
}, 60*1000, 120*1000);
In general, you should avoid using Java singletons, where using Spring singletons is enough.
Also, if you declared LogProcessorServiceImpl with a #Service annotation, that implies it to be a Spring singleton, so you should not use static fields there at all.
P.S. this answer is about autowiring, it assumes that the idea with TimerTask is correct, in the real apps consider using the Spring Scheduling API
Related
I am currently writing a Spring boot application that will perform loadtests on another app. I want to use Gatling to manage the tests, but I need it to access the configuration that I defined in beans of my Spring app.
Here is what I would like to see working :
public class MySimulation extends Simulation {
#Autowired
private JMSConnectionFactoryBeanClass myConnectionFactory;
public MySimulation() {
JmsProtocolBuilder jmsProtocol = jms.connectionFactory(myBean);
ScenarioBuilder scn = scenario("My Simulation Scenario")
.exec(
jms("test")
.send()
.queue("myQueue")
.textMessage("message")
);
{
setUp(
scn.injectOpen(rampUsers(10).during(5))
).protocols(jmsProtocol);
}
}
When I hardcode the configuration into the simulation class and remove all #Autowired thing, everything works, so it must be comming from the dependency injection. Does anybody know if there is a way to us spring beans in a gatling simulation ?
Following Stéphane Landelle advice, here is what I came up with, but instead of creating my app context inside of the simulation, I figured out how to run the simulation along with my spring app using gatling API :
public class GatlingRunner {
public static void run() {
GatlingPropertiesBuilder props = new GatlingPropertiesBuilder();
props.simulationClass("path.to.Simulation");
Gatling.fromMap(props.build());
}
}
This is how I modified my spring app :
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
GatlingRunner.run();
}
}
Finally, to use spring beans in the simulation, I wrote a context provider that would make the link between spring and gatling :
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
ApplicationContextProvider.context = context;
}
}
Now, to get a bean inside of the simulation, all I needed was this :
Bean myBean = ApplicationContextProvider.getApplicationContext()
.getBean("myBean", Bean.class)
You can't use #Autowired. You have to create an ApplicationContext programmatically and pull the JMSConnectionFactoryBeanClass from it.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = MySimulation.class, loader = SpringApplicationContextLoader.class)
public class MySimulation extends GatlingTest {
#Autowired
private JMSConnectionFactoryBeanClass myConnectionFactory;
#Test
public void test() {
...
}
}
I am writing a custom evaluator in which I want to autowire another bean. I am unable to do so as evaluator gets initialized by logger where as beans are initialized by spring context. Below is the sample of my code:
In logback-spring.xml:
<appender name="myAppender" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="com.package.CustomEvaluator">
<marker>FATAL</marker>
<interval>1000000</interval>
</evaluator>
</appender>
My custom evaluator:
#Slf4j
#Component
public class CustomEvaluator extends OnMarkerEvaluator {
#Autowired
private MyService myService;
#Override
public boolean evaluate(ILoggingEvent event) throws EvaluationException {
\\logic goes here
}
}
I am getting object of MyService always as null(which is expected). Is there any work around for this?
It don't think its possible because the Evaluator being an internal logback abstraction is not managed / initialized by spring, so obviously spring can't autowire anything into the evaluator.
In addition note, that logback gets initialized even before application context starts.
Of course you could provide some global holder class for the application context and set the context to it in the main method, and then get the reference to it in this evaluator, something like this:
public class ApplicationContextHolder {
private static ApplicationContext context;
// call it in main() method
public static void setContext(ApplicationContext ctx) {context = ctx;}
public static ApplicationContext getContext() {return context;}
}
class CustomEvaluator extends OnMarkerEvaluator {
public boolean evaluate(ILoggingEvent event) throws EvaluationException {
ApplicationContext ctx = ApplicationContextHolder.getContext();
if(ctx == null) {return false;} // not yet initialized / accessible
MyService myService = ctx.getBean(MyService.class);
}
}
But all-in-all I believe its a very ugly solution.
As a suggestion, I think you should consider refactoring of the logic so that the decision of whether to send an email based on logging event will be taken in the application (which is, I assume, spring boot driven so you have an access to the MyService)
Given the current implementation:
public foo() {
LOGGER.info("This should be sent by email");
}
I suggest a part of application:
#Component
public class MyLogic {
#Autowired MyService myService;
public void foo() {
if(myService.shouldSend()) {
LOGGER.info("This should be sent by email");
}
}
}
I'm using Spring 4.3.8.RELEASE with Hibernate 5.1.5.Final. I want to have a method executed after another another transaction completes. That transaction is defined below
#Service("organizationService")
#Transactional
public class OrganizationServiceImpl implements OrganizationService, ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher;
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher)
{
this.publisher = publisher;
}
#Override
public void save(Organization organization)
{
...
// sync data with ThirdParty but only if something has definitelychanged on the SB
// side, in which case we want to send ThirdParty an update.
if (!hasSameAttributes)
{
publisher.publishEvent(new ThirdPartyOrganizationEvent(organization.getId()));
} // if
} // save
So here is the method that I want executed after the above transaction completes ...
#Service
public class ThirdPartyAPIServiceImpl implements ThirdPartyAPIService
{
#Override
#TransactionalEventListener
public boolean updateOrg(final ThirdPartyOrganizationEvent thirdPartyOrgEvent)
{
...
}
But when I load my application context I get this error
Caused by: java.lang.IllegalStateException: No TransactionalEventListener annotation found on method: public abstract boolean org.mainco.subco.myproject.service.ThirdPartyAPIService.updateOrg(org.mainco.subco.myproject.domain.ThirdPartyOrganizationEvent)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter.<init>(ApplicationListenerMethodTransactionalAdapter.java:55)
at org.springframework.transaction.event.TransactionalEventListenerFactory.createApplicationListener(TransactionalEventListenerFactory.java:55)
at org.springframework.context.event.EventListenerMethodProcessor.processBean(EventListenerMethodProcessor.java:159)
at org.springframework.context.event.EventListenerMethodProcessor.afterSingletonsInstantiated(EventListenerMethodProcessor.java:104)
... 34 more
Wbat do I need to do to get this configured properly?
Defining #TransactionalEventListener on interface method rather then on method implementing interface worked for me.
I have springBoot standalone application. I used #SpringBootApplication, #ServletComponentScan annotations in my standalone application. All my components, beans getting initialized in spring container and prints in the application startup.
Inside my servlet, i invoke handler and beans were coming as null. How do i pass spring container through my servlet ?
#SpringBootApplication
#ServletComponentScan
public class AStandaloneApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AStandaloneApplication.class, args);
}
}
#WebServlet("/ba")
public class BAServlet extends SpeechletServlet {
#Autowired
private BASpeechletHandler bASpeechletHandler;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.setSpeechlet(bASpeechletHandler);
}
}
public class BASpeechletHandler implements Speechlet {
#Autowired
private BSEngine bSEngine;
#Autowired
private IBotResponseObjToAlexaSpeechletResponseObj botResponseObjToAlexaSpeechletResponseObj;
}
The bASpeechletHandler is null in servlet, if i instatiate object in my servlet for bASpeechletHandler and move on then components, services and repository inside bASpeechletHandler also null.
Thanks.
1.Add the packages to component scan - similar to this
#ServletComponentScan(basePackages="org.my.pkg")
2.Add one of the #Component annotations into your BASpeechletHandler class.
This will make that class eligible for auto-discovery of beans.
May be i little complication in asking. I found the solution. In Web applicationContext i pinged the spring context and got the bean.
private ApplicationContext appContext;
private BASpeechletHandler bASpeechletHandler;
public void init(ServletConfig config) throws ServletException {
super.init();
appContext = (ApplicationContext) config.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
bASpeechletHandler = (bASpeechletHandler) appContext.getBean("bASpeechletHandler");
}
Thanks.
I have a Spring JUnit tester class MySimpleTester:
#
RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:/spring/mySimpleConfig.xml"})
public class MySimpleTester {
#Before
public void setUp() throws Exception {
myAdapter = (MyAdapter) applicationContext.getBean("myAdapter");
}
#test
public void testGetSimpleList() {
List<SimpleLink> simpleList = **myAdapter.getSimpleLinksList**();
}
...
...
In the adapter class I have:
public MyAdapter {
public List<SimpleLink> getSimpleLinksList() {
List<SimpleLink> simLinks = null;
String environment = AppFactory.getPropertiesObj();
...
...
class AppFactory implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext acontext) {
context = acontext;
}
public getPropertiesObj() {
return getAppContext().getBean("propertiesBean");
}
I get NullPointerException and see that ApplicationContext is Null here.
However at the SpringJUnitTestRunner class MySimpleTester I could find the applicationContext to be initialized correctly. I am not including the mySimpleConfig.xml and included files. The method in MyAdapter class getSimpleLinksList() works perfectly fine from the web application when run in the application server, and the appcontext is obtained there.
Only from the Spring tester is it not able to reach the static application context AppFactory class, as it is called statically through AppFactory.getPropertiesObj(). I had the classpath set correctly as other test classes are executing.
If you want to access the current ApplicationContext in MySimpleTester:-
public class MySimpleTester {
#Autowired
ApplicationContext applicationContext;
#Before
public void setUp() throws Exception {
myAdapter = (MyAdapter) applicationContext.getBean("myAdapter");
}
#test
public void testGetSimpleList() {
List<SimpleLink> simpleList = **myAdapter.getSimpleLinksList**();
}
I think it is happening as multiple application contexts are created. The AplliCationContext object is supposed to be singleton. But when from the static method we call the applicationContext again it is refering to altogether different confirguration. The ApplicationContext is not even initialised there.
This does not happen when the same module is called from Spring MVC webcontanier. It happens only when you try to use Spring tester classes RunWith(SpringJUnit4ClassRunner.class). I can pass the AppContext in the business method but I do not want to change the bsiness method signature. I found some threads in spring community with similar issue.