Spring Cache in MVC - Possible to lookup with autowiring? - spring

I see that when the application starts up my singleton cache is created
DEBUG Creating CGLIB proxy: target source is SingletonTargetSource for
target object [com.abc.xyz.util.CacheUtil#14e3dd3] DEBUG Unable to
apply any optimizations to advised method: public java.util.Map
But how do I lookup the value using autowiring as when I attempt, it does not hit the singleton created and creates a new instance of CacheUtil.
CacheUtil.java [This class is annotated with #Component]
public Map getSelectOptions(String codeType) {
System.out.println("Cache Breached!!!");
HashMap selectOpts = new HashMap();
Vector<MyTableDO> vCodeMap = null;
vCodeMap = MyTableDO.getCodesFromDatabase(codeType, "LookupCacheUtil");
if(vCodeMap == null || vCodeMap.size() == 0) return selectOpts;
vCodeMap.forEach(codeMap -> selectOpts.put(codeMap.getCodeValue(), codeMap.getCodeDesc()));
return selectOpts;
}
My spring config xml
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.abc.xyz" />
<context:annotation-config />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="cacheUtil" class="com.abc.xyz.util.CacheUtil" />
</beans>
Class Invoking the Cached method
#Autowired
#Qualifier("cacheUtil")
protected CacheUtil cacheUtil;
public Map getSelectOptions(String codeType) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyApplication.class);
//ctx.refresh();
CacheUtil lkp = (CacheUtil) ctx.getBean(CacheUtil.class);
ctx.close();
System.out.println("App Context lookupCacheUtil -"+lkp); // Not the same object of Spring Cache and comes to be new instance on every call
System.out.println("Autowired lookupCacheUtil -"+cacheUtil); // Always comes to be NULL
return lkp.getSelectOptions(codeType);
}
}
MyApplication class
#SpringBootApplication
#EnableCaching
public class MyApplication extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
#Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext context = new XmlWebApplicationContext();
context.setConfigLocation("/WEB-INF/config/spring-servlet.xml");
//using servlet 3 api to dynamically create spring dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("spring", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(2);
dispatcher.addMapping("/");
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

On detailed analysis, my understanding of Autowired become more refined. Thanks to this link.
In my case, I had autowired 'CacheUtil' on a form bean. It appears that the form beans are not being managed by spring or at least in this case. The same autowired works normally in a controller which is managed by Spring.
So I to work around by fetching the Spring Cache 'Proxy' version of CacheUtil from the Application Context. Below code snippet should help (method getInstance()):
import org.springframework.beans.BeansException;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component("MyCache")
public class CacheUtil implements ApplicationContextAware{
private static ApplicationContext appContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
appContext = applicationContext;
}
/**
* Method to fetch the shared instance of the Spring Cache Object that helps reach the
* Cache that resides in Application Context.
*
* #return Singleton shared instance of the Spring Cache Proxy of this class CacheUtil
*/
public static CacheUtil getInstance() {
CacheUtil appCache = appContext.getBean("MyCache", CacheUtil.class);
if(appCache != null) return appCache;
return new CacheUtil();
}

Related

Spring application context bean creation issue with latest spring version

