I am using servlets 3.0, jersey 2, and NOT Spring.
Here's my endpoint:
#Path("/foo")
#Produces(MediaType.TEXT_PLAIN)
public class Foo {
#GET
#Path("/bar")
public String bar() {
return "hello world"
}
}
Here's my pointcut:
pointcut endPoints(): execution (#javax.ws.rs.GET * *.*(..));
How do I get HttpServletRequest, HttpServletResponse, etc. from within my advice without changing bar's signature and without using Spring?
Regards,
Shackman
Related
I am trying to write a Java web application that provides both HTML and REST interface. I would like to create a servlet that would provide the HTML interface using JSP, but data should also be accessible via REST.
What I already have is something like this for the REST:
#javax.ws.rs.Path("/api/")
public class RestAPI {
... // Some methods
}
and
#WebServlet("/servlet")
public class MyServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Howdy at ");
}
}
Now, when I change the #WebServlet("/servlet") annotation to #WebServlet("/"), the servlet stops working probably due to path clash with the REST.
How can I provide REST on specific path and HTML in the root?
Thank you,
Lukas Jendele
This seems to work OK for me. What I did:
In my pom.xml, I have a dependency on org.wildfly.swarm:undertow (for Servlet API) and org.wildfly.swarm:jaxrs (for JAX-RS). And of course the Swarm Maven plugin.
For servlet, I have just this one class:
#WebServlet("/")
public class HelloServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello from servlet");
}
}
For JAX-RS, I have these 2 classes:
#ApplicationPath("/api")
public class RestApplication extends Application {
}
#Path("/")
public class HelloResource {
#GET
public Response get() {
return Response.ok().entity("Hello from API").build();
}
}
To test, I run curl http://localhost:8080/ and curl http://localhost:8080/api. Results are as expected. (Maybe my example is too simple?)
We would usually define POST and PUT verbs as different service APIs.
#POST
#Path("/getbook")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket() {
... }
#PUT
#Path("/getbook/{name}")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket(#PathParam("name") String name) {
... }
Would there be a way to combine these verbs into a single method - and then drive different logic based on the type of the verb ?
Hypothetically
#POST
#PUT
#Path("/getbook/{name}")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket(#PathParam("name") String name) {
if(verb=POST){
... }
else{
}
}
You may try like this using MessageContext. You need the context injected into the service method like below for updateCustomer method and then you can check for the method type as you like (here I am checking for PUT):
#Path("/customer")
public class CustomerService {
#Context
private org.apache.cxf.jaxrs.ext.MessageContext mc;
#PUT
public Response updateCustomer(#Context MessageContext context, Customer c) {
HttpServletRequest request = context.getHttpServletRequest();
boolean isPut = "PUT".equals(request.getMethod());
}
}
In my app I'm using MOXy JAXB with JAX-RS (Jersey) on Glassfish server,
I have the following REST webservice:
#Named
#RequestScoped
#Path("/product")
public class ProductService extends BaseServiceFacade<Product, Integer, ProductVO> {
#EJB(mappedName="java:global/myAppEAR/myAppEJB/ProductServiceRest")
ProductServiceRestRemote productServiceRestRemote;
// ...
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/featuredlists")
public List<List<ProductVO>> featuredlists() {
return productServiceRestRemote.featuredlists();
}
}
When I try to test the REST service accessing:
localhost:8080/atlanteusPortal/rest/product/featuredlists
I get:
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.getDomainClass(MOXyJsonProvider.java:267)
If I put a debug breakpoint before the method return I can see that the List<List<ProductVO>> chunkList is populated but it's not transformed into JSON
Can someone point out a solution to send a List<List<Object>> type via JSON using JAX-RS MOXy and Jersey?
I solved the issue using a workaround encapsulating List of Lists inside an object
called ProductListVO:
#XmlRootElement
public class ProductListVO extends BaseVO<String> {
private List<ProductVO> productVOs;
public List<ProductVO> getProductVOs() {
return productVOs;
}
public void setProductVOs(List<ProductVO> productVOs) {
this.productVOs = productVOs;
}
public static ProductListVO buildVO(List<Product> t) {
ProductListVO vo = new ProductListVO();
List<ProductVO> prodVOs = new ArrayList<ProductVO>();
StringBuilder sb = new StringBuilder();
for (Product product : t) {
sb.append(product.getId()).append('-');
prodVOs.add(ProductVO.buildVO(product));
}
vo.setId(sb.substring(0, sb.length() - 1));
vo.setProductVOs(prodVOs);
return vo;
}
}
in Service method:
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/featured")
public List<ProductListVO> featuredlists() {
return productServiceRestRemote.featuredLists();
}
I have an #AroundInvoke REST Web Service interceptor that I would like to use for logging common data such as the class and method, the remote IP address and the response time.
Getting the class and method name is simple using the InvocationContext, and the remote IP is available via the HttpServletRequest, as long as the Rest Service being intercepted includes a #Context HttpServletRequest in its parameter list.
However some REST methods do not have a HttpServletRequest in their parameters, and I can not figure out how to get a HttpServletRequest object in these cases.
For example, the following REST web service does not have the #Context HttpServletRequest parameter
#Inject
#Default
private MemberManager memberManager;
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member) throws MemberInvalidException {
return memberManager.add(member);
}
I have tried injecting it directly into my Interceptor, but (on JBoss 6.1) it is always null...
public class RestLoggedInterceptorImpl implements Serializable {
#Context
HttpServletRequest req;
#AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
logger.info(req.getRemoteAddr()); // <- this throws NPE as req is always null
...
return ic.proceed();
I would like advice of a reliable way to access the HttpServletRequest object - or even just the Http Headers ... regardless of whether a REST service includes the parameter.
After researching the Interceptor Lifecycle in the Javadoc http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html I don't think its possible to access any servlet context information other than that in InvocationContext (which is defined by the parameters in the underlying REST definition.) This is because the Interceptor instance has the same lifecycle as the underlying bean, and the Servlet Request #Context must be injected into a method rather than the instance. However the Interceptor containing #AroundInvoke will not deploy if there is anything other than InvocationContext in the method signature; it does not accept additional #Context parameters.
So the only answer I can come up with to allow an Interceptor to obtain the HttpServletRequest is to modify the underlying REST method definitons to include a #Context HttpServletRequest parameter (and HttpServletResponse if required).
#Inject
#Default
private MemberManager memberManager;
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member, #Context HttpServletRequest request, #Context HttpServletResponse response) throws MemberInvalidException {
...
}
The interceptor can then iterate through the parameters in the InvocationContext to obtain the HttpServletRequest
#AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
HttpServletRequest req = getHttpServletRequest(ic);
...
return ic.proceed();
}
private HttpServletRequest getHttpServletRequest(InvocationContext ic) {
for (Object parameter : ic.getParameters()) {
if (parameter instanceof HttpServletRequest) {
return (HttpServletRequest) parameter;
}
}
// ... handle no HttpRequest object.. e.g. log an error, throw an Exception or whatever
Another work around to avoid creating additional parameters in every REST method is creating a super class for all REST services that use that kind of interceptors:
public abstract class RestService {
#Context
private HttpServletRequest httpRequest;
// Add here any other #Context fields & associated getters
public HttpServletRequest getHttpRequest() {
return httpRequest;
}
}
So the original REST service can extend it without alter any method signature:
public class AddService extends RestService{
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member) throws MemberInvalidException {
return memberManager.add(member);
}
...
}
And finally in the interceptor to recover the httpRequest:
public class RestLoggedInterceptorImpl implements Serializable {
#AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
// Recover the context field(s) from superclass:
HttpServletRequest req = ((RestService) ctx.getTarget()).getHttpRequest();
logger.info(req.getRemoteAddr()); // <- this will work now
...
return ic.proceed();
}
...
}
I'm using Glassfish 3.1.2.2 Jersey
For http header this works for me:
#Inject
#HeaderParam("Accept")
private String acceptHeader;
To get UriInfo you can do this:
#Inject
#Context
private UriInfo uriInfo;
I have a pre-existing web service "connect" (SOAP) I would like to call without using the Swing framework if possible. I have followed the contact first development generating my java files using cxf/wsdl2java tool.
I wish for the userName and password to be extracted from the java object and placed in a SOAP object then sent onot my localhost web service.
When sending the Connect object as a body to the "direct:start" I get an exception...
Caused by: java.lang.IllegalArgumentException: Get the wrong parameter size to invoke the out service, Expect size 2, Parameter size 1. Please check if the message body matches the CXFEndpoint POJO Dataformat request.
I've checked the first argument is actually an instance of the Connect object passed in.
Do I need some additional annotations in the one of the classes, is the method of testing invalid or
is there an alternative pattern I should follow?
public class TestConnectCXF extends CamelTestSupport
{
#Override
protected RouteBuilder createRouteBuilder() throws Exception
{
return new RouteBuilder()
{
String cxfAddressLine = "cxf:http://localhost:8081/nuxeo/webservices/privateadservice?wsdlURL=wsdl/privateadservice.wsdl" //
+ "&dataFormat=POJO" //
+ "&serviceClass=com.sandbox.camelfeed.PrivateAdServiceInterface" //
+ "&serviceName={http://ws.sandboxtest.com/}PrivateAdService" //
+ "&synchronous=true" //
+ "&loggingFeatureEnabled=true" //
+ "&portName={http://ws.sandboxtest.com/}PrivateAdServiceInterfacePort";
#Override
public void configure() throws Exception
{
from("direct:start").to(cxfAddressLine).to("mock:end");
}
};
}
#Test
public void testConnectViaPojo() throws InterruptedException
{
Connect connectToServer = new Connect();
connectToServer.setUserName("FakeUser");
connectToServer.setPassword("scrubbed");
template.sendBody("direct:start", connectToServer);
Thread.sleep(1000);
}
}
I'm new to camel and web services so any helpful pointers would be greatly appreciated.
Additional info
Using camel 2.10, Java 1.6
Classes generated from wsdl2java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "connect", propOrder = {
"userName",
"password"
})
public class Connect {
protected String userName;
protected String password;
public String getUserName() {
return userName;
}
public void setUserName(String value) {
this.userName = value;
}
public String getPassword() {
return password;
}
public void setPassword(String value) {
this.password = value;
}
}
#WebService(targetNamespace = "http://ws.sandboxtest.com/", name = "PrivateAdServiceInterface")
#XmlSeeAlso({ObjectFactory.class})
public interface PrivateAdServiceInterface {
// Omitted Code relating to other web calls
#WebResult(name = "return", targetNamespace = "")
#RequestWrapper(localName = "connect", targetNamespace = "http://ws.sandboxtest.com/", className = "com.sandbox.camelfeed.Connect")
#WebMethod
#ResponseWrapper(localName = "connectResponse", targetNamespace = "http://ws.sandboxtest.com/", className = "com.sandbox.camelfeed.ConnectResponse")
public java.lang.String connect(
#WebParam(name = "userName", targetNamespace = "")
java.lang.String userName,
#WebParam(name = "password", targetNamespace = "")
java.lang.String password
) throws ClientException_Exception;
}
#XmlRegistry
public class ObjectFactory {
{
// Omitted other web calls information
private final static QName _Connect_QNAME = new QName("http://ws.sandboxtest.com/", "connect");
#XmlElementDecl(namespace = "http://ws.sandboxtest.com/", name = "connect")
public JAXBElement<Connect> createConnect(Connect value) {
return new JAXBElement<Connect>(_Connect_QNAME, Connect.class, null, value);
}
}
In my experience there are some things in Camel, like calling a SOAP Web service or making a REST call, that are easier to do in a custom processor, than using a component like CXF, HTTP or HTTP4.
I usually work with Spring, so I tend to use either Spring REST template or the JaxWsPortProxyFactoryBean (for Webservice calls) for outbound calls.
Here is an example using a JAX-WS call:
public class WebServiceProcessorBean {
#Autowired
private JAXWSProxy theProxy;
public void callWebservice(Exchange exchange) {
Response response = theProxy.call();
//Do something with the response and Exchange.
}
}
The definition in the Spring application context:
<bean id="theProxyService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="XXX"/>
<property name="wsdlDocumentUrl" value="http://xxxxx.wsdl"/>
<property name="namespaceUri" value="xxxx"/>
<property name="serviceName" value="xxxx"/>
<property name="portName" value="xxxxx"/>
</bean>
Use the WebServiceProcessorBean defined in Spring application context with beanRef() DSL method.
.beanRef("theProxyService", "callWebservice")
I try also dig into Apache Camel with CXF:).
I think exception, which is throws, is about problem with number of argument.
template.sendBody(what_is_called, input_parameter_s, output_parameter);
Output parameter is the most probably return value from calling of cxf webservices.