I want to call a remote SOAP/CXF web service from a Spring app. The complication is I cannot pre-configure invocation params (remote endpoint URL) at app startup via XML or Java config. Instead I need to obtain them dynamically from an external configuration, taking a fresh value each invocation. (Actually the config is backed by LDAP and accessbible via a custom Java library.)
¿Does Spring 4 offers a common way to achieve that?
For now, I came with the following custom solution, but I don't like it much beacause I need to define a dummy proxy class DynamicallyConfiguredMyService at least.
#Component
class ParentBean {
#Inject
MyService service;
...
service.doAction(); // calling WS
}
interface MyService {
void doAction();
}
#Component
class DynamicallyConfiguredMyService implements MyService {
void doAction() {
getRealService.doAction();
}
private MyService getRealService() {
String url = get URL from external config
MyService endpoint = new MyServiceHelper().getMyService();
Map<String, Object> ctx = ((BindingProvider) endpoint).getRequestContext();
ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
return endpoint;
}
}
Spring 4, Java 7, JBoss EAP 6.4.x
Related
I have read an article about mapping an DTO class to an entity. In the article, it manages to create a very general and extensible way of mapping DTO using annotations and RequestResponseBodyMethodProcessor. I have been creating a SOAP service using Spring Boot + Apache CXF. The service is still in an early stage, but it will get really big in the following months. The DTO pattern in the article seems to be a good choice for separating what the client sends and what is stored in the database. I have tried multiple ways of implementing it in the project, but none worked properly. I know RequestResponseBodyMethodProcessor is only used for #RequestBody and #ResponseBody, so I tried putting it in the SOAP Service, but Spring seems to ignore it completely. I have also done some search and maybe the problem comes from the fact CXF uses JAXB and Spring uses Jackson. If this is the case, is there any way to integrate CXF to use Jackson? If the problem isn't JAXB and Jackson, is there any other way of implementing the above pattern for a SOAP service? Just for completeness, the project is Java 1.8, the SOAP service is created using #WebService and the service is published through an WebServiceConfiguration class. Example code of how the code looks like:
#WebService
#Service
public interface MyService {
#WebMethod
public void myEndpoint(#WebParam(name = "someClass") SomeClass someClass);
// others endpoints
}
#Configuration
public interface WebServiceConfiguration {
#AutoWired
private MyServiceImpl myServiceImpl;
#Bean
public ServletRegistrationBean<CXFServlet> servletRegistrationBean(ApplicationContext context) {
return new ServletRegistrationBean<>(new CXFServlet(), "/service/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus bus() {
return new SpringBus();
}
#Bean
public Endpoint myService() {
Edpoint endpoint = new EndpointImpl(bus(), myServiceImpl);
endpoint.publish("/myService");
return endpoint;
}
}
I'm running a test on Hello World trying to follow the Jersey Framework in my spring java program.
I have extended JerseyTest, but I'm getting the error above. The example Jersey gives link doesn't seem to help.
public class SimpleTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(RestService.class);
}
#Test
public void test() {
final String hello = target("\service\hello").request().get(HelloModel.class);
assertEquals("Hello World!", hello);
}
}
With Jersey 2.26 the dependency has changed to jersey-spring4, but to flush out the answer a little bit more if you don't want to spin up the entire Spring context.
Create the object:
final ResourceConfig resourceConfig = new ResourceConfig(restResource);
In my case, I just needed an empty context:
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
resourceConfig.property("contextConfig", context);
That was able to allow the JerseyTest to run successfully.
This is going to happen if you have the jersey-spring3 dependency. The default behavior is to look for this applicationContext.xml file (which is your Spring context configuration).
If you want to configure Spring for the test, you can do a couple things.
You could manually create the ApplicationContext and pass it to Jersey
ApplicationContext context = ...
return new ResourceConfig(RestService.class)
.property("contextConfig", context)
If you are using xml configuration, then you would create a ClassPathXmlApplicationContext. If you're using Java config, then you would create an AnnotationConfigApplicationContext.
If you need servlet servlet container support, check out the example in this post
I'm attempting to write functional tests for my REST API using the Jersey Test framework. However, I've seem to hit a roadblock when it comes to using dependency injection within my functional tests. My main application looks like this:
#ApplicationPath("/")
public class Application extends ResourceConfig {
private static final URI BASE_URI = URI.create("http://localhost:8080/api/");
public static void main(String[] args) throws Exception {
System.out.println("Starting application...");
final ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(JacksonFeature.class);
resourceConfig.register(LoggingFeature.class);
resourceConfig.packages(true, "my.package.name");
final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, locator);
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
server.start();
Thread.currentThread().join();
}
}
Notice here that I'm using the HK2's ServiceLocatorUtilities.createAndPopulateServiceLocator() method in order to read the hk2-metadata-generator file. This method creates a ServiceLocator object which then in turn is passed to the GrizzlyHttpServerFactory.createHttpServer method. This all works great for running the Grizzly server, however, the question I have now is how do I create functional tests for my application with the Jersey Test Framework?
My unit test currently looks like this:
public class FormsResourceTest extends JerseyTest {
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
#Test
public void testMe() {
Response response = target("/test").request().get();
assertEquals("Should return status 200", 200, response.getStatus());
}
}
Is there even a way to use the HK2 service locator with the Jersey Test framework or do I need to treat my application as an external container and use the external container provider as documented here: External container?
Also, since these are functional tests, mocking the injected services is not an option here.
You can use the Locator Bridge to take two separate locator (the one you created and the one from Jersey) and bridge them together. The bridge can be made bi-directional as well (within limits) and so it'll appear in most normal usage to be one large ServiceLocator.
Note that there was a bug fixed this week with the ServiceLocator bridge which has not yet been pushed out to maven but will (probably) be pushed sometime next week. See HK2-295
I have an application that is trying to access a webservice using generated classes via wsdl2java. I would like to be able to configure it so that I can use a different endpoint based on the environment (TEST/PROD).
I found the following answer to be exactly what I was looking for
https://stackoverflow.com/a/3569291/346666
However, I would like to use Spring to inject an instance of the service into my service layer - is there a pure Spring approach to the above?
Or, is there a better way to inject an instance of a webservice into a class and still be able to dynamically configure the endpoint?
Using Spring Java-based configuration:
#Configuration
public class HelloServiceConfig {
#Bean
#Scope("prototype")
public HelloService helloService(#Value("${webservice.endpoint.address}") String endpointAddress) {
HelloService service = new HelloService();
Hello port = service.getHelloPort();
BindingProvider bindingProvider = (BindingProvider) port;
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,endpointAddress);
return service;
}
}
#Component
public class BusinessService {
#Autowired
private HelloService hellowService;
...
public void setHelloService(HelloService helloService) {
this.helloService = hellowService;
}
}
Edit
To use this with Spring XML-based configuration you just need to register the HelloServiceConfig as a bean in your Spring context xml file:
<bean class="com.service.HelloServiceConfig.class"/>
<bean id="businessService" class="com.service.BusinessService">
<property name="helloService" ref="helloService"/>
</bean>
Other alternatives for creating web service clients in Spring include using Spring Web Services or Apache CXF. Both options allow defining a JAX-WS client based on wsdl2java using only XML but required additional dependencies.
I have recently wrote a very simple Restful service deployed to JBoss AS 7.
I have a JAX-RS interface defiled as (using scala):
#Provider
#Path("/customers")
trait ICustomerService {
#GET
#Path("/{id}")
#Produces(Array("application/xml"))
def getCustomer(#PathParam("id") id: Int): StreamingOutput
}
And a class implements it (using scala):
class ServiceFacade extends ICustomerService {
val ctx = new ClassPathXmlApplicationContext("orderservice.xml")
val customerService = ctx.getBean("customerService").asInstanceOf[CustomerService]
def getCustomer(id: Int): StreamingOutput = {
customerService.getCustomer(id)
}
}
Here the problem comes. Everytime I issue a request from a client browser, a new ServiceFacade is created by Jboss, thus the Spring xml file is parsed once.
Is there anyway I can create the ServiceFacade myself in a spring config and simply let JBoss uses it rather than create for every single clieng request?
Many thanks.
You are creating a new Spring context on every instance creation of your ServiceFacade, Try either injecting the context or creating a singleton. I don't believe JAX-RS or RestEasy gurantees only a single instance of a annotated class is created.
Also, I am just getting up to speed on Scala myself, but should you not place the annotations on the implementation and not the trait?