In wildfly 8.1 with REST services, I wanted to implement CORS ContainerRequestFilter and ContainerResponseFilter.
My request filter is working properly but ContainerResponseFilter never gets loaded nor called
package org.test.rest;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
#Provider
#PreMatching // <-- EDIT : This was my mistake ! DO NOT ADD THIS
public class CorsResponseFilter implements ContainerResponseFilter {
public CorsResponseFilter() {
System.out.println("CorsResponseFilter.init");
}
#Override
public void filter(ContainerRequestContext req,
ContainerResponseContext resp) throws IOException {
System.out.println("CorsResponseFilter.filter");
resp.getHeaders().add("Access-Control-Allow-Origin", "*");
resp.getHeaders().add("Access-Control-Allow-Credentials", "true");
resp.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, DELETE, PUT");
resp.getHeaders().add("Access-Control-Allow-Headers",
"Content-Type, Accept");
}
}
This seems to me as a Wildfly / resteasy bug. Do you have another idea / am I missing something ?
You are mixing ContainerRequestFilter and ContainerResponseFilter in your question. As you want to send additional Headers to the client the ContainerResponseFilter is the right one.
The #PreMatching annotation can be applied to a ContainerRequestFilter "to indicate that such filter should be applied globally on all resources in the application before the actual resource matching occurs".
Adding it to a ContainerResponseFilter does not make sense. Just remove the annotation and your filter should work.
Related
I have just started working around the Apache Camel. I have a requirement to implement an FTP/FTPS/SFTP client, which would be used to fetch the files from the respective servers. I was looking into the possibility of using Apache Camel to do this but I am still confused after going through the examples and the tutorials.
The requirement is to fetch the files from the FTP/SFTP servers when the request is received from the scheduler.
Following is the route created using EndPoint-DSL
#Component
public class FtpReceiveRoute extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(
ftp("localhost:2001/home/admin")
.account("admin")
.password("admin12345")
.recursive(true)
)
.routeId("ftpReceive")
.log("From done!")
.to("log:ftp-log")
.log("To done!!");
}
}
I am trying to use the above route by invoking it when the request is made to fetch the file like below.
#Override
protected FtpResponse doMessage(String param, FtpRequest req) {
FtpResponse response = new FtpResponse ();
CamelContext ctx = new DefaultCamelContext();
ctx.addRoutes(##route); //FtpReceiveRoute, add the Routebuilder instance as EndpointRouteBuilder is acceptable.
ctx.start();
//Might need to induce sleep so that all the files are downloaded
ctx.stop();
return response;
}
The confusion is around how to invoke the Camel process with the route. I have used EndpointRouteBuilder to create the route because of the type-safe creation of the endpoint URI. I am not getting an option to add this route to the CamelContext as it expects the RouteBuilder instance which is not type-safe.
Further, the CamelContext is the engine and to invoke the route I would need to start and stop this engine. This I am not able to digest if I need to start and stop the engine to execute a route then I would need to induce some sleep in between so that all files are downloaded. Just to add there are more routes that I need to add with the implementation. Once the engine is started it would load and execute all the added routes which is not the requirement.
Maybe I am not getting how to use this properly. Any resources aiding my situation are welcome. Thanks.
You should not create and start new camel context every time you want to fetch file from server. What you should do instead is start one when your application starts and use that for all your exchanges.
You can use Spring-boot to initialize CamelContext and add annotated RouteBuilders to it automatically. Check the maven archetype camel-archetype-spring-boot for example.
If you want to call camel routes from Java you can Inject CamelContext to your bean and use it to create ProducerTemplate. This can be used to invoke Routes defined in the RouteBuilder.
Using ProducerTemplate.send you can get the resulting exchange.
Using producer template
Using File-component which works very similary to ftp-component.
package com.example;
import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class MySpringBootRouter extends EndpointRouteBuilder {
#Override
public void configure() {
from(direct("fileFromFTP"))
.routeId("fileFromFTP")
// reads files from <project>/input using file consumer endpoint
.pollEnrich(file("input"), 1000)
// If file is found, convert body to string.
// Which in this case will read contents of the file to string.
.filter(body().isNotNull())
.convertBodyTo(String.class)
.end()
;
}
}
package com.example;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.support.DefaultExchange;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import static org.apache.camel.builder.endpoint.StaticEndpointBuilders.direct;
#Configuration
#EnableScheduling
public class MySpringBean {
#Autowired
CamelContext camelContext;
#Scheduled(fixedRate = 1000)
public void scheduledTask() {
System.out.println("Scheduled Task!");
if(camelContext.isStopped()) {
System.out.println("Camel context not ready yet!");
return;
}
useProducerTemplate();
}
public void useProducerTemplate(){
ProducerTemplate producerTemplate = camelContext.createProducerTemplate();
Exchange inExchange = new DefaultExchange(camelContext);
//synchronous call!
Exchange result = producerTemplate.send(direct("fileFromFTP").toString(), inExchange);
String resultBody = result.getMessage().getBody(String.class);
String fileName = result.getMessage().getHeader(Exchange.FILE_NAME, String.class);
if(resultBody != null){
System.out.println("Consumed file: "+ fileName + " contents: " + resultBody.toString());
}
else{
System.out.println("No file to consume!");
}
}
}
Depending on what you need to do with the files you could probably do that inside camel route. Then you would only need to call the producerTemplate.sendBody.
public void useProducerTemplate(){
ProducerTemplate producerTemplate = camelContext.createProducerTemplate();
Exchange inExchange = new DefaultExchange(camelContext);
producerTemplate.sendBody(direct("fileFromFTP").toString(), inExchange);
}
Starting stopping camel route
If you want to start polling file consumer only for a short while you can do start the route and use for example aggregation timeout to shutdown the route when no new files have been received in any given duration.
#Component
public class MySpringBootRouter extends EndpointRouteBuilder {
#Override
public void configure() {
AggregationStrategy aggregateFileNamesStrategy = AggregationStrategies
.flexible(String.class)
.accumulateInCollection(ArrayList.class)
.pick(header(Exchange.FILE_NAME))
;
from(file("input"))
.routeId("moveFilesRoute")
.autoStartup(false)
.to(file("output"))
.to(seda("moveFilesRouteTimeout"));
;
from(seda("moveFilesRouteTimeout"))
.routeId("moveFilesRouteTimeout")
.aggregate(constant(true), aggregateFileNamesStrategy)
.completionTimeout(3000)
.log("Consumed files: ${body.toString()}")
.process(exchange -> {
exchange.getContext().getRouteController().stopRoute("moveFilesRoute");
})
.end()
;
}
}
public void startMoveFilesRoute() {
try {
System.out.println("Starting moveFilesRoute!");
camelContext.getRouteController().startRoute("moveFilesRoute");
//Sending null body moveFilesRouteTimeout to trigger timeout if there are no files to transfer
camelContext.createProducerTemplate().sendBody(seda("moveFilesRouteTimeout").toString(), null);
} catch(Exception e) {
System.out.println("failed to stop route. " + e);
}
}
I want to perform a post using netty4 in camel but its returning bad request 400, i have tried many permutations of http options to see if any work but the result is same (as consumer its working but as producer its not)
#Component
public class RestDslRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
restConfiguration()
.component("netty4-http")
.host("localhost").port("8686")
.enableCORS(true)
.corsHeaderProperty("Access-Control-Allow-Origin", "*")
.corsHeaderProperty("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
.bindingMode(RestBindingMode.json);
rest("/api")
.get("/")
.to("bean:helloBean")
.post().type(PostRequestType.class)
.to("bean:postBean");
/*from("netty4-http:http://localhost:8686/foo")
.transform().constant("Bye World");
*/
from("netty4-http:http://localhost:8686/foo")
.log("Logged id issue without body: ${header.id}")
.to("netty4-http:http://ptsv2.com/t/8wl8p-1547734804/post?bridgeEndpoint=true");
}
We have a SOAP web service that we are migrating from JBoss EAP 5.1 to 6.4.7 and one of the webservices returns absolutely nothing but 200 (in JBoss 5). When we migrated to 6 it still works and returns nothing but returns a 202 instead and that is going to break clients. We have no control over clients. I tried a SOAPHandler at the close method but it does nothing as it is not even called as my guess is that since there is no SOAP message going back there is nothing that triggers the handler.
I also tried to access the context directly in the web method and modif but it did nothing.
MessageContext ctx = wsContext.getMessageContext();
HttpServletResponse response = (HttpServletResponse) ctx.get(MessageContext.SERVLET_RESPONSE);
response.setStatus(HttpServletResponse.SC_OK);
I couldn't find anything in the manual.
Any direction is very much appreciated.
Here is how the port and its implementation look like:
Here is how the port and its implementation head look like:
#WebService(name = "ForecastServicePortType", targetNamespace = "http://www.company.com/forecastservice/wsdl")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
#XmlSeeAlso({
ObjectFactory.class
})
public interface ForecastServicePortType {
/**
*
* #param parameters
* #throws RemoteException
*/
#WebMethod(action = "http://www.company.com/forecast/sendForecast")
public void sendForecast(
#WebParam(name = "SendForecast", targetNamespace = "http://www.company.com/forecastservice", partName = "parameters")
SendForecastType parameters) throws RemoteException;
}
#WebService(name = "ForecastServicePortTypeImpl", serviceName = "ForecastServicePortType", endpointInterface = "com.company.forecastservice.wsdl.ForecastServicePortType", wsdlLocation = "/WEB-INF/wsdl/ForecastServicePortType.wsdl")
#HandlerChain(file = "/META-INF/handlers.xml")
public class ForecastServicePortTypeImpl implements ForecastServicePortType {
...
}
In case anybody will find this useful. Here is the solution;
Apache CXF by default uses async requests and even if the annotation #OneWay is missing it still behaves as it if was there.
So in order to disable that an interceptor needs to be created like below:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import java.util.Arrays;
public class DisableOneWayInterceptor extends AbstractSoapInterceptor {
private static final Log LOG = LogFactory.getLog(DisableOneWayInterceptor.class);
public DisableOneWayInterceptor(){
super(Phase.PRE_LOGICAL);
addBefore(Arrays.asList(org.apache.cxf.interceptor.OneWayProcessorInterceptor.class.getName()));
}
#Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
if(LOG.isDebugEnabled())
LOG.debug("OneWay behavior disabled");
soapMessage.getExchange().setOneWay(false);
}
}
And called in WebService class (annotated with #WebService) as below:
#org.apache.cxf.interceptor.InInterceptors (interceptors = {"com.mycompany.interceptors.DisableOneWayInterceptor" })
To retrieve some open data from a remote web server to process, I'm trying out Apache Camel.
The problem is that it seems that the data is never received. I have tried the jetty, ahc and cxf components but can't get it to work. For example like this:
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class CamelHttpDemo {
public static void main(final String... args) {
final CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
this.from("direct:start")
.to("ahc:http://camel.apache.org/")
.process(exchange -> {
System.out.println(exchange);
});
}
});
context.start();
Thread.sleep(10000);
context.stop();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
No output is written so the line System.out.println(exchange); is never executed and I assume the data is not retrieved.
I'm using the most recent version of Apache Camel, 2.17.1.
You need some message producer in your route to emit Exchange that would trigger the http component. Your route starts with direct:start which cannot emit new Exchanges, it just sits and waits for someone to initiate the process.
The easiest way to make your route work is to replace direct:start with some producer. For instance, replacing it with this timer .from("timer://foo?fixedRate=true&period=10000") will trigger your http-request once every 10 seconds.
If you want to initiate the request manually, you need to create a ProducerTemplate and use it to send a message to direct:start. That would be:
ProducerTemplate template = context.createProducerTemplate();
template.sendMessage("direct:start", "Message body");
I'm not able to find the right dependency to implement a REST client with tomEE and CXF.
My module has this dependency:
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>tomee-jaxrs</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
The initial client implementation is simple. It has to serve a post method and submit a MultiValueMap.
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.ext.form.Form;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RestClient<T> {
private static final Logger LOG = LoggerFactory.getLogger(RestClient.class);
private WebClient client;
private Class<T> type;
public RestClient(Class<T> aType, String aBaseUrl, String aPath) {
this.client = WebClient.create(aBaseUrl);
this.client.path(aPath);
this.client.accept(MediaType.APPLICATION_JSON);
this.type = aType;
}
public T post(MultivaluedMap<String, String> params) {
LOG.debug("sending POST request to: " + this.client.getCurrentURI());
Form theForm = new Form(params);
T theReponse = (T) this.client.post(theForm, this.type.getClass());
return theReponse;
}
}
The problem is that I cannot figure out an implementation of javax.ws.rs.core.MultivaluedMap. In result I cannot call my method. :(
I see only the interface. Isn't CXF completely provided by my pom.xml and doesn't it have an implementation of this interface? What dependency should I use to enable a proper work of CXF with tomEE?
I did not find any example on the web.
With cxf, the MultivaluedMap implementation is org.apache.cxf.jaxrs.impl.MetadataMap.
In newer JAX-RS 2.0 compliant versions, there is a javax.ws.rs.core.MultivaluedHashMap, but with JAX-RS 1.x, the implementation of the interface is implementation specific.