I'm using CXF with Spring to publish and to consume my WebServices in JBoss 5.1. All works fine.
However, there's a thing that's I think very tedious: to put a jaxws:endpoint tag for every WebService in applicationContext.xml.
There's realy no way to do that with annotations? Thanks for all.
As time pass, there arise some new possibilities.
Working with CXF/SpringBoot (SpringBoot: 1.2.3, CXF: 3.10, Spring: 4.1.6) there is a nice alternative in order to get rid of the jaxws:endpoint configuration in cxf-servlet.xml, as jonashackt pointed out in nabble.com. However, this solution is only possible if there is only one endpoint in the application (at least I did not succeed to configure more than one).
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServletRegistrationBean dispatcherServlet() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean(cxfServlet, "/api/*");
}
#Bean(name="cxf")
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public MyServicePortType myService() {
return new MyServiceImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), myService());
endpoint.publish("/MyService");
return endpoint;
}
Where MyServicePortType is a class having the #WebService annotation. This Endpoint is then called for URL's like "localhost:8080/api/MyService"
Of course these #Bean declarations may be placed in any other spring config class.
In contrary to the copied original solution I suggest to instantiate the Bus (cxf-Bean) by using the factory method instead of the direct "new SpringBus()":
BusFactory.newInstance().createBus()
There are some annotations to configure things that you can also put in <jaxws:endpoint>. An annotation to declare a CXF endpoint would be nice.
You can configure an endpoint using code instead of Spring XML. This can be handy if you have a lot of repetitive configuration that you can factor out. Or if you have certain endpoints configured differently in different environments.
For example:
#Autowired var authImpl: Auth = _
#Autowired var faultListener: FaultListener = _
def initWebServices() {
var sf: JaxWsServerFactoryBean = _
val propMap = mutable.HashMap[String, AnyRef]("org.apache.cxf.logging.FaultListener"->faultListener.asInstanceOf[AnyRef])
sf = new JaxWsServerFactoryBean
sf.setServiceBean(authImpl)
sf.setAddress("/auth")
sf.setServiceName(new QName("http://auth.ws.foo.com/", "auth", "AuthService"))
sf.setProperties(propMap)
sf.create
// more services...
Related
I have a use case where by the #CachePut annotation adds an entry to the cache, and I have to retrieve it manually (via code).
I can see that the total backup count gives me 1 as the number of entries, but all the maps give me the size as 0. So, I am not sure what I am doing wrong.
Here's my code
HazelcastConfig.java
#Configuration
public class HazelcastConfig {
#Bean
public Config hazelcastConf() {
Config c = new Config()
.setInstanceName("hazelcast-instance")
.addMapConfig(
new MapConfig()
.setName("testmap")
.setEvictionConfig(
new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setMaxSizePolicy(MaxSizePolicy.PER_NODE)
.setSize(1000)
)
.setTimeToLiveSeconds(500000)
);
c.getNetworkConfig().getRestApiConfig().setEnabled(true);
c.getNetworkConfig().getRestApiConfig().enableGroups(RestEndpointGroup.DATA);
return c;
}
}
TestServiceImpl.java
#Service
public class TestServiceImpl implements TestService {
#Autowired
#Lazy
private RestTemplate restTemplate;
#Override
#CachePut(value = "testmap", key="1")
public String getId() {
System.out.println("--------------------------");
System.out.println("-------INSIDE getId-------");
String id = null;
CBObject obj = restTemplate.getForObject("http://localhost:3000/testCB", CBObject.class);
if (null != obj && null != obj.getId()) {
id = String.valueOf(obj.getId());
}
System.out.println("-------- EXIT getId-------");
System.out.println("--------------------------");
return id;
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
TestController.java
#RestController
#RequestMapping("/v1")
public class TestController {
#Autowired
private TestService testService;
#GetMapping("/testCB")
public ResponseEntity<?> doCB() {
Map<String, String> resp = new HashMap<>();
String id = testService.getId();
if (null != id) {
resp.put("id", id);
}
Config config = new HazelcastConfig().hazelcastConf();
System.out.println(config.getMapConfig("testmap").getTotalBackupCount()); // 1
HazelcastInstance hz = Hazelcast.getHazelcastInstanceByName(config.getInstanceName());
System.out.println(hz.getReplicatedMap("testmap").size()); // 0
System.out.println(hz.getMap("testmap").size()); // 0
System.out.println(hz.getMultiMap("testmap").size()); // 0
return ResponseEntity.status(HttpStatus.ACCEPTED).body(resp);
}
}
Have you explicitly enabled caching using the #EnableCaching annotation (Ref Doc, Javadoc)?
Also, see the guidance from Spring Boot in the Ref Doc on Caching if you are using Spring Boot.
Furthermore, when using Spring Boot, you can either add the command-line switch --debug to your launch command or set the debug property to true in Spring Boot application.properties to get output from the Auto-configuration that has been applied. In particular you will want to see that the CacheAutoConfiguration class has been processed.
If you are NOT using Spring Boot, then in addition to the #EnableCaching annotation, you will also need to explicitly declare a CacheManager bean, such as:
#Bean
HazelcastCacheManager cacheManager(HazelcastInstance hazelcaseInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
This will require the com.hazelcast:hazelcast-spring JAR dependency on your runtime classpath.
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>${hazelcast.version}</version>
<scope>runtime</scope>
</dependency>
For example.
NOTE: Do you not confuse the HazelcastCacheManager Spring Cache Abstraction CacheManager implementation, which requires the hazelcast-spring JAR and is required by Spring's Cache Abstraction either with or without Spring Boot, with Hazelcast's standard HazelcastCacheManager. These 2 classes are not the same thing.
Alternatively, you could also use Hazelcast as a JCache caching provider implementation in either Spring Framework or Spring Boot. The core Spring Framework offers support for using JCache as well. When using Spring Boot, you will need to specify the JCache cache provider type for Hazelcast (i.e. Embedded or Client/Server). I will leave this as an exercise for you to figure out.
Lastly, I recently built an example for my own testing purposes using Hazelcast as a caching provider in Spring Framework's Cache Abstraction using Spring Boot, if you would like to take a look.
Hope this helps!
Cheers!
I am trying to use the RepliyngKafkaTemplate like I managed to use the KafkaTemplate in a REST controller.
#RestController
public class TestController {
#Autowired
private ReplyingKafkaTemplate<Object, KafkaExampleRecord, KafkaExampleRecord> replyingTemplate;
#PostMapping("/test/request")
public void requestReply(#RequestBody KafkaExampleRecord record) throws ExecutionException, InterruptedException, TimeoutException {
ProducerRecord<Object, KafkaExampleRecord> producerRecord = new ProducerRecord<>("mytopic", record);
RequestReplyFuture<Object, KafkaExampleRecord, KafkaExampleRecord> replyFuture = replyingTemplate.sendAndReceive(producerRecord);
SendResult<Object, KafkaExampleRecord> sendResult = replyFuture.getSendFuture().get(10, TimeUnit.SECONDS);
ConsumerRecord<Object, KafkaExampleRecord> consumerRecord = replyFuture.get(10, TimeUnit.SECONDS);
}
}
However I am getting the following exception.
Field replyingTemplate in com.blah.KafkaController required a bean of type 'org.springframework.kafka.requestreply.ReplyingKafkaTemplate' that could not be found.
I enabled auto configuration like this.
#Configuration
#EnableKafka
public class KafkaConfig {
}
All Kafka settings are in my application.yml.
What else do I need? Do I really have to define beans? Seems unnecessary.
Do I really have to define beans? Seems unnecessary.
Yes, you have to declare a beans for the replying template (including the reply container); Spring Boot only auto configures a simple KafkaTemplate.
Can you check, whether you are scanning the basePackages correctly. Sometimes, you may end-up with this issue, if you not scanning the packages correctly, and I have experienced this many times in the Spring Boot application.
#ComponentScan(
basePackages = {
"x.x.x.x"
}
)
I would like to create two separate endpoints, with different providers.
So if I just register one endpoint this works fine:
#Bean
public Server rsServer(MyService myService) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
serverFactory.setServiceBean(myService);
serverFactory.setAddress("/");
serverFactory.setBus(new SpringBus());
serverFactory.setProviders(MyCustomProviders.getProviders());
return serverFactory.create();
}
Now I would like to add a second service to this, but it should not use MyCustomProviders.getProviders().
I haven't been able to figure out how I could add a second Bean (which I think is the wrong way), or looking at JAXRSServerFactoryBean I haven't found a way where I can specify which providers should operate on which beans.
So something like this:
#Bean
public Server rsServer(MyService myService, MyOtherService myOtherService) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
serverFactory.setServiceBean(List.of(myService, myOtherService));
serverFactory.setAddress("/");
serverFactory.setBus(new SpringBus());
serverFactory.setProviders(MyCustomProviders.getProviders()); // How do I specify this only for MyService?
return serverFactory.create();
}
I'm using with org.apache.cxf:cxf-rt-frontend-jaxrs:3.1.4.
I'd prefer if I could do it programatically.
Any ideas?
If you're okay with separating them on different base addresses you should be able to do something like this:
#Bean
public Server createMyServiceServer(MyService myService) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
serverFactory.setServiceBean(myService);
serverFactory.setAddress("/");
serverFactory.setBus(new SpringBus());
serverFactory.setProviders(MyCustomProviders.getProviders());
return serverFactory.create();
}
#Bean
public Server createMyOtherServiceServer(MyOtherService myOtherService) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
serverFactory.setServiceBean(myOtherService);
serverFactory.setAddress("/otherservice");
serverFactory.setBus(new SpringBus());
return serverFactory.create();
}
I know you can set the server.contextPath in application.properties to change the root context.
Also, I can add an additional context in the application config for Spring Boot like the following example (in Groovy) to add an "/api" to the URL mappings of the root context:
#Bean
ServletRegistrationBean dispatcherServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
reg.name = "dispatcherServlet"
reg.addInitParameter("contextConfigLocation", "")
reg.addUrlMappings("/api/*")
reg.loadOnStartup = 2
reg
}
}
I am trying to have a separate base URI "/api" specifically for web service calls, that I can leverage for security, etc. However using the above approach will mean that any of my URIs, web service or not, can be reached with "/" or "/api", and provides no concrete segregation.
Is anyone aware of a better approach to set a base path for all #RestController(s) using configuration, without having to formally prefix every controller with /api/? If I am forced to manually prefix the URI for each controller, it would be possible to mistakenly omit that and bypass my security measures specific to web services.
Here is a reference in Stack Overflow to the same type of question, which was never completely answered:
Spring Boot: Configure a url prefix for RestControllers
In continuation to the currently accepted solution the github issue addresses the same.
Spring 5.1 and above you can implement WebMvcConfigurer and override configurePathMatch method like below
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Now all the #RestControllers will have /api as the prefix path alongside the path configured.
Official Documentation
There's a new solution to solve this kind of problem available since Spring Boot 1.4.0.RC1 (Details see https://github.com/spring-projects/spring-boot/issues/5004)
The solution of Shahin ASkari disables parts of the Auto configuration, so might cause other problems.
The following solution takes his idea and integrates it properly into spring boot. For my case I wanted all RestControllers with the base path api, but still serve static content with the root path (f.e. angular webapp)
Edit: I summed it up in a blog post with a slightly improved version see https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
RestController restApiController = beanType.getAnnotation(RestController.class);
if (restApiController != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
Also You can achieve the same result by configuring WebMVC like this:
#Configuration
public class PluginConfig implements WebMvcConfigurer {
public static final String PREFIX = "/myprefix";
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(MyCustomAnnotation.class));
}
}
Implement WebMvcConfigurer on any #Configuration class.
Override configurePathMatch method.
You can do many useful things with PathMatchConfigurer e.g. add prefix for several classes, that satisfy predicate conditions.
I had the same concern and was not a fan of the Spring EL option due to the issues documented and I wanted the prefix to be tightly controlled in the controllers but I did not want to depend on the developers doing the right thing.
There might be a better way these days but this is what I did. Can you guys see any downsides, I am still in the process of testing any side-effects.
Define a custom annotation.
This allows a developer to explicitly provide typed attributes such as int apiVersion(), String resourceName(). These values would be the basis of the prefix later.
Annotated rest controllers with this new annotation
Implemented a custom RequestMappingHandlerMapping
In the RequestMappingHandlerMapping, I could read the attribute of the custom annotation and modify the final RequestMappingInfo as I needed. Here are a few code snippets:
#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyCustomRequestMappingHandlerMapping();
}
}
And in the MyCustomRequestMappingHandlerMapping, overwrite the registerHandlerMethod:
private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class);
public MyCustomRequestMappingHandlerMapping() {
super();
}
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// find the class declaring this method
Class<?> beanType = method.getDeclaringClass();
// check for the My rest controller annotation
MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class);
if (myRestAnnotation != null) {
// this is a My annotated rest service, lets modify the URL mapping
PatternsRequestCondition oldPattern = mapping.getPatternsCondition();
// create a pattern such as /api/v${apiVersion}/${resourceName}
String urlPattern = String.format("/api/v%d/%s",
myRestAnnotation.apiVersion(),
myRestAnnotation.resourceName());
// create a new condition
PatternsRequestCondition apiPattern =
new PatternsRequestCondition(urlPattern);
// ask our condition to be the core, but import all settinsg from the old
// pattern
PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);
myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}",
beanType, myRestAnnotation, oldPattern, updatedFinalPattern);
mapping = new RequestMappingInfo(
mapping.getName(),
updatedFinalPattern,
mapping.getMethodsCondition(),
mapping.getParamsCondition(),
mapping.getHeadersCondition(),
mapping.getConsumesCondition(),
mapping.getProducesCondition(),
mapping.getCustomCondition()
);
}
super.registerHandlerMethod(handler, method, mapping);
}
}
Slightly less verbose solution which doesn't duplicate the logic of checking the annotation, but only changes the mapping path:
private static final String API_PREFIX = "api";
#Bean
WebMvcRegistrationsAdapter restPrefixAppender() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
if (mappingForMethod != null) {
return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod);
} else {
return null;
}
}
};
}
};
}
Side effects
Your error controller will also be mapped under /api/error, which breaks error handling (DispatcherServlet will still redirect errors to /error without prefix!).
Possible solution is to skip /error path when adding /api prefix in the code above (one more "if").
Someone has filed an issue in the Spring MVC Jira and come up with a nice solution, which I am now using. The idea is to use the Spring Expression Language in the prefix placed in each RestController file and to refer to a single property in the Spring Boot application.properties file.
Here is the link of the issue: https://jira.spring.io/browse/SPR-13882
I am running Restful web-service as standalone application using Jersey.
Below are my service classes which serve's the requests.
LoginServiceImpl.java
#Component
public class LoginServiceImpl implements LoginService {
#Value("${login.service.defaultmessage}")
private String defaultMessage;
#Autowired
private EmLoginDAO emLoginDAO;
#Override
public String defaultCall() {
return defaultMessage;
}
#Override
public String updatePassword(List<Login> userList) {
System.out.println(emLoginDAO + "\n" + userList);
emLoginDAO.save(userList);
return "Passwords Updated...";
}
#Override
public List<Login> getPasswords() {
System.out.println("OBJECT: " + emLoginDAO);
List<Login> userList = null;
userList = emLoginDAO.findAll();
return userList;
}
}
LoginService.java
#Component
#Path("/user")
public interface LoginService {
#GET
public String defaultCall();
#POST
#Path(value = "/print")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.TEXT_PLAIN)
public String updatePassword(List<Login> userList);
#GET
#Path(value = "/getpassword")
#Produces(MediaType.APPLICATION_XML)
public List<Login> getPasswords();
}
Below is my spring configuration file.
<context:component-scan base-package="com.em.login" />
<context:annotation-config />
After starting the service when I call the respective method get called.
But my defaultMessage and emLoginDAO objects are null. So it is not referring to the properties and spring configuration files.
So can any one please help me to get this correct. Or to find a way to set the properties and spring configuration file paths to Jersey.
Update
Closeable server = null;
try {
DefaultResourceConfig resourceConfig = new DefaultResourceConfig(
LoginServiceImpl.class);
resourceConfig.getContainerResponseFilters().add(
new GZIPContentEncodingFilter());
server = SimpleServerFactory.create(serviceurl,
resourceConfig);
System.in.read();
} catch (IOException e) {
} finally {
if (server != null) {
try {
server.close();
} catch (IOException ex) {
}
}
}
I think this is the culprit:
DefaultResourceConfig resourceConfig = new DefaultResourceConfig(LoginServiceImpl.class);
You are using Spring's IOC to create the objects and do the autowiring, but you are not getting the instance from the Spring container. You need to get the LoginServiceImpl instance from the Spring container, and not have Jersey create it (Jersey does not know how to autowire your #Autowired properties.
You should use the Spring integration with Jersey, seen here.
Edit to respond to your comment, you posted this code:
LoginServiceImpl loginServiceImpl = (LoginServiceImpl) SpringApplicationContext.getBean("loginServiceImpl");
DefaultResourceConfig resourceConfig = new DefaultResourceConfig( loginServiceImpl.getClass());
You are creating a loginServiceImpl via the spring container, and I'll bet if you check your autowired fields will be there.
However, the second line where you use loginServiceImpl.getClass() this is going to create a new LoginServiceImpl, which is not the same one as the loginServiceImpl you got from the context, so you still are going to have the same problem.
You could take a look at Microserver, that will do all the wiring between Jersey and Spring for you (and setup a Grizzly webserver). From the tags I notice you are using Spring boot, with Microserver: micro-boot module you can do (in a class in package com.em.login):
public static void main(String[] args){
new MicrobootApp(()->"test-app").run();
}
And it should wire up Grizzly, Jersey & Spring with Spring-boot enabled for any backend (non-Jax-rs) dependencies.
Alternatively without Spring Boot (plain old Jersey and Spring)
public static void main(String[] args){
new MicroserverApp(()->"test-app").run();
}
To do it manually, you will need to add the Jersey-Spring integration jar to your classpath and make sure both are configured in a way that interoperates (i.e. I think a registering Spring ContextListener is essential). There is an example app here.
Have you configured those two in your spring configuration files?
I mean have you annotated EmLoginDAO also as stereotype Component?
I got this working.
Referred the this part of the Jersey documentation.
Below is the code I have used to make this working.
ResourceConfig resourceConfig = new ResourceConfig(LoginServiceImpl.class);
resourceConfig.register(org.glassfish.jersey.server.filter.UriConnegFilter.class);
resourceConfig.register(org.glassfish.jersey.server.spring.SpringComponentProvider.class);
resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
resourceConfig.property("contextConfigLocation", "classpath:/spring-config.xml");
URI serviceUri = UriBuilder.fromUri(serviceHost).port(servicePort).build();
server = SimpleContainerFactory.create(serviceUri, resourceConfig);
Thank you all for helping.