All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.process.internal.ResponseProcessor Maven / Gradle / Ivy

There is a newer version: 4.0.0-M1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2011-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package org.glassfish.jersey.process.internal;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.MappableException;
import org.glassfish.jersey.spi.ExceptionMappers;

import org.glassfish.hk2.Factory;

import com.google.common.base.Function;
import com.google.common.util.concurrent.SettableFuture;

/**
 * Processes result of the request transformation (successful or not). The response
 * processor also represents the ultimate future request-to-response transformation
 * result.
 * 

* A response processor is invoked when the request-to-response transformation * processing is finished. When invoked, the response processor retrieves the * transformation result. * * If the transformation was successful and a response instance * is returned, the response processor runs the response instance through the chain of * registered response filters and returns a result once finished. * * In case the request transformation finished with an exception, the response processor * tries to map the exception to a response using the registered {@link javax.ws.rs.ext.ExceptionMapper * exception mappers} and, if successful, runs the mapped response instance through * the chain of registered response filters and returns a result once finished. * In case the exception was not mapped to a response, the exception is presented * as the ultimate request-to-response transformation result. * * @param processed data type. * @author Marek Potociar (marek.potociar at oracle.com) */ public abstract class ResponseProcessor implements Runnable { private static final Logger LOGGER = Logger.getLogger(ResponseProcessor.class.getName()); /** * Injectable context that can be used during the data processing for * registering response processing functions that will be invoked during the * response processing. * * @param processed data type. */ public static interface RespondingContext { /** * Push response transformation function that should be applied. * * @param responseTransformation response transformation function. */ void push(Function responseTransformation); /** * Push chainable response transformation stage that should be applied. * * @param stage response transformation chainable stage. */ void push(ChainableStage stage); /** * (Optionally) create a responder chain from all transformations * previously pushed into the context. * * @return created responder chain root or {@code null} in case of no * registered transformations. */ Stage createResponderRoot(); } /** * Response processor factory. * * @param processed data type. */ public static interface Builder { /** * Create a new response processor for a given request / response message exchange. * * @param inflectedResponse inflected response data future. * @param processedResponse settable future that will be used to set the response * processing result. * @param callback the invocation callback to be invoked once the * response processing has finished. * @param scopeInstance the instance of the request scope this processor * belongs to. * @return new response processor instance. */ public ResponseProcessor build( final Future inflectedResponse, final SettableFuture processedResponse, final InvocationCallback callback, final RequestScope.Instance scopeInstance); } private final RequestScope requestScope; private volatile RequestScope.Instance scopeInstance; private final InvocationCallback callback; private final Future inflectedResponse; private final SettableFuture processedResponse; private final Factory> respondingCtxProvider; private final Factory exceptionMappersProvider; /** * Create new response processor for a given request / response message exchange. * * @param callback the invocation callback to be invoked once the * response processing has finished. * @param inflectedResponse inflected response data future. * @param processedResponse settable future that will be used to set the response * processing result. * @param respondingCtxProvider responding context provider. * @param scopeInstance the instance of the request scope this processor * belongs to. * @param requestScope Jersey request scope. * @param exceptionMappersProvider exception mappers provide. */ protected ResponseProcessor( final InvocationCallback callback, final Future inflectedResponse, SettableFuture processedResponse, final Factory> respondingCtxProvider, final RequestScope.Instance scopeInstance, final RequestScope requestScope, final Factory exceptionMappersProvider) { this.processedResponse = processedResponse; this.requestScope = requestScope; this.scopeInstance = scopeInstance; this.callback = callback; this.inflectedResponse = inflectedResponse; this.respondingCtxProvider = respondingCtxProvider; this.exceptionMappersProvider = exceptionMappersProvider; } @Override public void run() { try { requestScope.runInScope(scopeInstance, new Runnable() { @Override public void run() { if (inflectedResponse.isCancelled()) { // the request processing has been cancelled; just cancel this future & return processedResponse.cancel(true); return; } DATA response; try { response = inflectedResponse.get(); } catch (Throwable ex) { final Throwable unwrapped = (ex instanceof ExecutionException) ? ex.getCause() : ex; LOGGER.log(Level.FINE, "Request-to-response transformation finished with an exception.", unwrapped); try { response = convertResponse(mapException(unwrapped)); } catch (Throwable ex2) { setResult(ex2); return; } if (response == null) { setResult(unwrapped); return; } } for (int i = 0; i < 2; i++) { try { response = runResponders(response); break; } catch (Throwable ex) { LOGGER.log(Level.FINE, "Responder chain execution finished with an exception.", ex); if (i == 0) { // try to map the first responder exception try { response = convertResponse(mapException(ex)); } catch (Throwable ex2) { setResult(ex2); return; } } if (response == null) { setResult(ex); return; } } } setResult(response); } }); } finally { scopeInstance.release(); } } private DATA runResponders(DATA response) { Stage responder = respondingCtxProvider.get().createResponderRoot(); if (responder != null) { Stage.Continuation continuation = Stage.Continuation.of(response, responder); while (continuation.hasNext()) { continuation = continuation.next().apply(continuation.result()); } return continuation.result(); } return response; } /** * Convert an exception-mapped JAX-RS {@link Response response} to supported * processing data type. * * @param exceptionResponse a processing exception mapped to a JAX-RS response. * @return JAX-RS exception-mapped response transformed to supported processing * data type. */ protected abstract DATA convertResponse(Response exceptionResponse); @SuppressWarnings("unchecked") private Response mapException(Throwable exception) throws Exception { Response response = null; if (exception instanceof MappableException) { exception = exception.getCause(); } if (exception instanceof WebApplicationException) { response = ((WebApplicationException) exception).getResponse(); } final ExceptionMappers exceptionMappers = exceptionMappersProvider.get(); if ((response == null || !response.hasEntity()) && exceptionMappers != null) { ExceptionMapper mapper = exceptionMappers.find(exception.getClass()); if (mapper != null) { response = mapper.toResponse(exception); // may throw exception } } return response; } private void setResult(DATA response) { try { processedResponse.set(response); } finally { notifyCallback(response); } } private void setResult(Throwable exception) { try { processedResponse.setException(exception); } finally { notifyCallback(exception); } } private void notifyCallback(DATA response) { try { callback.result(response); } catch (Exception ex) { LOGGER.log(Level.WARNING, LocalizationMessages.CALLBACK_METHOD_INVOCATION_FAILED("result", Thread.currentThread().getName()), ex); } } private void notifyCallback(Throwable exception) { try { callback.failure(exception); } catch (Exception ex) { LOGGER.log(Level.WARNING, LocalizationMessages.CALLBACK_METHOD_INVOCATION_FAILED("failure", Thread.currentThread().getName()), ex); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy