JBoss 7.1.1 Webservice programatic JAAS authentication - web-services

I have a simple POJO as a webservice that is mapped to, lets say /public/authenticate :
WebService
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class AuthWS{
#WebMethod
public boolean doAuthenticate(String securityToken) {
....
}
}
This webservice doesn't requre authentication and is not a protected resource.
I do have other private webservices mapped to path : /private/ws/*;
For the moment I use a security-domain that has a Database login module setup. It works fine, but user first needs to authenticate trought a web form based that makes a post request to /j_security_check. Only after this step user can use other private webservices.
I want to perform a programatically authentication after client calls this doAuthenticate method. So that client to be able to invoke other /private/ws/* webservice methods.
I'll type what I want to achieve:
#WebService
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class AuthWS{
#WebMethod
public boolean doAuthenticate(String securityToken) {
SomeSecurityManager manager= SomeSecurityManager.getDefaultManager()
Map<String,Object> map = new HashMap<String, Object>();
map.put("MY_CUSTOM_SECURITY_TOKEN",securityToken);
manager.doLogin(map);
// after webservice method returns, client should now be able to invoke other private webservice
// this means that the manager should associate with this session an authenticated user.
// in order that authorization to work.
}
}
And my CustomLoginModule :
class CustomLogModule implements LoginModule {
...
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
// store what's needed
}
public boolean login(){
// get securityToken send from the SomeSecurityManager and validate it.
// get user information from that token and store into Subject object.
}
}
And in my CustomLoginModule that implements JAAS LoginModule to check that securityToken with a custom logic, verify if it right signed with a public key for example. That securityToken contains information about principal.
If you need more details, feel free to ask.
Thanks.
EDITED
1.) Created custom-login-module.jar together with module.xml
<module xmlns="urn:jboss:module:1.1" name="custom.login.module">
<resources>
<resource-root path="custom-login-module.jar"/>
</resources>
<dependencies>
<module name="org.picketbox"/>
<module name="javax.api"/>
<module name="org.slf4j"/>
</dependencies>
</module>
2.) Added custom-login-module.jar and module.xml into jboss-as-7.1.1.Final\modules\custom\login\module
3.) custom-login-module.jar contains :
public class CustomCallbackHandler implements CallbackHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomCallbackHandler.class);
private String token;
public CustomCallbackHandler(String token) {
this.token= token;
}
public String getToken() {
return token;
}
#Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof TokenCallback) {
((TokenCallback) callback).setToken(token);
}
}
}
}
public class TokenCallback implements Callback {
private static final Logger LOGGER = LoggerFactory.getLogger(TokenCallback.class);
private String token;
public TokenCallback() {
}
public String getToken() {
return token;
}
public void setToken(String token) {
LOGGER.info("Setting token = " + token);
this.token = token;
}
}
public class CustomLoginModule extends AbstractServerLoginModule {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomLoginModule.class);
#Override
public boolean login() throws LoginException {
LOGGER.info("Doing login()");
boolean login = super.login();
super.loginOk = true;
return login;
}
#Override
protected Principal getIdentity() {
return new UserPrincipal("some user");
}
#Override
protected Group[] getRoleSets() throws LoginException {
return new Group[]{new MyGroup()}; // that and has name 'dummy'
}
}
These are only dummy implementations.
My web application is deployed from within a .war archive. And it Contains following :
jboss-web.xml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE jboss-web
PUBLIC -//JBoss//DTD Web Application 2.3V2//EN
http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd>
<jboss-web>
<security-domain>custom-auth</security-domain>
</jboss-web>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>WebApp</display-name>
<session-config>
<session-timeout>120</session-timeout>
</session-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<description>Protects all private resources</description>
<url-pattern>/private/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>dummy</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-role>
<role-name>dummy</role-name>
</security-role>
<servlet>
<servlet-name>Private</servlet-name>
<servlet-class>com.company.private.PrivateWs</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Private</servlet-name>
<url-pattern>/private/PrivateWs</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>AuthWS</servlet-name>
<servlet-class>com.company.auth.AuthWS</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AuthWS</servlet-name>
<url-pattern>/AuthWS</url-pattern>
</servlet-mapping>
</web-app>
#WebService
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class AuthWS{
private static final Logger LOGGER = LoggerFactory.getLogger(AuthWS.class);
#WebMethod
public boolean doAuthenticate(String token) {
tryProgrammaticLogin(token);
return true;
}
private void tryProgrammaticLogin(String token) {
LoginContext loginContext = null;
try {
loginContext = new LoginContext("custom-auth", new CustomCallbackHandler(token));
loginContext.login();
} catch (LoginException e) {
LOGGER.info("Some problem occured when trying to custom login.", e);
}
}
}
The call to doAuthenticate from my ws client works but the problem is that after try ProgrammaticLogin an exception occurs. And the PrivateWS is not accesible by client.
17:33:40,901 INFO [com.mycompany.AuthWS] (http--0.0.0.0-8080-1) Some problem occured when trying to custom login.: javax.security.auth.login.LoginException: Login Failure: all modules ignored
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:921) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683) [rt.jar:1.6.0_26]
at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext.login(LoginContext.java:579) [rt.jar:1.6.0_26]
standalone.xml from jboss configuration directory contains:
<security-domain name="custom-auth">
<authentication>
<login-module code="com.mycompany.CustomLoginModule" flag="required" module="custom.login.module"/>
</authentication>
</security-domain>
Please tell me if the way of doing authentication with creating a new LoginContext object is the right way of doing. I can't understand why this problem occurs.

Related

Apache Camel - testing log message

I'm trying to test a Camel route which uses from(x).to(y).log("SuccessKey123") and onException(HttpOperationFailedException.class).log("ErrorKey123").
How can I test that Camel logs "SuccessKey123" when the message was successfully processed or it logs "ErrorKey123" if HttpOperationFailedException is thrown?
My RouteBuilder():
#Component
public class myHttp4RouteBuilder extends SpringRouteBuilder {
public static final ID = "foo";
#Override
public void configure() throws Exception {
onException(HttpOperationFailedException.class)
.log("ErrorKey123")
.to(ERROR_QUEUE)
.handled(true);
from(AWS_SQS_ENDPOINT)
.to(REST_API_ENDPOINT)
.log("SuccessKey123");
}
}
Testclass:
public class myHttp4RouteBuilderTest {
#Produce(uri = MOCK_ROUTE_FROM)
protected ProducerTemplate template;
#EndpointInject(uri = MOCK_ROUTE_TO)
private MockEndpoint mockEndpoint;
#Autowired
private CamelContext camelContext;
#Before
public void setup() throws Exception{
RouteDefinition rd = camelContext.getRouteDefinition(myHttp4RouteBuilder.ID);
rd.adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith(MOCK_ROUTE_FROM);
weaveByToUri(ERROR_QUEUE)
.replace()
.to(MOCK_ROUTE_TO);
}
});
}
#Test
#DirtiesContext
public void testSuccess() throws Exception {
// throw an HttpOperationFailedException
mockEndpoint.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
throw new HttpOperationFailedException("Exception", 400, null, null, null, null);
}
});
//
//
// How can I test here that camel logs "ErrorKey123"
//
//
template.sendBody(MOCK_ROUTE_FROM, "some content");
mockEndpoint.assertIsSatisfied();
}
}
Thank you very much!
Camel uses slf4j so you can just add some test appender on setup to the required logger and check what was logged after that (or even mock appender interface)
I got it ;-) You put me to the right way. Thanks!
This is my solution:
First: create a custom Appender
package de.example.test;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.slf4j.event.LoggingEvent;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Plugin(name="myAppenderForTesting", category="Core", elementType="appender", printObject=true)
public class MyAppenderForTesting extends AbstractAppender {
/** Here we collect all log messages */
public static List<LogEvent> logEvents = new ArrayList<>();
protected MyAppenderForTesting(String name, Filter filter, Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
#PluginFactory
public static MyAppenderForTesting createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
return new MyAppenderForTesting(name, filter, layout, true);
}
#Override
public void append(LogEvent event) {
try {
logEvents.add(event);
} catch (Exception ex) {
if (!ignoreExceptions()) {
throw new AppenderLoggingException(ex);
}
} finally {
}
}
/**
* Clear log messages
*/
public static void clean() {
logEvents.clear();
}
}
Short explanation: with append() method we add each log event to a public static variable logEvents. Later in test we can access logEvents.
It was a little bit difficult to get this appender working with log4j. In my case I created a log4j2.xml in the test resources src\test\resources\log4j2.xml.
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration packages="de.example">
<Appenders>
<myAppenderForTesting name="myAppenderForTesting">
<PatternLayout alwaysWriteExceptions="false" pattern="%d{dd.MM.yyyy HH:mm:ss} %-5p %t [%C{1}.%M:%L] %m %ex{10000}%n" />
</myAppenderForTesting>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="myAppenderForTesting"/>
</Root>
</Loggers>
</Configuration>
In my test classes I can access directly to MyAppenderForTesting.logEvents. For example
for (LogEvent event : MyAppenderForTesting.logEvents) {
String message = event.getMessage().toString();
if (message.contains(search)) {
// do somethind
}
}
A different approach could be to use a log listener to collect the messages and assert them afterwards:
// adding log listener
Set<String> logMessages = new HashSet<>();
((SpringBootCamelContext) camelContext)
.addLogListener((Exchange exchange, CamelLogger camelLogger, String message) -> {
logMessages.add(message);
return message;
});
// others test initializations...
// asserting the expected log message
assertThat(logMessages.stream()
.filter(m -> m.contains("looking for this message")).count()).isEqualTo(1);
You can also use Camel's advice-with and then mock/replace those log endpoints with a mock etc, and then just assert that Camel routed a message to those depending on what you do.
http://camel.apache.org/advicewith.html
I agree with Claus Ibsen's answer. You could use AdviceWith and weaveByType(LogDefinition.class).selectIndex(...) to pinpoint the logging you expect.
Old thread but it has a high visibility, so I hope this answer helps someone.
e.g.
#SpringBootTest
#CamelSpringBootTest
public class MyRouteTest {
#Autowired
protected CamelContext context;
#EndpointInject("mock:successRoute")
private MockEndpoint successRouteMockEndpoint;
#EndpointInject("mock:failRoute")
private MockEndpoint failRouteMockEndpoint;
...
#Test
public void Given_SuccessfulCall_ThenLogSuccess() throws Exception {
AdviceWith.adviceWith(context, myRouteId,
a -> a.weaveByType(LogDefinition.class).selectIndex(1).replace().to(successRouteMockEndpoint));
// directives to mock a successful response
successRouteMockEndpoint.expectedMessageCount(1);
failRouteMockEndpoint.expectedMessageCount(0);
// trigger route
successRouteMockEndpoint.assertIsSatisfied();
failRouteMockEndpoint.assertIsSatisfied();
}
#Test
public void Given_UnsuccessfulCall_ThenLogFailure() throws Exception {
AdviceWith.adviceWith(context, myRouteId,
a -> a.weaveByType(LogDefinition.class).selectIndex(0).replace().to(failRouteMockEndpoint));
// directives to mock an unsuccessful response
successRouteMockEndpoint.expectedMessageCount(0);
failRouteMockEndpoint.expectedMessageCount(1);
// trigger route
successRouteMockEndpoint.assertIsSatisfied();
failRouteMockEndpoint.assertIsSatisfied();
}
}

