I'm currently working on a distributed services architecture for a project at work. Essentially, we are managing upwards of 200 machines. Each of these machines has an instance of a service running on them that allows us to interface with the machine in a specific way.
At the center I have a control application which needs to talk to these 200 identical services. I was hoping to use RMI via Spring Remoting to make this happen, allowing me to #Autowire my remote service into my #Controller and treat it like a local service with exception propagation and maybe in the future propagation of transactions / security context via hooks.
This works great for a single service on a single machine where I can hardcode the remote service in my Spring configuration, but what I'm not able to figure out is how to dynamically choose which service (aka, which machine) I want to talk to at runtime and make that remote service available the "Spring" way.
I would like to be able to configure this dynamically from a database table and use the same table information to do a service lookup, while still taking advantage of dependency injection.
I thought of maybe injecting some kind of service manager that could do a service lookup of some sort, but was hoping someone else has managed to solve this (or a similar) problem elegantly.
An example of a hardcoded, single service instance would be like so:
The first XML snippet is on the machine service itself, telling Spring to expose it via RMI
<!-- Expose DeviceService via RMI -->
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="serviceName" value="DeviceService" />
<property name="service" ref="deviceService" />
<property name="serviceInterface"
value="com.example.service.DeviceService" />
<property name="registryPort" value="1199" />
</bean>
The second XML snippet is on the client (control application) which lets me access the exposed service
<!-- Proxy our remote DeviceService via RMI -->
<bean id="remoteDeviceService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://machineurl:1199/DeviceService"/>
<property name="serviceInterface" value="com.example.service.DeviceService"/>
</bean>
It's that second bit of configuration that I'm trying to make dynamic. As you can see, to create this service proxy, I need to know at bean creation time the service URL. The Service URL can be 1 of 200+ variations, depending on what machine I want to talk to. The service I'm talking to is the same interface, but I won't know which machine until runtime depending on the current request context.
You could create connections to your servers dynamically with an additional service and remove "remoteDeviceService" from your client/controll app, i.e.:
public class RMIConnectionService {
public DeviceService connect(String serverUrl) {
RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
factory.setServiceInterface(DeviceService.class);
factory.setServiceUrl("rmi://" + serverUrl + ":1099/SERVICE_URL");
factory.afterPropertiesSet();
//...
return (DeviceService) factory.getObject();
}
}
Then add this service to you service layer:
<bean id="rmiService" class="...RMIConnectionService" >
//...
</bean>
In your logic autowire the service and use it like:
DeviceService server1 = rmiService.connect("127.0.0.1");
For Database config add your DAO to this service, to load the right url. Port and url, or even the interface class could be configured this way too.
I have no RMI service to test this, but it worked with Hessian too. I hope this helps you.
Related
Setting up an HTTP proxy for Java JVM 6.x isn't working for me; I'm hoping someone can spot what I'm missing.
I have a Java application deployed on JBOSS 5.1.2 that makes several calls to external web services. I'd like to be able to intercept these calls using a proxy: Fiddler version 4.4.8.0.
After doing an SO search I added the following flags to my JBOSS VM parameters at startup:
-DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888 -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8888
I'm running JBOSS in IntelliJ 14.
I can see traffic from the browser to the application if I start JBOSS, Fiddler, and open the UI in Chrome. I do not see any calls from JBOSS to external services being intercepted. I thought I would see all the calls from JBOSS to external services in addition to those from the browser to JBOSS.
Update:
I tried adding these to properties-service.xml per this SO answer - no joy.
I'm running Spring 3, using Apache HttpClient as my web service client. I'm going to look into configuring proxy just for it.
Thanks to bmargulies and anyone else who looked at this. I have a solution that I hope will help someone else.
Adding -Dhttp.proxyHost parameters to my JVM startup options did nothing.
Adding those same parameters to JBOSS 5.1.2 configuration in my deployment properties-services.xml did nothing.
I believe that using Spring 3.x is a factor in explaining this behavior. I had to tell the Spring web service clients to use a proxy.
I added some Spring beans to wire in a Fiddler proxy HttpClient and injected that into the web service client, replacing the non-proxied version.
It failed the first time I tried it. It took me a while to figure out that the Apache Commons HttpConfiguration class didn’t follow the Java bean standard, so Spring blew up when it tried to wire it. I had to use the Spring MethodInvokingFactoryBean to get around it.
Here's the pertinent Spring configuration XML:
<!-- New beans for Fiddler proxy -->
<bean id="fiddlerProxyHost" class="org.apache.commons.httpclient.ProxyHost">
<constructor-arg name="hostname" value="localhost"/>
<constructor-arg name="port" value="8888"/>
</bean>
<bean id="fiddlerProxyHostConfiguration" class="org.apache.commons.httpclient.HostConfiguration"/>
<bean id="fiddlerProxyHostSetter" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="fiddlerProxyHostConfiguration"/>
<property name="targetMethod" value="setProxyHost"/>
<property name="arguments" ref="fiddlerProxyHost"/>
</bean>
<bean id="fiddlerProxyClient" class="org.apache.commons.httpclient.HttpClient">
<property name="hostConfiguration" ref="fiddlerProxyHostConfiguration"/>
</bean>
Now I can see the calls from the application to the web service in Fiddler. Joy!
Those parameters, first and foremost, are read by HttpURLConnection. They are specific to HTTP, of course, and so any other means of connecting to the outside world will necessarily ignore them.
There are many good reasons for code to avoid HttpURLConnection and just open a TCP connection through a plain old socket, even if that code plans to talk HTTP. HttpURLConnection has several 'browser emulation features' that get in the way. For example, it's broken for CORS and rejects some legitimate HTTP verbs.
Code that does that and in turn happens to do HTTP might choose to respect those parameters, and it might not. For example, I'm reasonably sure that the Apache Commons HTTP library gives the caller a choice.
If you put JBoss in a debugger and break on the socket connection primitives, I think you'll find out what's happening to you pretty quick in this respect.
I would like to create a functionality for the application admin to be able to stop and start the webservice from the front end without bringing down the entire application.
The application has various other interfaces, screens and jobs which need to run all the time. The webservice alone has to be shut down for some data maintenance work and needs to be up again after it is complete. The webservice is consumed by few thirdparty applications.
Application is built with Spring 3.0.5.
I need to somehow get the webservice object(?) in the controller and trigger stop/start.
spring-config.xml
<wss:binding url="/wss/shipRequest.wss">
<wss:service>
<ws:service bean="#shipRequestWS">
</ws:service>
</wss:service>
</wss:binding>
<bean id="shipRequestWS" class="com.xxx.ShipRequestService">
<property name="shipRequestBO" ref="shipRequestBO" />
</bean>
Service
#WebService
#SOAPBinding(style = SOAPBinding.Style.RPC, use = SOAPBinding.Use.LITERAL)
public interface ShipRequestService extends java.rmi.Remote {
WebService methods...
}
One of the most simple solutions will be adding a JMX controlled bean offering start and stop of the web service. This bean can implement the control in two types:
Setting a flag on the injected service. The service may throw an exception on deactivated state but needs to be extended to this special behavior.
The web service may be created by a special sub context to be loaded by the activation or stopped by deactivation. The context may be created with ClassPathXmlApplicationContext using the current context as a parent context.
For "lightweight" maintenance changes, you can simply make a check on a flag at the beginning of the service methods to know if you should process the request or not.This technique is widely used for feature flipping for example.
I am using Hessian in a Spring MVC project. I create server side implementation and then would like to configure the client. Client can be configured with code that uses HessianProxyFactory for the initialization of the client. The URL that is used is now hard coded in the code, but I would like to wire the service some how as a Spring bean so that the code side config would be handled with #Autowired annotation.
How to make this? All help is appreciated.
It is described in 20.3.3 Linking in the service on the client:
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
Where example.AccountService is the service interface that the server implements. The client also needs that interface, but you probably know that.
Alternatively use Java configuration:
#Bean
public HessianProxyFactoryBean accountService() {
HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
factory.setServiceUrl("http://remotehost:8080/remoting/AccountService");
factory.setServiceInterface(AccountService.class);
return factory;
}
Now you are capable of simply injecting:
#Autowired
private AccountService accountService;
The HessianProxyFactoryBean allows you to configure various other features like security and timeouts.
How to access beans from the applicationContext.xml in my service layer.
I am working on a Spring Web MVC project and that issue is that my service layer needs a URL, User ID and Password to do a web post to a server to save my data. I dont want to hardcode the URL, User ID and password in the Java Code of the Service Layer so I would like to put them into the applicationContent.xml file but I dont know how to access them from the service layer.
Can someone please help me out with this.. example code would be great.
If your service is already a Spring managed bean, it should be trivial:
Add 3 properties to your service class (url, userId and password) along with their corresponding setters. Then you can set the values via your context:
<bean id="yourService" class="xxx.yyy.zz.YourService">
<property name="url" value="http://someurl.com"/>
<property name="userId" value="username"/>
<property name="password" value="passwd"/>
</bean>
Had a similar question. May be this link will be of help.
Spring MVC 3.0 - Service layer using annonations
Create a map of the values in dispatcher-servlet.xml and access it programmatically in your class. Or better inject the values to your bean in the xml file itself.
If you would rather have these values as environmental variables, that is also possible in Spring. Use a "propertyplaceholderconfigurer" for reading the external variables.
Let me know if you need further help.
I have two webapps: a web-service client and a server (both CXF-based, using the Simple Front-End approach).
This is the server definition:
<simple:server id="server" bindingId="http://schemas.xmlsoap.org/soap/"
address="/thingy" transportId="http://schemas.xmlsoap.org/soap/"
serviceName="cs:thingyService"
serviceClass="com.mycompany.thingy.api.service.ThingyService"
endpointName="cs:thingyServicePort">
<simple:serviceBean>
<bean class="com.mycompany.thingy.server.service.ThingyServiceDelegate">
<property name="thingyService" ref="thingyService"></property>
</bean>
</simple:serviceBean>
<simple:dataBinding>
<bean class="org.apache.cxf.aegis.databinding.AegisDatabinding" />
</simple:dataBinding>
<simple:binding>
<soap:soapBinding version="1.1" mtomEnabled="true" />
</simple:binding>
</simple:server>
And here the client:
<http-conf:conduit name="*.http-conduit">
<http-conf:client AllowChunking="false" />
</http-conf:conduit>
<simple:client id="thingyService" wsdlLocation="${wsdl.url}?wsdl"
serviceName="cs:thingyService"
endpointName="cs:thingyServicePort"
transportId="http://schemas.xmlsoap.org/soap/"
address="${wsdl.url}"
bindingId="http://schemas.xmlsoap.org/soap/"
serviceClass="com.mycompany.thingy.api.service.ThingyService">
<simple:dataBinding>
<bean class="org.apache.cxf.aegis.databinding.AegisDatabinding" />
</simple:dataBinding>
<simple:binding>
<soap:soapBinding mtomEnabled="true" version="1.1" />
</simple:binding>
</simple:client>
I have an interface called ThingyService (the names have been changed ...) that is known to both client and server and the above client definition creates a proxy client that can be injected using this interface.
Everything works beautifully when both webapps are running, specifically when I deploy the server first and then the client. But when the server webapps does not start correctly, the client webapp hangs in an infinite loop trying to create the proxy from the non-existent WSDL.
Basically what I'd like is a proxy around the service proxy that lets the calls pass through when the service is available and throws an adequate exception when the service is down, which I can catch and show a "sorry, we're offline" page in the gui and resumes service when the web service is available again. I have access to the WSDL in static form through the build process (generated automatically through cxf maven plugins), so I could use this for the initial configuration, so from that point of view I am independent of the server.
Does anybody have any pointers in how to implement this functionality? The server is tomcat. The webapps may or may not be deployed onto the same server during production. The backend uses spring / jpa / cxf, the front end uses spring / wicket.
Instead of generating a proxy at runtime, it sounds like you want to create the web service proxy / client code offline.
I'm not sure how this is handled with CXF but you can use tools like wsdl2java to generate the web service client code for a given wsdl document.
As an alternative approach, the client bean could be pointed at a static wsdl file, rather than one located on a remote server.
Although the static wsdl creation approach was promising, I chose a different one (mainly because the cxf maven code generation is buggy).
I wrapped another factoryBean around the existing one, and I attached it to a service provider object that regularly pings the wsdl URL for availability. I keep a service proxy in a cache inside the factory bean, once it is created, and delete it as soon as the service provider ping fails.
If the service is currently not available, my FactoryBean throws a ServiceNotAvailableException. My front end catches this and shows a nice "Service currently unavailable" page.
Additionally, an AspectJ aspect catches all writing calls to the service and re-schedules them when the service is available again.
Here is an excerpt from my spring config:
<bean id="outerFactoryBean">
<property name="innerFactory">
<bean class="org.apache.cxf.frontend.ClientProxyFactoryBean">
<!-- translation of spring:client definition from question -->
</bean>
</property>
<property name="serviceProvider" ref="serviceProvider" />
</bean>
<bean id="serviceProvider" class="de.mytoys.shop.coupons.web.client.ServiceProvider">
<property name="wsdlUrl" value="${wsdl.url}?wsdl" />
<property name="connectionFactory">
<bean class="org.apache.cxf.transport.http.HttpURLConnectionFactoryImpl" />
</property>
</bean>
<task:scheduled-tasks>
<task:scheduled ref="serviceProvider" method="checkAvailability"
fixed-delay="1000" />
</task:scheduled-tasks>
<task:scheduler id="scheduler" pool-size="1" />