Upgrading from CXF 2.7 to CXF 3.0 - java

I am trying to upgrading one of the code bases to CXF 3.0 and some of the classes are deprecated and trying to upgrade the dependencies.
#Override
public Response handleResponse(Message m, OperationResourceInfo ori,
Response response) {
if (response.getStatus() == Response.Status.OK.getStatusCode()){
if (response.getHeaderString("my_header") == null){
Message inMessage = m.getExchange().getInMessage();
String headerKey = getMyHeaderKey(inMessage);
if (headerKey != null){
AbstractResponse entityResponse = (AbstractResponse) response.getEntity();
response = generateResponse(entityResponse, inMessage, false);
}
}
}
return response;
}
private Response generateResponse(AbstractResponse ar, Message msg, boolean isConflict){
ResponseBuilder responseBldr;
if (isConflict){
responseBldr = Response.status(Status.CONFLICT);
}
else{
responseBldr = Response.ok(ar);
}
responseBldr.header("header1", "true");
HttpServletRequest request = (HttpServletRequest) msg.get(AbstractHTTPDestination.HTTP_REQUEST);
String retryId = request.getHeader("header2");
if (retryId != null){
responseBldr.header("header2", retryId);
}
return responseBldr.build();
}
I tried to use ContainerRequest/Response filters, but couldn't find how can I set the response
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
Message m = JAXRSUtils.getCurrentMessage();
containerResponseContext.getStatus();
if (containerResponseContext.getStatus() == Response.Status.OK.getStatusCode()){
if (containerResponseContext.getHeaderString("my_header") == null){
Message inMessage = m.getExchange().getInMessage();
String headerKey = getMyHeaderKey(inMessage);
if (headerKey != null){
AbstractResponse entityResponse = (AbstractResponse) containerResponseContext.getEntity();
response = generateResponse(entityResponse, inMessage, false); //how do I do this with CXF 3.0?
}
}
}
}

Please refer JAX-RS 2.0 Filter here which says new filters ContainerRequestFilter and ContainerResponseFilter have been introduced. Here is an example below which in short does all the actions you were trying to achieve.
public class KPFilter implements ContainerResponseFilter {
private Logger LOG = LoggerFactory.getLogger(KPFilter.class);
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if(responseContext.getStatus()== Status.OK.getStatusCode()){
final String myHeader = requestContext.getHeaderString("myHeader");
if(myHeader !=null && myHeader.equals("kp-header")){
responseContext.getHeaders().add("resHeader", myHeader+"-res");
}else{
responseContext.setEntity("An erro occured");
responseContext.setStatus(500);
}
}else{
LOG.info("Status is not OK, its {}", responseContext.getStatus());
}
}
}
And the cxf configuration file
<jaxrs:providers>
<bean class="com.xxxxx.xxxxx.KPFilter" />
</jaxrs:providers>

Related

Authorize requests using tokens stored in database