How to use Tuckey urlrewrite in spring boot to access service using ?wsdl

My clients wanted to access wsdl using ?wsdl notation, but the springboot service i have created is working with only '.wsdl' format. I need a working example/sample how to configure Tuckey urlrewrite in the springboot application.
I have tried using below code, but the application complaints as it cant find urlrewrite.xml (which i have placed in src/main/resources folder.
Q1: How can i make my service to be accessible using url below
http://localhost:8080/ws/organisation?wsdl
I have tried using below code, but tuckey cannot find the urlrewrite.xml which is under src/java/resources.
#Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
final FilterRegistrationBean registrationBean = new ilterRegistrationBean();
registrationBean.setFilter(new UrlRewriteFilter());
registrationBean.addInitParameter("confPath", "urlrewrite.xml");
return registrationBean;
}
Finally I could figure out a solution. This is now reading urlrewrite.xml from the src/main/resources folder.
No need to declare above mentioned bean definition in the question post (public FilterRegistrationBean tuckeyRegistrationBean()), as the below code declared as #Component will automatically register with context and url-rewriting is performed.
#Component
public class WsdlUrlRewriteFilter extends UrlRewriteFilter {
private static final String CONFIG_LOCATION = "classpath:/urlrewrite.xml";
#Value(CONFIG_LOCATION)
private Resource resource;
#Override
protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
Conf conf = new Conf(filterConfig.getServletContext(), resource.getInputStream(), resource.getFilename(), "");
checkConf(conf);
} catch (IOException ex) {
throw new ServletException("Unable to load URL-rewrite configuration file from " + CONFIG_LOCATION, ex);
}
}
}
Best option is to write your own filter as follows.You can make use of HttpServletRequestWrapper to handle the ?wsdl extension and let the server handle the request.
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
#Component
public class WSDLQuestionMarkReplaceFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//put init logs
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ("wsdl".equalsIgnoreCase(httpRequest.getQueryString())) {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
#Override
public String getQueryString() {
return null;
}
#Override
public String getRequestURI() {
return super.getRequestURI() + ".wsdl";
}
};
chain.doFilter(requestWrapper, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
//put destroy logs
}
}
It worked for me. Hope it works for others too. SpringBoot+JAR
Please use the following dependency
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.4</version>
</dependency>
Created urlrewrite.xml in resource folder
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite
PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite>
<rule>
<name>Domain Name Check</name>
<condition name="host" operator="notequal">www.userdomain.com</condition>
<from>^(.*)$</from>
<to type="redirect">http://www.userdomain.com$1</to>
</rule>
Added in main ApplicationRunner.java
#Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CustomURLRewriter());
return registrationBean;
}
And created a CustomURLRewriter
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;
import org.tuckey.web.filters.urlrewrite.UrlRewriter;
import javax.servlet.*;
import java.io.InputStream;
public class CustomURLRewriter extends UrlRewriteFilter {
private UrlRewriter urlRewriter;
#Autowired
Environment env;
#Override
public void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
ClassPathResource classPathResource = new ClassPathResource("urlrewrite.xml");
InputStream inputStream = classPathResource.getInputStream();
Conf conf1 = new Conf(filterConfig.getServletContext(), inputStream, "urlrewrite.xml", "");
urlRewriter = new UrlRewriter(conf1);
} catch (Exception e) {
throw new ServletException(e);
}
}
#Override
public UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#Override
public void destroyUrlRewriter() {
if(urlRewriter != null)
urlRewriter.destroy();
}
}

