Axis2(aar) + spring, without a servletContext - spring

Greetings dear Stackoverflow users, I have been lately in lots of pain with one specific problem with axis2 web services with Spring framework. I have read lots of different guides and read different forums but found people with the same problems but with no solutions. Basically ended up holding the monitor with both of my hands and yelling "What did you find out BudapestHacker938?". Anyway my axis2 web service class needs Spring beans and therefore they are autowired inside the web service class. Everything works so well inside the jetty server where I have servletContext. Just define needed listeners in web.xml and it works. Such a bliss. But unfortunately all good things come to the end in some point, for me, the devil is CICS environment inside of mainframe. There is no servletcontext like in Jetty/Tomcat, luckily it still has axis2 support. So according to the different user-guides I decided to archive my web-service into .aar and added it under the services folder. Axis2 folder structure is the following:
repository/
modules
services
When I am building this .aar archive then I am also generating my own wsdl, not using axis2 inbuilt wsdl generator which according to services.xml generates the services out of the given class (when I am running the axis2server, not using because doesn't like JAX-WS annotations as far as I know). To initialize Spring framework, I needed to write little SpringInit class which initializes Spring beans. Unfortunately it also for some reason initializes my web-service class according to its annotations and then occupies the main port(suspect that SpringInit intializes by its own the web service class since it is also defined as a Spring bean and SpringInit extends Axis2 class ServiceLifeCycle) and I get JVM BIND exception where it is stating that address is already in use. I would like to have the service built up according to the wsdl which is stored inside of the WSDL rather than generate new one, because I have various environments: 1) local machine - Jetty 2) mainframe. Anyway I give an insight to my services.xml:
<service name="Absence" class="org.services.SpringInit">
<description>
random description
</description>
<parameter name="ServiceTCCL">composite</parameter>
<parameter name="useOriginalwsdl" locked="false">true</parameter>
<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringAppContextAwareObjectSupplier</parameter>
<parameter name="ServiceClass">org.services.Absence</parameter>
<parameter name="SpringBeanName">absence</parameter>
<parameter name="SpringContextLocation">META-INF/applicationContextAar.xml</parameter>
</service>
Spring applicationContextAar.xml, little bit refactored it for dear Stack community:
<beans>
<bean id="applicationContext" class="org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder" />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:tcp://localhost/~/devDb" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="absence" class="org.services.Absence"></bean>
<bean id="jtemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="ds"></constructor-arg>
</bean>
<bean id="datasetFactory" class="org.vsam.DataSetFactory"></bean>
<bean id="dataManagerFactory" class="org.datamanager.DataManagerFactory"></bean>
<bean id="absenceFactory" class="org.services.AbsenceFactory"></bean>
<bean id="h2Database" class="org.dataset.H2Database"><constructor-arg ref="jtemplate"></constructor-arg>
</bean>
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"></bean>
</beans>
My SpringInit class looks something like that:
public class SpringInit implements ServiceLifeCycle {
public void startUp(ConfigurationContext ignore, AxisService service) {
try {
ClassLoader classLoader = service.getClassLoader();
ClassPathXmlApplicationContext appCtx = new
ClassPathXmlApplicationContext(new String[] {"applicationContextAar.xml"}, false);
appCtx.setClassLoader(classLoader);
appCtx.refresh();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void shutDown(ConfigurationContext ctxIgnore, AxisService ignore) {}
}
Now we are moving to org.services.Absence.class, it is an ordinary JAX-WS web-service class with following header (contains JAX-WS annotations):
#WebService(name = "AbsenceService", serviceName = "Absence", portName = "Absence",
targetNamespace = "http://www.something.org/Absence")
public class Absence extends ServiceHandlerBase {
#Autowired
private AbsenceFactory absenceFactory;
#Autowired
private DataManagerFactory dataManagerFactory;
#Autowired
private DataSetFactory dataSetFactory;
...
}
Containing methods like that:
#WebMethod
#WebResult(name = "AbsenceResponse")
public SearchAbsenceRecordsResponse invokeSearchAbsenceRecords(
#WebParam ServiceRequest request,
#WebParam SearchAbsenceRecordsRequest absenceRequest) {...}
One alternative is to add "servicejars" folder into "repository" folder and populate it with absence.jar which has all its dependencies in the sub-folder "lib". Axis2 then automatically runs absense.jar since it has JAX-WS annotation. But in there when I call out the web-service for example with SOAP-UI, it doesn't have Spring initialized since I don't know how to initialize Spring in that solution. Maybe someone has any expertise about that.
TL;DR
How do I get my Spring beans initialized in manner that it doesn't start the services in the web service class according to the annotation and would rather build up services according to the wsdl?
You are welcome to ask questions.

How I initialized Spring inside of CICS without servletcontext?
Basically until today the SOAP web services have been published through servicejars which means into the repository folder has been created "servicejars" folder which cointains jars which have been built from the web service classes. "servicejars" subfolder "lib" contains all the dependencies which web service jars need.
At first I learnt from the web(Axis2 homepage, there was an instruction about axis2 and spring integration) for initializing Spring in Axis2 web service I need .aar archive and SpringInit service defined in services.xml. But this brought lots of problems since having old architecture built on jaxws and jaxb there was a huge need for refactoring the web services layer. Axis2 tolerated jaxws annotations only with "servicejars" solution. Initing Spring with SpringInit class meant that it initializes Spring beans according to the application context. This now runs web service bean(absence bean in previous post) as a separate web service and occupied 8080 port, when time came for the web service creation according to WSDL I got an error "JVM bind address already in use". So after that I figured I should create the service according to the absence Spring bean and let axis2server generate the WSDL, but axis2server didn't like jaxws annotation and even without them it didn't like my jaxb DTOs.
Therefore, I decided to drop .aar architecture and went back to the "servicejars" architecture. Unfortunately in there I didn't have services.xml support, to define the potential SpringInit service.
Since jaxws web services are the only entrypoints then I decided do the following (initialize Spring beans in the web service layer):
#WebService(name = "AbsenceService", serviceName = "Absence", portName = "Absence",
targetNamespace = "http://www.something.org/Absence")
public class Absence extends ServiceHandlerBase {
private static AbsenceFactory absenceFactory;
private static DataManagerFactory dataManagerFactory;
private static DataSetFactory dataSetFactory;
static {
try {
ClassPathXmlApplicationContext appCtx = new
ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"}, false);
appCtx.refresh();
absenceFactory = (AbsenceFactory) appCtx.getBean("absenceFactory", AbsenceFactory.class);
dataManagerFactory = (DataManagerFactory) appCtx.getBean("dataManagerFactory", DataManagerFactory.class);
dataSetFactory = (DataSetFactory) appCtx.getBean("datasetFactory", DataSetFactory.class);
} catch (Exception ex) {
ex.printStackTrace();
}
}
...
}
As you can see when this class is being called out, it will initialize applicationcontext and since it is static, all the spring beans will stay in the memory until the end(when service is closed). In other classes autowiring works perfectly, no need to get these beans wired manually.
In the end, I didn't find the possiblity to initialize Spring in the matter as I hoped through .aar architecture, but I found a work around with the guidance of a senior programmer. Huge thanks to him! And now the possible solution is visible for all StackOverFlow users.
EDIT:
In applicationContext.xml I had:
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"/>
Tries to create web services with Absence.class(absence bean). Removed it since I can in local machine as well use pre-generated WSDL with Jetty (originally was used for creating web service in the local machine, like I said before, I have local development environment and it should be also compatible with CICS, now it is solved).

Related

Using Hazelcast for both Spring Session and 2 Level Cache (LC2) with Hibernate

So I want to use Hazelcast in my web application for both 2 level caching (hibernate layer) and spring session, the setup is very simple I want to be able to use NearCache configurations with a server running on the network.
I first ran into a compatibility problem recent version of Hazelcast 4.* is not yet supported in spring session, so I was happy to use the supported version: 3.12.6...
below is my hazelcast-client.xml and I have properly configured it to be used by spring.hazelcast.config=file://... when I start my application the Hazelcast instance is not created, so I decided well I should create the ClientConfig and HazelcastInstance beans myself using the code below:
#Bean()
ClientConfig clientConfig() throws IOException{
try(final InputStream stream = Files.newInputStream(Paths.get("proper-path/hazelcast-client.xml"))){
com.hazelcast.client.config.XmlClientConfigBuilder builder = new com.hazelcast.client.config.XmlClientConfigBuilder(stream);
return builder.build();
}
}
#Bean()
HazelcastInstance hazelcastInstance(final ClientConfig clientConfig){
return HazelcastClient.newHazelcastClient(clientConfig);
}
Now i have a problem, I can't add the code below so I can use it with sessions:
final MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
hazelcastInstance.getConfig().getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
hazelcast-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config
http://www.hazelcast.com/schema/client-config/hazelcast-client-config-3.12.xsd">
<group>
<name>GroupName</name>
<password>pass</password>
</group>
<instance-name>${application.container.name}</instance-name>
<properties>
<property name="hazelcast.client.shuffle.member.list">true</property>
<property name="hazelcast.client.heartbeat.timeout">60000</property>
<property name="hazelcast.client.heartbeat.interval">5000</property>
<property name="hazelcast.client.event.thread.count">5</property>
<property name="hazelcast.client.event.queue.capacity">1000000</property>
<property name="hazelcast.client.invocation.timeout.seconds">120</property>
</properties>
<client-labels>
<label>web-app</label>
<label>${application.container.name}</label>
</client-labels>
<network>
<cluster-members>
<address>127.0.0.1</address>
</cluster-members>
<outbound-ports>
<ports>5801</ports>
</outbound-ports>
<smart-routing>true</smart-routing>
<redo-operation>true</redo-operation>
<connection-timeout>60000</connection-timeout>
<connection-attempt-period>3000</connection-attempt-period>
<connection-attempt-limit>2</connection-attempt-limit>
<socket-options>
<tcp-no-delay>false</tcp-no-delay>
<keep-alive>true</keep-alive>
<reuse-address>true</reuse-address>
<linger-seconds>3</linger-seconds>
<buffer-size>128</buffer-size>
</socket-options>
</network>
<executor-pool-size>40</executor-pool-size>
<native-memory enabled="false" allocator-type="POOLED">
<size unit="MEGABYTES" value="128"/>
<min-block-size>1</min-block-size>
<page-size>1</page-size>
<metadata-space-percentage>40.5</metadata-space-percentage>
</native-memory>
<load-balancer type="random"/>
<flake-id-generator name="default">
<prefetch-count>100</prefetch-count>
<prefetch-validity-millis>600000</prefetch-validity-millis>
</flake-id-generator>
<connection-strategy async-start="true" reconnect-mode="ASYNC">
<connection-retry enabled="true">
<initial-backoff-millis>2000</initial-backoff-millis>
<max-backoff-millis>60000</max-backoff-millis>
<multiplier>3</multiplier>
<fail-on-max-backoff>true</fail-on-max-backoff>
<jitter>0.5</jitter>
</connection-retry>
</connection-strategy>
</hazelcast-client>
You need to configure the map on the server side, which means you need to have some of the Spring classes on member's classpath. But keep in mind that you need this configuration only when HazelcastIndexedSessionRepository#findByIndexNameAndIndexValue is used.
Also in client mode, do not forget to deploy necessary classes and enable user code deployment for members. Otherwise session updates will fail:
// member
config.getUserCodeDeploymentConfig().setEnabled(true)
.setClassCacheMode(UserCodeDeploymentConfig.ClassCacheMode.ETERNAL);
// client
clientConfig.getUserCodeDeploymentConfig().setEnabled(true).addClass(Session.class)
.addClass(MapSession.class).addClass(SessionUpdateEntryProcessor.class);
But I recommend to include spring-session-hazelcast on member's cp rather than user code deployment. This will satisfy both of the needs above.
Finally, if hazelcast-client.xml exists in one of the known paths in your project (e.g. under resources/), the client will be created with this configuration. You do not need to create ClientConfig bean in that case.

RMI & Spring ends up on ClassCastException on client

I wrote a simple client server architecture that helps me producing PDF files out of MS Office documents. The communication is handled via RMI and Spring wraps the entire complexity on the server side.
I cannot use Spring on the client side, because I call the methods from Matlab 2007b. A Jar with dependencies to spring produces exceptions due to the special handling of static and dynamic classpaths in Matlab.
Long story short: I wrote a simple RMI client in plain java:
import com.whatever.PDFCreationService;
Object service = Naming.lookup("rmi://operations:1099/pdfCreationService");
System.out.println((PDFCreationService)service); //produces ClassCastException
Interface:
public interface PDFCreationService {
public PDFCreationConfig createPDF(PDFCreationConfig config) throws IOException, InterruptedException, OperationInterruptionException;
}
Extracted out of my "former" spring config (client side):
<bean id="pdfCreationService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://operations:1099/pdfCreationService"/>
<property name="serviceInterface" value="com.whatever.creator.PDFCreationService"/>
</bean>
and on the server side :
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="serviceName" value="pdfCreationService"/>
<property name="service" ref="pdfCreationService"/>
<property name="serviceInterface" value="com.whatever.creator.PDFCreationService"/>
<!-- defaults to 1099 -->
<property name="registryPort" value="1099"/>
</bean>
When I run the code the following exception is thrown:
Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to com.whatever.creator.PDFCreationService
I am 100% sure that I do not try to cast to a class like in this post: "ClassCastException: $Proxy0 cannot be cast" error while creating simple RMI application
Does spring encapsulate my interface in a different interface? Is there a way to find out which interface the Proxy hides?
Please let me know if you need more details to clarify my issue.
The RmiServiceExporter exports a RmiInvocationHandler if the remote service don't implements Remote, (ie, not is a traditional RMI Server)
If you can't use a RmiProxyFactoryBean in the client side, that is a bean factory for service interface proxies that convert service calls to RemoteInvocations, seems better option to use traditional RMI instead.
You can use the RmiServiceExporter to export tradional RMI Services too, like
public interface PDFCreationService extends Remote {
public PDFCreationConfig createPDF(PDFCreationConfig config) throws RemoteException;
}

Spring, Infinispan and JBoss 7 integration

I'm trying to use JBoss 7 Infinispan cache as a communication form (something more later) of two war-deployed spring-based apps. I'm having a problem with accessing the JBoss managed cache managers.
When I use
DefaultCacheManager cacheManager = new DefaultCacheManager();
cache = cacheManager.getCache();
on each of two applications, I get two separate caches. Is there any way to access the cache created by JBoss server without using the #ManagedBean annotation and Java EE standard at all ?
It's done. Thanks to Kazaag, I used JNDI.
JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.lookup("java:jboss/infinispan/container/cluster");
I had the well known problem with a DefaultEmbeddedCacheManager Class Cast Exception. I used reflections.
Map<Object, Object> cache;
JndiTemplate jndiTemplate = new JndiTemplate();
Object cacheManager;
try {
cacheManager = (Object) jndiTemplate.lookup("java:jboss/infinispan/container/cluster");
Method method = cacheManager.getClass().getMethod("getCache");
cache = (Map) method.invoke(cacheManager);
} catch (Exception e) {
e.printStackTrace();
return;
}
Moreover I had to mark container as started eagerly.
<cache-container name="cluster" aliases="ha-partition" default-cache="default">
<transport lock-timeout="60000"/>
<replicated-cache name="default" mode="SYNC" start="EAGER" batching="true">
<locking isolation="REPEATABLE_READ"/>
</replicated-cache>
</cache-container>
The cache is replicated although different class loaders.
If each application are using there own cache manager, they will have separated cached.
You can retrieve the cache container managed by the application server via JNDI support of Spring (The JNDI name is java:jboss/infinispan/my-container-name). So Spring will be responsible to make sure every part are using the same container.
I am not 100% sure you will get the same cache, it may return you a application specific cache (the 2 applications data object are in fact coming from different class loader).
Embedded cache is probably not mean for inter application communication. You probably need to use the client/server paradigm.
A bit late in the day but the information on accessing the infinispance cache store via JNDI can be found here
With that a JNDI lookup I get the CacheContainer
<jee:jndi-lookup id="cache1"
jndi-name="java:jboss/infinispan/container/jbossas7-quickstart"
cache="true" resource-ref="false" lookup-on-startup="true" />
which I inject via a setter
public void setContainer(CacheContainer container) {
this.container = container;
}
and now I have access to the cachestore. Note the suggestions here
#Resource(lookup="java:jboss/infinispan/container/my-container-name")
#Resource(lookup="java:jboss/infinispan/cache/my-container-name/my-cache-name")
do not work within my Spring Bean

Spring JMS running as a JAR project

I have a Spring JMS application. Infact there is no UI. Just Spring configuration (JMS listener) and the Spring configuration is loaded by web.xml .
so when i deploy in server, the listener starts working.
But I do not want the web part, because, there no UI, It is just a project which listen to a Queue and do its processing. So I think it should be JAR and it should run standalone(or when i deploy in server) How to create such project/ JAR when deployed in server it automatically starts running. I do not want run a main class every time I update the JAR.
I have used an executable jar to launch a JMS queue before. You just have to make sure you have access to all the jar dependencies for Spring and JMS, which is a lot. This can be done by setting the classpath to point at the dependency jars or create an Uberjar and pack all the dependency jars in the executable jar.
Here is an example class that will start up ActiveMQ from a Jar when you set it as a the main-class in the jar manifest. A jms.pid will be created with the process id for process. You must set the paths to your Spring contexts for JMS in the ConfigurableApplicationContext.
public class Server {
public static void main(String[] args) throws Exception {
// Define Spring contexts required for JMS to run
List<String> contexts = Arrays.asList( "classpath:applicationContext.xml", "classpath:spring/jmsContext.xml" );
ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext(contexts);
// Get activeMQ from JMS context
BrokerService broker = applicationContext.getBean( "broker" );
// Start up activeMQ
broker.start();
// Get pid for this process
String sysId = ManagementFactory.getRuntimeMXBean().getName();
String pid = sysId.substring(0, sysId.indexOf("#"));
// Write PID file
File file = new File("jms.pid");
DataOutputStream outs = new DataOutputStream(new FileOutputStream(file, false));
outs.write(pid.getBytes());
outs.close();
}
}
Example Spring configuration for getting access to the BrokerService
<bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean">
<property name="config" value="classpath:org/activemq/xbean/activemq.xml" />
<property name="start" value="true" />
</bean>
I have typically seen JMS applications run as Windows Services or Unix daemons. These provide you features that you can configure like restarting your JMS app if the server reboots, etc.
There are some commercial Java EE containers like Weblogic that provide start-up classes that you can use to start your JMS application when a node in the cluster starts. This provides console control over the JMS application / server. It doesn't sound like that is an option in your case though.

writing hessian service

I am new to Spring and Hessian and never used them before.
I want to write a small Hello World Program which clearly shows how this service works.
I am using Maven for list project details and dependencies.
The resources for hessian available online are not complete step-by-step guide.
would appreciate if I get help form someone who has worked writing hessian services
The steps for implementing a Hessian-callable service are:
Create a Java interface defining methods to be called by clients.
Write a Java class implementing this interface.
Configure a servlet to handle HTTP Hessian service requests.
Configure a HessianServiceExporter to handle Hessian service requests from the servlet by delegating service calls to the Java class implementing this interface.
Let's go through an example. Create a Java interface:
public interface EchoService {
String echoString(String value);
}
Write a Java class implementing this interface:
public class EchoServiceImpl implements EchoService {
public String echoString(String value) {
return value;
}
}
In the web.xml file, configure a servlet:
<servlet>
<servlet-name>/EchoService</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/EchoService</servlet-name>
<url-pattern>/remoting/EchoService</url-pattern>
</servlet-mapping>
Configure an instance of the service class in the Spring application context:
<bean id="echoService" class="com.example.echo.EchoServiceImpl"/>
Configure the exporter in the Spring application context. The bean name must match the servlet name.
<bean
name="/EchoService"
class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="echoService"/>
<property name="serviceInterface" value="com.example.echo.EchoService"/>
</bean>
The client has to create a proxy of the remote interface. You could simply write a JUnit-Test:
HessianProxyFactory proxyFactory = new HessianProxyFactory();
proxyFactory.setHessian2Reply(false);
proxyFactory.setHessian2Request(false);
com.example.echo.EchoService service = proxyFactory.create(
com.example.echo.EchoService, "http://localhost:8080/<optional-context/>remoting/EchoService");
Assert.equals(service.echoString("test"), "test");

Resources