I have a project which uses an old spring.jar (1.2.6),from this project, I am expected to call a newer version (spring version 5.0.7) spring boot project's method. Below is the way I am creating my bean in old version project.
I am getting NullPointer exception while creating the Autowired bean.
Create bean from XML:spring
test-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "qvc-spring-beans.dtd">
<beans>
<bean name="testPci" class="com.test.client.TestPci">
</bean>
</beans>
sampleParent-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans SYSTEM "spring-beans.dtd">
<beans>
<import resource="classpath:/com/test/test-context.xml" />
<bean id="classA" class="com.test.A" >
<property name="testPci">
<ref bean="testPci"/>
</property>
</bean>
</beans>
Java code old spring project:
package com.test;
public class A{
private TestPci testPci;
private ApplicationContext ctx;
public TestPci getTestService() {
if (!StringUtils.isValid(ctx)) {
ctx = new ClassPathXmlApplicationContext("./com/test/test-context.xml");
}
if (!StringUtils.isValid(this.testPci)) {
if (StringUtils.isValid(ctx)) {
testPci = (TestPci) ctx.getBean("testPci");
TestPci testPci = (TestPci) ctx
.getBean("testPci");
this.setSecureTestService(testPci);
}
}
return this.getSecureTestService();
}
public TestPci getSecureTestService() {
return testPci;
}
public void setSecureTestService(TestPci testPci) {
this.testPci = testPci;
}
public void methodA(){
//Calling newer code form old spring code:
testPci.testing("1", "2", "3");
}
}
Calling "TestPci" class as above, but when trying to call using the above, it actually calls the "TestPci"."testing" method. But the object autowired as "testWebClientService" is returning as null. I would like to get the object created instead it returns null.
New spring version class:
#Service
#EnableConfigurationProperties(TestWebClientProperties.class)
#Configurable
public class TestPci{
#Autowired
private TestWebClientService testWebClientService;
public Map<String, String> testing(String a, String b, String c) throws Exception {
Map<String, String> map = testWebClientService.find(a, b, c);
System.out.println("**=="+map.get(0));
return map;
}
}
Adding junit which is used to call the TestPci class from newer version of spring:
#RunWith(SpringJUnit4ClassRunner.class)
#EnableConfigurationProperties(TestWebClientProperties.class)
#SpringBootTest(classes = { TestWebClientService.class, TestPci.class }, webEnvironment = WebEnvironment.NONE)
public class TestJunit {
#MockBean(name="restTemplate")
public RestTemplate restTemplate;
#Autowired
private TestPci testPci;
#Test
public void ff() throws Exception {
testPci.testing("1","1","1");
}
}

How to create a websocket endpoint in an existing spring application

I'm trying to make available a new websocket endpoint in an existing spring application.
I'm following the documentation at https://docs.spring.io/spring/docs/4.3.2.RELEASE/spring-framework-reference/html/websocket.html#websocket-server-handler . But based on documentation I should configure the DispatcherServlet or use WebSocketHttpRequestHandler.
How can I make available the websocket endpoint without any change in the web.xml config file ?
This is what I tried, but not working (Client error 404 not found).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ws="http://jax-ws.dev.java.net/spring/core" xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd
http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet.xsd
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="*">
<websocket:mapping path="/ws" handler="websocketService"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="websocketService" class="com.krios.SocketHandler"/>
</beans>
Class file:
public class SocketHandler extends TextWebSocketHandler {
List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws InterruptedException, IOException {
for(WebSocketSession webSocketSession : sessions) {
Map value = new Gson().fromJson(message.getPayload(), Map.class);
webSocketSession.sendMessage(new TextMessage("Hello " + value.get("name") + " !"));
}
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//the messages will be broadcasted to all users.
sessions.add(session);
}
}
I can provide you the java configuration (you can then change it to xml yourself or use it as java class and scan it from xml)
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Bean
public MessagingWebSocketHandler webSocketHandler() {
//handler of your websocket. should be a class implementing WebSocketHandler.
//You could also extend AbstractWebSocketHandler or TextWebSocketHandler and override methods
return new MessagingWebSocketHandler();
}
#Bean
public WebSocketContainerFactoryBean createWebSocketContainer() {
WebSocketContainerFactoryBean container = new WebSocketContainerFactoryBean();
container.setMaxTextMessageBufferSize(StaticConfig.MAXIMUM_WS_TEXT_BUFFER_SIZE);
container.setMaxBinaryMessageBufferSize(StaticConfig.MAXIMUM_WS_BINARY_BUFFER_SIZE);
container.setMaxSessionIdleTimeout(StaticConfig.MAXIMUM_WS_SESSION_IDLE_TIMEOUT);
container.setAsyncSendTimeout(StaticConfig.MAXIMUM_WS_ASYNC_SEND_TIMEOUT);
return container;
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(webSocketHandler(), "/message").setAllowedOrigins("*"); // you could also get handler from context
}
}
I hope it hepls.
update
I myself don't use xml based configuration. But the simplest way is to add this java code then scan it from xml. For example, by adding this line to your spring servlet configuration xml you can scan your configuration package or whole project package.
<context:component-scan base-package="com.my.company.config" />
Then your WebSocketConfig class must be under com.my.company.config
Documentation Of Websocket support and configuration is here. On section Create and Configure a WebSocketHandler you can read about xml configuration. I havent test it myself tho.

Getting JAX-WS WebServices working in Spring Boot