I am try to implement authentication filter in spring project. I have tokens stored for customers in database. Currently the authorization happens separately in each API.
Here is the current version of the api:
#CrossOrigin
#RequestMapping(value="/thirdParty", method=RequestMethod.GET)
private void getThirdPartyOffers(HttpServletRequest request, HttpServletResponse response){
response.setHeader("Access-Control-Allow-Origin", "*");
String authToken = request.getHeader(AUTHORIZATION);
if (null != authToken) {
Customer customer = getCustomer(authToken, sessionFactory);
if (null != customer) {
JsonObject responseObject = new JsonObject();
Integer cityId=-1;
if(customer.getDeliveryAddress()!=null &&customer.getDeliveryAddress().getCity()!=null){
cityId=customer.getDeliveryAddress().getCity().getId();
}else if(customer.getLocality()!=null){
cityId= customer.getLocality().getCity().getId();
}else if(customer.getArea()!=null){
cityId= customer.getArea().getCity().getId();
}
JsonArray offers = promotionOfferUtils.getThirdPartyOfferList(customer, cityId);
responseObject.add("offers", offers);
responseObject.addProperty(ERROR, false);
sendResponse(response,HttpServletResponse.SC_OK,responseObject);
return;
}else{
sendResponse(response, HttpServletResponse.SC_UNAUTHORIZED, ERROR,
AUTHORIZATION_FAILED);
}
}else {
sendResponse(response, HttpServletResponse.SC_UNAUTHORIZED, ERROR,
AUTHORIZATION_FAILED);
}
}
The final api should be like this:
#CrossOrigin
#RequestMapping(value = "/thirdParty", method = RequestMethod.GET)
private void getThirdPartyOffers(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
String customerId = response.getHeader("customerId");
Customer customer = getCustomer(sessionFactory, customerId);
JsonObject responseObject = new JsonObject();
Integer cityId = -1;
if (customer.getDeliveryAddress() != null && customer.getDeliveryAddress().getCity() != null) {
cityId = customer.getDeliveryAddress().getCity().getId();
} else if (customer.getLocality() != null) {
cityId = customer.getLocality().getCity().getId();
} else if (customer.getArea() != null) {
cityId = customer.getArea().getCity().getId();
}
JsonArray offers = promotionOfferUtils.getThirdPartyOfferList(customer, cityId);
responseObject.add("offers", offers);
responseObject.addProperty(ERROR, false);
sendResponse(response, HttpServletResponse.SC_OK, responseObject);
return;
}
I am now trying to authenticate using a filter.Here I have created the required filter.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// invoked when a matching request sent to the server
// used to intercept the request and transform the response
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Headers", "origin, content-type, Authorization, accept, x-requested-with, IS_UPDATED");
httpResponse.setHeader("Access-Control-Max-Age", "60"); // seconds to cache preflight request --> less OPTIONS traffic
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
Customer customer;
try
{
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
} else {
String url = ((HttpServletRequest) request).getRequestURL().toString();
String endUrlPath = url.split("/").length > 1 ? url.split("/")[url.split("/").length - 1] : "";
if (!endUrlPath.equalsIgnoreCase("login") && !endUrlPath.equalsIgnoreCase("forgotPasswordSendOtp") &&
!endUrlPath.equalsIgnoreCase("changePasswordInForgotPassword") && !endUrlPath.equalsIgnoreCase("verifyUserOTP")) {
String authToken = httpRequest.getHeader("Authorization");
customer = getCustomer(authToken,sessionFactory);
if (customer == null) {
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
httpResponse.addHeader("customerId", Integer.toString(customer.getId()));
}
}
} catch (Exception ex) {
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
chain.doFilter(request, response);// invokes next filter in the chain
}
The problem now is that the sessionFactory object is null at this point and I am unable to access database to get the customer.
I am unable to figure out how to get access to sessionFactory when the startup is incomplete?

How to implement custom error handling with retroift2

I calling to the api with the basic retrofit Call object:
public interface dataApi {
#GET("animal/cats")
Call<AllAnimals> getAllData(
#Query("api_key") String apiKey
);
}
And I can get the response inside my view model like this:
call.enqueue(new Callback<AllAnimals>() {
#Override
public void onResponse(Call<AllAnimals> call, Response<AllAnimals> response) {
animals.setValue(response.body());
}
#Override
public void onFailure(Call<AllAnimals> call, Throwable t) {
Log.i(TAG, "onFailure: " + t);
}
});
Nothing speical here.
I've several problem with this approach
FIRST - if I give the wrong api key for example, the response should give me a response with the code of the problem, instead I just get null body.
SECOND I am planning to have more api calls, and it's a huge code duplication to handle errors every call I wrote.
How can I implement custom error handling for this situation, that will be apply to other calls too?
I think you can use okhttp interceptor and define yourself ResponseBody converter to fix your problem.
First,intercept you interested request and response;
Second,check the response,if response is failed then modify the response body to empty。
define a simple interceptor
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
System.out.println(request.url());
okhttp3.Response response = chain.proceed(request);
if (!response.isSuccessful() && url.contains("animal/cats")) {
// request failed begin to modify response body
response = response.newBuilder()
.body(ResponseBody.create(MediaType.parse("application/json"), new byte[] {}))
.build();
}
return response;
}
};
define self ResponseBody converter
most code from com.squareup.retrofit2:converter-jackson we just add two lines:
final class JacksonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final ObjectReader adapter;
JacksonResponseBodyConverter(ObjectReader adapter) {
this.adapter = adapter;
}
#Override public T convert(ResponseBody value) throws IOException {
try {
if (value.contentLength() == 0) {
return null;
}
return adapter.readValue(value.charStream());
} finally {
value.close();
}
}
}
the below code is added:
if (value.contentLength() == 0) {
return null;
}

Netflix's Zuul Error filter implementation

What would be an example implementation of an error filter for netflix's zuul gateway service? All the examples I have found have been either incorrect or too old to work.
You can create your own custom filter and put that filter to be executed right before Zuul's default SendErrorFilter, but sometimes the default filter will override your body or HTTP error.
I prefer to disable the default filter by putting this in application properties:
zuul.SendErrorFilter.error.disable=true
Then create your own CustomSendErrorFilter by extending the default one.
An example implementation would be:
#Component
public class SendErrorCustomFilter extends SendErrorFilter {
private static final Logger LOG = LoggerFactory.getLogger(SendErrorCustomFilter.class);
private static final String SERVLET_ERROR_STATUS_CODE = "javax.servlet.error.status_code";
private static final String SERVLET_ERROR_EXCEPTION = "javax.servlet.error.exception";
private static final String SERVLET_ERROR_MESSAGE = "javax.servlet.error.message";
#Value("${error.path:/error}")
private String errorPath;
#Override
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
ExceptionHolder exception = findZuulException(ctx.getThrowable());
HttpServletRequest request = ctx.getRequest();
Throwable cause = exception.getThrowable().getCause();
int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
if (causeIsIOError(cause)) {
statusCode = HttpServletResponse.SC_CONFLICT;
} else if (causeIsAuthorizationError(cause)) {
statusCode = HttpServletResponse.SC_UNAUTHORIZED;
}
request.setAttribute(SERVLET_ERROR_STATUS_CODE, statusCode);
LOG.warn("Error during filtering", cause);
request.setAttribute(SERVLET_ERROR_EXCEPTION, cause);
if (StringUtils.hasText(exception.getErrorCause())) {
request.setAttribute(SERVLET_ERROR_MESSAGE, cause.getMessage());
}
RequestDispatcher dispatcher = request.getRequestDispatcher(this.errorPath);
if (dispatcher != null) {
ctx.set(SEND_ERROR_FILTER_RAN, true);
if (!ctx.getResponse().isCommitted()) {
ctx.setResponseStatusCode(exception.getStatusCode());
dispatcher.forward(request, ctx.getResponse());
}
}
} catch (Exception ex) {
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
private boolean causeIsIOError(Throwable cause) {
return cause instanceof InvalidTokenPayloadException
|| cause instanceof InvalidResponseBodyException;
}
public boolean causeIsAuthorizationError(Throwable cause) {
return cause instanceof InvalidJWTTokenException ||
cause instanceof NoPermissionForResourceException ||
cause instanceof MissingAuthorizationHeaderException;
}
This way you have all the control for the error that is going to be sent back to the client. I have extracted a few methods that check for different kinds of Exceptions and put different HTTP errors depending on these Exceptions.
I am using the cause of the exception because these exceptions are wrapped inside a ZuulException.

How to continue request processing after sending response from filter in Jersey?

I have a situation where I need to return an "accepted" response for every request received and publish the actual response later to a separate endpoint outside the service.
To implement the 'accepted' Response I implemented a filter.
public class AcknowledgementFilter implements ContainerRequestFilter{
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
containerRequestContext.abortWith(Response.accepted().build());
// call Resource method in new Thread() . <------ ?
}
}
Implementation of service endpoints:
#Path("/vendor")
public class VendorHandler {
#POST
public void addVendor(VendorRequest addVendorRequest)){
vendor = new Vendor();
Client.publish(vendor); // publish request to an endpoint
return null;
}
How do I call the addVendor of VendorHandler(or any method depends on request) from the acknowledgement filter?
Is there any other way to implement an accepted response for every request then process the request separately?
You can use AsyncResponse,
#GET
#ManagedAsync
#Produces(MediaType.APPLICATION_JSON)
public void getLives(#Suspended final AsyncResponse asyncResponse,
#DefaultValue("0") #QueryParam("newestid") final int newestId,
#QueryParam("oldestid") final Integer oldestId) {
asyncResponse.setTimeoutHandler(asyncResponse1 -> {
logger.info("reached timeout");
asyncResponse1.resume(Response.ok().build());
});
asyncResponse.setTimeout(5, TimeUnit.MINUTES);
try {
List<Life> lives = oldestId == null ?
Lifes.getLastLives(newestId) : Lifes.getOlderLives(oldestId);
if (lives.size() > 0) {
final GenericEntity<List<Life>> entity = new GenericEntity<List<Life>>(lives) {
};
asyncResponse.resume(entity);
} else LifeProvider.suspend(asyncResponse);
} catch (SQLException e) {
logger.error(e, e);
asyncResponse.resume(new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR));
}
}
Check this Link for more details.