Jackson not consuming the JSON root element

I'm using JAX-RS + Jersey to consume the web-service request and Jackson to translate JSON data:
#Path("/")
public class JAXRSRestController {
#Path("/jsonRequest")
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response submitJsonRequest(SampleObject sampleObject, #Context HttpHeaders headers)
{
Ack ack = new Ack();
ack.setUniqueId(sampleObject.getId());
ack.setType(sampleObject.getName());
return Response.ok().entity(ack).build();
}
}
Here if the request is in the below format, it would not be consumed:
{
"sampleObject": {
"id": "12345",
"name": "somename"
}
}
But if the request is in the below format, it will be consumed:
{
"id": "12345",
"name": "somename"
}
How can I make the controller consume the Json root element as well?
SampleObject class:
import org.codehaus.jackson.map.annotate.JsonRootName;
#XmlRootElement(name = "sampleObject")
#JsonRootName(value = "sampleObject")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "SampleObject", propOrder = {
"id",
"name"
})
public class SampleObject
{
protected String id;
protected String name;
public SampleObject(){}
public SampleObject(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
web.xml:
<?xml version="1.0" encoding= "UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<display-name>Wed Application</display-name>
<servlet>
<servlet-name>Jersey RESTFul WebSerivce</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.jaxrs.rest</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey RESTFul WebSerivce</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
There are two approaches I can think of. If this is a common occurrence in your application, I would recommend enabling unwrapping on your ObjectMapper. If this is a one-off situation, a wrapper object is not a bad option.
A. Enable Unwrapping
#JsonRootName will only apply if unwrapping is enabled on the ObjectMapper. You can accomplish this with a deserialization feature. Note that this will unwrap all requests:
public CustomObjectMapper() {
super();
enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
}
If you do not already have a custom ObjectMapper registered then you will need to add a provider to register your custom config with Jersey. This answer explains how do accomplish that.
B. Create a Wrapper
If you do not want to unwrap globally, you can create a simple wrapper object and omit the #JsonRootName annotation:
public class SampleObjectWrapper {
public SampleObject sampleObject;
}
Then update your resource method signature to accept the wrapper:
public Response submitJsonRequest(SampleObjectWrapper sampleObjectWrapper, #Context HttpHeaders headers)

Endpoint Publish for REST Web Services

I've published JAX-WS web services with Endpoint.publish during development. Is there any such utility class exists (in JAX-RS) for publishing REST web services in jersey? I referred couple of articles, and majority of them are based on publishing the web services in some containers like Jetty, Grizzly etc.
Jersey-Grizzly has a very simple solution. From https://github.com/jesperfj/jax-rs-heroku:
package embedded.rest.server;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
#Path("/hello")
public class Main {
public static void main(String[] args) {
final String baseUri = "http://localhost:7080/";
final Map<String, String> initParams = new HashMap<String, String>();
// Register the package that contains your javax.ws.rs-annotated beans here
initParams.put("com.sun.jersey.config.property.packages","embedded.rest.server");
System.out.println("Starting grizzly...");
try {
SelectorThread threadSelector =
GrizzlyWebContainerFactory.create(baseUri, initParams);
System.out.println(String.format("Jersey started with WADL "
+ "available at %sapplication.wadl.", baseUri));
}
catch(Exception e) {
e.printStackTrace();
}
}
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Well, this was easy!";
}
}
If you're using Maven, you'll need the following three dependencies:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-grizzly</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-servlet-webserver</artifactId>
<version>1.9.18-i</version>
</dependency>
To test it, just open http://localhost:7080/hello in a browser.
I think you can use Provider interface to publishing a RESTful Web Service with JAX-WS.
The example class:
#WebServiceProvider
#BindingType(value=HTTPBinding.HTTP_BINDING)
public class AddNumbersImpl implements Provider {
#Resource
protected WebServiceContext wsContext;
public Source invoke(Source source) {
try {
MessageContext mc = wsContext.getMessageContext();
// check for a PATH_INFO request
String path = (String)mc.get(MessageContext.PATH_INFO);
if (path != null && path.contains("/num1") &&
path.contains("/num2")) {
return createResultSource(path);
}
String query = (String)mc.get(MessageContext.QUERY_STRING);
System.out.println("Query String = "+query);
ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST);
int num1 = Integer.parseInt(req.getParameter("num1"));
int num2 = Integer.parseInt(req.getParameter("num2"));
return createResultSource(num1+num2);
} catch(Exception e) {
e.printStackTrace();
throw new HTTPException(500);
}
}
private Source createResultSource(String str) {
StringTokenizer st = new StringTokenizer(str, "=&/");
String token = st.nextToken();
int number1 = Integer.parseInt(st.nextToken());
st.nextToken();
int number2 = Integer.parseInt(st.nextToken());
int sum = number1+number2;
return createResultSource(sum);
}
private Source createResultSource(int sum) {
String body =
"<ns:addNumbersResponse xmlns:ns="http://java.duke.org"><ns:return>"
+sum
+"</ns:return></ns:addNumbersResponse>";
Source source = new StreamSource(
new ByteArrayInputStream(body.getBytes()));
return source;
}
}
To deploy our endpoint on a servlet container running with the JAX-WS
RI we need to create a WAR file.
The adjusted web.xml:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener
</listener>
<servlet>
<servlet-name>restful-addnumbers</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restful-addnumbers</servlet-name>
<url-pattern>/addnumbers/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
and need to add sun-jaxws.xml deployment descriptor to the WAR file.
<endpoints
xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
version="2.0">
<endpoint
name="restful-addnumbers"
implementation="restful.server.AddNumbersImpl"
wsdl="WEB-INF/wsdl/AddNumbers.wsdl"
url-pattern="/addnumbers/*" />
</endpoints>
Or could be create simple HttpServer
import java.io.IOException;
import com.sun.jersey.api.container.httpserver.HttpServerFactory;
import com.sun.net.httpserver.HttpServer;
public class YourREST {
static final String BASE_URI = "http://localhost:9999/yourrest/";
public static void main(String[] args) {
try {
HttpServer server = HttpServerFactory.create(BASE_URI);
server.start();
System.out.println("Press Enter to stop the server. ");
System.in.read();
server.stop(0);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Subresource for target class has no jax-rs annotations

I am trying to call a webservice method via a proxy but I have got an error message that says: "Subresource for target class has no jax-rs annotations.: org.jboss.resteasy.core.ServerResponse"
Here is my server class
#Path("/authorizationCheck")
public class AuthorizationRestService implements AuthorizationService {
#Override
#Path("/webserviceTest")
public Response webserviceTest(){
TestDTO x = new TestDTO();
x.setFieldOne("ffff");
x.setFieldTwo("gggg");
Response res = Response.ok(x).build();
return res;
}
}
with a an interface like this
#Path("/authorizationCheck")
public interface AuthorizationService {
#POST
#Path("/webserviceTest")
public Response webserviceTest();
}
and my return object wrapped in response
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class TestDTO {
private String fieldOne;
private String fieldTwo;
public String getFieldOne() {
return fieldOne;
}
public void setFieldOne(String fieldOne) {
this.fieldOne = fieldOne;
}
public String getFieldTwo() {
return fieldTwo;
}
public void setFieldTwo(String fieldTwo) {
this.fieldTwo = fieldTwo;
}
}
and finally my client class
#Stateful
#Scope(ScopeType.CONVERSATION)
#Name("authorizationCheckService")
public class AuthorizationCheckService {
public void testWebservice(){
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
AuthorizationService proxy =
ProxyFactory.create(AuthorizationService.class,
ApplicationConfig.WORKFLOWSERVER_URL + "services/authorizationCheck/webserviceTest");
Response response = proxy.webserviceTest();
return;
}
}
what I am doing wrong here , any help will be appreciated.
You have two annotations with webserviceTest() which are #POST and #Path.
Repeat BOTH the annotations in over ridden method in implemented class. That means add the #POST annotation to webserviceTest() method.
It should work then !
And here is the reason why it din't work.. without proper annotations in implementing class.
Why java classes do not inherit annotations from implemented interfaces?
You can remove the #Path annotations on the implementation class and concrete method, and only annotate your interfaces, like this:
public class AuthorizationRestService implements AuthorizationService {
#Override
public Response webserviceTest(){
TestDTO x = new TestDTO();
x.setFieldOne("ffff");
x.setFieldTwo("gggg");
Response res = Response.ok(x).build();
return res;
}
}
Note: don't forget #Produces on your interface method to define your MIME type, such as MediaType.APPLICATION_XML
#Path("/authorizationCheck")
public interface AuthorizationService {
#POST
#Path("/webserviceTest")
#Produces(MediaType.APPLICATION_XML)
public Response webserviceTest();
}
See an example here: http://pic.dhe.ibm.com/infocenter/initiate/v9r5/index.jsp?topic=%2Fcom.ibm.composer.doc%2Ftopics%2Fr_composer_extending_services_creating_rest_service_rest_interface.html
and here: http://pic.dhe.ibm.com/infocenter/initiate/v9r5/index.jsp?topic=%2Fcom.ibm.composer.doc%2Ftopics%2Fr_composer_extending_services_creating_rest_service_rest_interface.html
I changed like this
#Path("/authorizationCheck")
public class AuthorizationRestService implements AuthorizationService {
#Override
#Path("/webserviceTest")
#POST
#Produces(MediaType.APPLICATION_XML)
public Response webserviceTest(){
TestDTO x = new TestDTO();
x.setFieldOne("ffff");
x.setFieldTwo("gggg");
Response res = Response.ok(x).build();
return res;
}
}
My Test Client is different
public class CustomerResourceTest
{
#Test
public void testCustomerResource() throws Exception
{
URL postUrl = new URL("http://localhost:9095/authorizationCheck/webserviceTest");
HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
connection.setRequestMethod("POST");
System.out.println("Content-Type: " + connection.getContentType());
BufferedReader reader = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
while (line != null)
{
System.out.println(line);
line = reader.readLine();
}
Assert.assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
connection.disconnect();
return;
}
}
It produced output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><testDTO><fieldOne>ffff</fieldOne><fieldTwo>gggg</fieldTwo></testDTO>
Had to add following dependency also
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.2.0.GA</version>
</dependency>
Tried your code.
public void testCustomerResource() throws Exception
{
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
AuthorizationService proxy =
ProxyFactory.create(AuthorizationService.class,"http://localhost:9095/");
ClientResponse response = (ClientResponse) proxy.webserviceTest();
String str = (String)response.getEntity(String.class);
System.out.println(str);
return;
}
Produced same output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><testDTO><fieldOne>ffff</fieldOne><fieldTwo>gggg</fieldTwo></testDTO>
Note how I created proxy. I had only base url **http://localhost:9095/**. I did not mention resources authorizationCheck/webserviceTest in that. This is different from how you coded.