I have some legacy JAX-WS #WebService annotated classes. I am trying to get this working in spring-boot. Been looking at https://jax-ws-commons.java.net/spring/ as a reference as well as http://docs.spring.io/spring/docs/current/spring-framework-reference/html/remoting.html.
My #SpringBootAnnotated class
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class SpringBootBooter extends SpringBootServletInitializer {
#Bean
public ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean reg = new ServletRegistrationBean(new WSSpringServlet(),"/myws");
reg.setLoadOnStartup(1);
return reg;
}
public static void main(String args[]) throws Exception {
SpringApplication.run(new Object[] {
SpringBootBooter.class,
new ClassPathResource("myLegacyAppContextWithWSBean.xml")
}, args);
}
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new WSServletContextListener());
}
}
My XML config for the WS implementation class
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ws="http://jax-ws.dev.java.net/spring/core"
xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://jax-ws.dev.java.net/spring/core
http://jax-ws.java.net/spring/core.xsd
http://jax-ws.dev.java.net/spring/servlet
http://jax-ws.java.net/spring/servlet.xsd">
<wss:binding url="/myws">
<wss:service>
<ws:service bean="#mywsbean" />
</wss:service>
</wss:binding>
<bean id="mywsbean" class="com.items.MyWsBean">
</bean>
</beans>
When everything boots up, I go to localhost:8080/myws and just get back "404 Not Found: Invalid Request".
Just not sure what I am missing, its like something is not parsing those wss:binding XML declarations to tie together these servlet requests to the bean, and I am not sure how to do this in spring-boot.
This appears in logs when I first hit that mapped URI
Jul 15, 2015 9:40:18 AM com.sun.xml.ws.transport.http.servlet.WSServletDelegate <init>
INFO: WSSERVLET14: JAX-WS servlet initializing
thanks
Your solution appears rather complex. You can get a JAX-WS service running with Spring Boot, using only
These Gradle dependencies:
dependencies {
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-jersey"
}
This configuration class (Groovy):
import org.glassfish.jersey.server.ResourceConfig
import org.springframework.stereotype.Component
#Component
class JerseyConfig extends ResourceConfig {
JerseyConfig() {
register(MyResource)
}
}
This resource class:
import org.springframework.stereotype.Component
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.Context
import javax.ws.rs.core.MediaType
#Component
#Path("/foo")
class MyResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Map bar() {
return ["hello":"world"]
}
}
and this:
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
class DemoJaxWSService {
static void main(String[] args) {
SpringApplication.run DemoJaxWSService, args
}
}
Your endpoint will be available at localhost:8080/foo

Can I use both configuring SI with annotation in java file and xml?

Last year, spring integration released 4.0 version for us to configure using annotation without configuring in XML files. But I want to use this feature using the existing XML configurations.
So I wrote the code using spring boot and integration annotation
#Configuration
#ComponentScan(basePackages ={"com.strongjoe.blue"},excludeFilters=#ComponentScan.Filter(type=FilterType.REGEX, pattern={"com.strongjoe.blue.agent.Bootstrap*"}))
#IntegrationComponentScan
#ImportResource( {"${context.agent.path}context-bean-*.xml", // Context Configuration
"${project.agent.path}context-properties.xml" } ) // Project Based Chain configuration
public class AgentStarter implements CommandLineRunner{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Lazy
#Bean
#ServiceActivator(inputChannel="blue-hub-start-channel", outputChannel="blue-hub-route-channel")
public <T> BlueMessage<T> startJob(BlueMessage<T> msg) {
logger.debug("BluE Hub Agent started :{} [phrase:{}]", System.currentTimeMillis() , "prototype-version");
return msg;
}
#Lazy
#Bean
#ServiceActivator(inputChannel="blue-hub-end-channel")
public <T> BlueMessage<T> endJob(BlueMessage<T> msg) {
logger.debug("BluE Hub Agent ended :{} [phrase:{}]", System.currentTimeMillis() , "prototype-version");
return msg;
}
#Bean
#Transformer(inputChannel="blue-normalized-channeel", outputChannel="blue-output-channel")
public org.springframework.integration.transformer.Transformer JsonToMap( ) {
return new JsonToObjectTransformer( List.class );
}
#MessageEndpoint
public static class Echo {
#ServiceActivator(inputChannel="blue-output-channel")
public void stringEcho(Message message) {
}
}
#Autowired
Gateway gateway;
public static void main(String[] args) {
SpringApplication app = new SpringApplication(AgentStarter.class);
app.setWebEnvironment(false);
app.run(args).close();
}
#Override
public void run(String... args) throws Exception {
System.err.println("blue-hub-agent started..");
System.out.println(gateway.sendReceive("gitlab"));
}
And I wrote the definition about every channel I use in the 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:int="http://www.springframework.org/schema/integration"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.0.xsd
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-4.0.xsd">
<int:channel id="blue-normalized-channel" />
<int:channel id="blue-header-channeel" />
<int:channel id="blue-request-channel" />
<int:channel id="blue-output-channel" />
<int:channel id="blue-gitlab-request-prepare-channel" />
<int:channel id="blue-hub-start-command-channel" />
<int:channel id="blue-hub-start-channel"/>
<int:channel id="blue-hub-end-channel" />
But I got error.
Caused by: org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application:8090.blue-hub-start-channel'.
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:81)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:255)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:223)
The reason will be, I think,
that spring bean in XML file and spring bean with the annotation has different context. So I think that even if blue-hub-start-channel is subscribed by the service activator named "startJob", it got error.
How can I solve this problem?
Annotating #ServiceActivator on #Bean is not for POJO Messaging. See the documentation.
When you annotate #Beans this way, you have to provide an appropriate object (MessageHandler in this case).
For POJO style annotated methods, the annotation must be on a method in a #Bean method (like you have on this one...
#MessageEndpoint
public static class Echo {
#ServiceActivator(inputChannel="blue-output-channel")
public void stringEcho(Message message) {
}
}
... and then declare a #Bean for Echo.
You can put all your #ServiceActivator methods (and #Transformers) in the same #MessageEndpoint.