Requests and response from netty server hangs

I have the following code to create a netty web server based on http server created in the netty's example. My buisness logic is the following.
public class HttpServerHandler extends SimpleChannelInboundHandler<Object> {
private final static Logger LOG = LogManager
.getLogger(HttpServerHandler.class);
private WorkflowService workflowService;
private HttpRequest request;
private final StringBuffer buff = new StringBuffer();
private API avalancheApi;
public HttpServerHandler(WorkflowService workflowService) {
this.workflowService = workflowService;
this.avalancheApi = new API(this.workflowService);
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOG.debug("channelActive");
LOG.debug(ctx.toString());
};
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg)
throws IOException {
avalancheApi.setContext(ctx);
if (msg instanceof HttpRequest) {
HttpRequest request = this.request = (HttpRequest) msg;
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx);
}
String command = getCommand(request);
LOG.debug(command);
Map<String, List<String>> parameters = getParameters(request);
LOG.debug(parameters);
switch (command) {
case "/login":
ctx = avalancheApi.login(parameters);
break;
case "/test":
ctx = avalancheApi.test();
break;
default:
break;
}
}
if (msg instanceof LastHttpContent) {
LOG.debug("msg is of LastHttpContent");
}
if (!HttpHeaders.isKeepAlive(request)) {
// If keep-alive is off, close the connection once the content is
// fully written.
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(
ChannelFutureListener.CLOSE);
}
}
public class API {
private static final Logger LOG = LogManager.getLogger(API.class);
private ChannelHandlerContext ctx;
private HttpResponse response;
private WorkflowService workflowService;
public API(WorkflowService workflowService) {
this.workflowService = workflowService;
this.ctx = null;
}
public void setContext(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
public ChannelHandlerContext login(Map<String, List<String>> parameters) {
boolean success;
String username = getUsername(parameters);
String password = getPassword(parameters);
User user = null;
user = workflowService.login(username, password);
success = validateLogin(user);
this.response = writeLoginResponse(success);
this.ctx.write(this.response);
writeLoginContext(success, response);
return this.ctx;
}
private void writeLoginContext(boolean success, HttpResponse response) {
JsonObject jsonResponseMessage = new JsonObject();
jsonResponseMessage.addProperty("result", success);
LOG.debug(jsonResponseMessage.toString());
this.ctx.write(Unpooled.copiedBuffer(jsonResponseMessage.toString(),
CharsetUtil.UTF_8));
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
}
private HttpResponse writeLoginResponse(boolean success) {
if (success)
return createSuccessfullLoginResponse();
else
return createLoginFailureResponse();
}
private HttpResponse createLoginFailureResponse() {
return Response.loginFailureResponse();
}
private HttpResponse createSuccessfullLoginResponse() {
return Response.loginSuccessResponse();
}
}
Response class is only creating the response and the content_type which is of application/json. Content Length is set in the API class. Using python client with requests, results in the request made in http://localhost/login?username=name&password=pass works only once. The second time everything works, but it doesn't finish processing the request and send the response object. Api calls get executed normally, and I also get the message of LastHttpContext message getting print. The problem sometimes happens with browser too. Am I missing something? Maybe the content data and the content length doesn't match? Could it be that when making requests from python client, the content of the previous context isn't flushed and the content_length value of the header and content length of the context doesn't match?
Just wild guess
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
Instead, shouldn't you be doing jsonResponseMessage.toString().getBytes().length ?? Sometimes, one character is not just one byte.
My guess is that you have overwritten the context in your API class, and as a result, are writing the response to the wrong context. Is your HttpServerHandler marked with #Shareable?

Categories