Spring Autowiring Issues in a Servlet

I am having some issues with Autowiring
First off i create an Embedded server
Main.java
Server server = new Server(8080);
CXFNonSpringServlet cxf = new CXFNonSpringJaxrsServlet();
ServletHolder servlet = new ServletHolder(cxf);
servlet.setInitParameter("javax.ws.rs.Application", "com.asd.dispatcher.rest.testApplication");
servlet.setName("services");
servlet.setForcedPath("services");
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/hello");
server.setHandler(context);
context.addServlet(servlet, "/*");
server.start();
testApplication.java
public class testApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(testServlet.class);
return classes;
}
}
testServlet.java
#Path("/people")
#Component
#Scope("prototype")
public class testServlet {
#Autowired
private StatsService statsService;
#Produces({ "application/json" })
#GET
public String getPeople(#QueryParam("page") #DefaultValue("1") final int page) {
System.out.println("======= getPeople");
//statsService.printStats();
return "Hello World";
}
}
Now my issue is that my statsService is not being autowired in testServlet.java, yet i can autowire it into another class annotated with #Service,
is this because of me using a CXFNonSpringServlet??
or is it because of the way i am trying to Autowire?
Ok i got it working
Ok so i fixed it (i would post this as the answer but cant answer my own question :/ )
putting the answer here to help anyone else with the same problem
After having a look at the following
Autowiring in servlet
I came to the conclusion that a Post Construct method that gets the applicationContext and then the bean would work
Eg: my code would be this
#Path("/people")
#Component
#Scope("prototype")
public class testServlet {
private StatsService statsService;
#PostConstruct
public void initStats() {
System.out.println("============================= Init");
ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
statsService = context.getBean("statsService", StatsService.class);
}
#Produces({ "application/json" })
#GET
public String getPeople(#QueryParam("page") #DefaultValue("1") final int page) {
System.out.println("======= getPeople");
statsService.printStats();
return "Hello World";
}
}
Although this is not autowiring it does work, if there is anyone who knows how to do this with autowiring i would love to know as it would be far cleaner then the solution i found.
*on a side note i picked up a new issue with this 'solution' to my problem in that the statsService that i have also has other beans autowired in, and it seems although the auto wiring initializes these beans any changes to their state in another class are not reflected in the statsService infact the state of these beans remain unchanged (although that might be the suspected behavior i am still new to spring so i am not sure)
I don't know what CXFNonSpringServelt is, but my question is: Have you added the lines above in the context-config.xml file of your application?
<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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
...
...
<context:component-scan base-package="package of the classes with annotations" />
And in your server class you should add the annotation #Service
#Service("myService")
public class MyService ...
And you can use the #Authowire like this:
public class Client{
#Autowire
MyService myservice;
...

Resources