org.entando.entando.plugins.jpoauthclient.aps.system.services.client.ProviderConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for org.entando.entando.plugins.jpoauthclient.aps.system.services.client.ProviderConnectionManager.java

Source

/*
 * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.entando.entando.plugins.jpoauthclient.aps.system.services.client;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.OAuthProblemException;
import net.oauth.OAuthServiceProvider;
import net.oauth.ParameterStyle;
import net.oauth.server.HttpRequestMessage;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.util.URIUtil;
import org.entando.entando.aps.system.services.api.model.ApiMethod;
import org.entando.entando.aps.system.services.api.model.StringApiResponse;
import org.entando.entando.aps.system.services.api.provider.json.JSONProvider;
import org.entando.entando.aps.system.services.api.server.ApiRestStatusServer;
import org.entando.entando.plugins.jpoauthclient.aps.oauth.client.OAuthClient;
import org.entando.entando.plugins.jpoauthclient.aps.servlet.Callback;
import org.entando.entando.plugins.jpoauthclient.aps.system.ConsumerSystemConstants;
import org.entando.entando.plugins.jpoauthclient.aps.system.CookieMap;
import org.entando.entando.plugins.jpoauthclient.aps.system.OAuthClientException;
import org.entando.entando.plugins.jpoauthclient.aps.system.RedirectException;
import org.entando.entando.plugins.jpoauthclient.aps.system.httpclient.OAuthHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.ServletContextAware;

import com.agiletec.aps.system.common.AbstractService;
import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.baseconfig.ConfigInterface;
import org.apache.commons.io.IOUtils;

/**
 * @author E.Santoboni
 */
public class ProviderConnectionManager extends AbstractService
        implements IProviderConnectionManager, ServletContextAware {

    private static final Logger _logger = LoggerFactory.getLogger(ProviderConnectionManager.class);

    @Override
    public void init() throws Exception {
        this.loadDefaultConsumer();
        _logger.debug("{} ready", this.getClass().getName());
    }

    @Override
    protected void release() {
        this._consumers.clear();
        super.release();
    }

    protected void loadDefaultConsumer() throws Exception {
        InputStream is = null;
        try {
            is = this.getServletContext()
                    .getResourceAsStream("/WEB-INF/plugins/jpoauthclient/oauth_consumer.properties");
            Properties properties = new Properties();
            properties.load(is);
            String base = this.getConfigManager()
                    .getParam(ConsumerSystemConstants.ENTANDO_SERVICE_PROVIDER_BASE_URL_PARAM_NAME);
            URL baseURL = (base == null) ? null : new URL(base);
            if (null == baseURL)
                return;
            String requestTokenURL = this.getURL(baseURL, properties,
                    ConsumerSystemConstants.ENTANDO_SERVICE_PROVIDER_REQ_TOKEN_URL_PARAM_NAME);
            String userAuthorizationURL = this.getURL(baseURL, properties,
                    ConsumerSystemConstants.ENTANDO_SERVICE_PROVIDER_AUTH_URL_PARAM_NAME);
            String accessTokenURL = this.getURL(baseURL, properties,
                    ConsumerSystemConstants.ENTANDO_SERVICE_PROVIDER_ACCESS_TOKEN_URL_PARAM_NAME);
            if (null == requestTokenURL || null == userAuthorizationURL || null == accessTokenURL)
                return;
            OAuthServiceProvider serviceProvider = new OAuthServiceProvider(requestTokenURL, userAuthorizationURL,
                    accessTokenURL);
            OAuthConsumer consumer = new OAuthConsumer(
                    this.getConfigManager().getParam(ConsumerSystemConstants.CONSUMER_CALLBACK_URL_PARAM_NAME),
                    this.getConfigManager().getParam(ConsumerSystemConstants.CONSUMER_KEY_PARAM_NAME),
                    this.getConfigManager().getParam(ConsumerSystemConstants.CONSUMER_SECRET_PARAM_NAME),
                    serviceProvider);
            consumer.setProperty("name", ConsumerSystemConstants.DEFAULT_CONSUMER_NAME);
            if (baseURL != null) {
                consumer.setProperty("serviceProvider.baseURL", baseURL);
            }
            this._consumers.put(ConsumerSystemConstants.DEFAULT_CONSUMER_NAME, consumer);
        } catch (Throwable t) {
            _logger.error("Error extracting default consumer", t);
        } finally {
            if (null != is) {
                is.close();
            }
        }
    }

    private String getURL(URL baseUrl, Properties properties, String pathParamName) throws MalformedURLException {
        String path = properties.getProperty(pathParamName);
        if (null == baseUrl || null == path)
            return null;
        return (new URL(baseUrl, path)).toExternalForm();
    }

    @Override
    public InputStream invokeApiMethod(ApiMethodRequestBean bean, HttpServletRequest request,
            HttpServletResponse response, boolean handleException) throws RedirectException, Exception {
        return this.invokeApiMethod(bean, request, response, handleException, true);
    }

    @Override
    public InputStream invokeApiMethod(ApiMethodRequestBean bean, HttpServletRequest request,
            HttpServletResponse response, boolean handleException, boolean redirectOAuthErrorPage)
            throws RedirectException, Exception {
        InputStream stream = null;
        try {
            this.checkConsumer(bean);
            Boolean free = this.checkMethod(bean);
            if (null == free)
                return null;
            ApiMethod.HttpMethod method = bean.getHttpMethod();
            if (free) {
                if (method.equals(ApiMethod.HttpMethod.GET) || method.equals(ApiMethod.HttpMethod.DELETE)) {
                    stream = this.invokeFreeGetDeleteApi(bean, handleException);
                } else {
                    stream = this.invokeFreePostPutApi(bean, handleException);
                }
            } else {
                if (method.equals(ApiMethod.HttpMethod.GET) || method.equals(ApiMethod.HttpMethod.DELETE)) {
                    stream = this.invokeProtectedGetDeleteApi(bean, request, response, handleException,
                            redirectOAuthErrorPage);
                } else {
                    stream = this.invokeProtectedPostPutApi(bean, request, response, handleException,
                            redirectOAuthErrorPage);
                }
            }
        } catch (OAuthClientException ume) {
            if (handleException) {
                throw ume;
            }
        } catch (RedirectException re) {
            if (handleException) {
                throw re;
            }
        } catch (Exception t) {
            if (handleException) {
                _logger.error("Error invoking api method", t);
                throw t;
            }
        }
        return stream;
    }

    @Override
    public Object extractApiMethodResult(ApiMethodRequestBean bean, Class expectedType, HttpServletRequest request,
            HttpServletResponse response, boolean handleException) throws RedirectException, Exception {
        return this.extractApiMethodResult(bean, expectedType, request, response, handleException, true);
    }

    @Override
    public Object extractApiMethodResult(ApiMethodRequestBean bean, Class expectedType, HttpServletRequest request,
            HttpServletResponse response, boolean handleException, boolean redirectOAuthErrorPage)
            throws RedirectException, Exception {
        Object bodyObject = null;
        try {
            InputStream stream = this.invokeApiMethod(bean, request, response, handleException,
                    redirectOAuthErrorPage);
            if (null == stream)
                return null;
            bodyObject = this.unmarshallResponse(stream, expectedType, bean.getMediaType());
        } catch (OAuthClientException ume) {
            if (handleException) {
                throw ume;
            }
        } catch (RedirectException re) {
            if (handleException) {
                throw re;
            }
        } catch (Exception t) {
            if (handleException) {
                _logger.error("Error extracting result", t);
                throw t;
            }
        }
        return bodyObject;
    }

    private void checkConsumer(ApiMethodRequestBean bean) throws OAuthClientException, ApsSystemException {
        try {
            if (null != bean.getConsumerName()) {
                if (null == this.getConsumer(bean.getConsumerName())) {
                    throw new OAuthClientException("Null consumer : " + bean.getConsumerName(),
                            OAuthClientException.ErrorCode.NULL_CONSUMER);
                } else
                    return;
            } else if (null != bean.getRequestUrl()) {
                Iterator<OAuthConsumer> consumers = this._consumers.values().iterator();
                while (consumers.hasNext()) {
                    OAuthConsumer oauthConsumer = consumers.next();
                    String baseUrl = ((URL) oauthConsumer.getProperty("serviceProvider.baseURL")).toExternalForm();
                    if (bean.getRequestUrl().startsWith(baseUrl)) {
                        String name = (String) oauthConsumer.getProperty("name");
                        bean.setConsumerName(name);
                    }
                }
                if (null == bean.getConsumerName()) {
                    throw new OAuthClientException("Null consumer for url : " + bean.getRequestUrl(),
                            OAuthClientException.ErrorCode.NULL_CONSUMER);
                }
            } else {
                throw new OAuthClientException("Null consumer", OAuthClientException.ErrorCode.NULL_CONSUMER);
            }
        } catch (OAuthClientException t) {
            throw t;
        } catch (Throwable t) {
            _logger.error("Error executing Check consumer", t);
            String message = "Error executing Check consumer";
            throw new ApsSystemException(message, t);
        }
    }

    private Boolean checkMethod(ApiMethodRequestBean bean) throws OAuthClientException, ApsSystemException {
        Boolean free = null;
        String url = null;
        try {
            OAuthConsumer consumer = this.getConsumer(bean.getConsumerName());
            if (null == consumer) {
                throw new OAuthClientException("Null consumer : " + bean.getConsumerName(),
                        OAuthClientException.ErrorCode.NULL_CONSUMER);
            }
            URL baseURL = (URL) consumer.getProperty("serviceProvider.baseURL");
            url = baseURL.toString();
            if (!url.endsWith("/"))
                url += "/";
            url += "api/apistatus/";
            if (bean.getNamespace() != null && bean.getNamespace().trim().length() > 0) {
                url += bean.getNamespace() + "/";
            }
            url += bean.getResourceName() + "/" + bean.getHttpMethod().toString() + "/.json";
            String methodResult = this.invokeGetDeleteMethod(url, true, null);
            InputStream stream = new ByteArrayInputStream(methodResult.getBytes());//method.getResponseBodyAsStream();
            StringApiResponse response = (StringApiResponse) this.unmarshallResponse(stream,
                    StringApiResponse.class, MediaType.APPLICATION_JSON_TYPE);
            String result = response.getResult();
            if (null != result && result.equalsIgnoreCase(ApiRestStatusServer.ApiStatus.INACTIVE.toString())) {
                throw new OAuthClientException(url, bean.getHttpMethod());
            }
            free = (null != result && result.equalsIgnoreCase(ApiRestStatusServer.ApiStatus.FREE.toString()));
        } catch (OAuthClientException t) {
            throw t;
        } catch (Throwable t) {
            _logger.error("Error executing Check for method for uri '{}' - method '{}'", url, bean.getHttpMethod(),
                    t);
            String message = "Error executing Check for method for uri '" + url + "' - method "
                    + bean.getHttpMethod();
            throw new ApsSystemException(message, t);
        }
        return free;
    }

    private Object unmarshallResponse(InputStream stream, Class expectedType, MediaType responseType) {
        Object bodyObject = null;
        try {
            if (responseType.equals(MediaType.APPLICATION_JSON_TYPE)) {
                JSONProvider jsonProvider = new JSONProvider();
                bodyObject = jsonProvider.readFrom(expectedType, expectedType.getGenericSuperclass(),
                        expectedType.getAnnotations(), MediaType.APPLICATION_JSON_TYPE, null, stream);
            } else {
                JAXBContext context = JAXBContext.newInstance(expectedType);
                Unmarshaller unmarshaller = context.createUnmarshaller();
                bodyObject = (Object) unmarshaller.unmarshal(stream);
            }
        } catch (Throwable t) {
            _logger.error("Error unmarshalling response result", t);
        }
        return bodyObject;
    }

    private InputStream invokeFreeGetDeleteApi(ApiMethodRequestBean bean, boolean handleException)
            throws Exception {
        InputStream stream = null;
        try {
            String url = this.buildApiMethodUri(bean);
            boolean isGetMethod = ApiMethod.HttpMethod.GET.equals(bean.getHttpMethod());
            String methodResult = this.invokeGetDeleteMethod(url, isGetMethod, bean.getRequestParameters());
            stream = new ByteArrayInputStream(methodResult.getBytes());
        } catch (Throwable t) {
            _logger.error("Error invoking Free Api", t);
            if (handleException) {
                throw new Exception("Error invoking Free Api", t);
            }
        }
        return stream;
    }

    private InputStream invokeFreePostPutApi(ApiMethodRequestBean bean, boolean handleException) throws Exception {
        InputStream stream = null;
        try {
            String url = this.buildApiMethodUri(bean);
            boolean isPostMethod = ApiMethod.HttpMethod.POST.equals(bean.getHttpMethod());
            MediaType bodyMediaType = (null != bean.getRequestBodyMediaType()) ? bean.getRequestBodyMediaType()
                    : MediaType.APPLICATION_JSON_TYPE;
            String methodResult = this.invokePostPutMethod(url, isPostMethod, bean.getRequestBody(),
                    bean.getRequestParameters(), bodyMediaType);
            stream = new ByteArrayInputStream(methodResult.getBytes());
        } catch (Throwable t) {
            _logger.error("Error invoking Free Api", t);
            if (handleException) {
                throw new Exception("Error invoking Free Api", t);
            }
        }
        return stream;
    }

    private String buildApiMethodUri(ApiMethodRequestBean bean) throws Throwable {
        OAuthConsumer consumer = this.getConsumer(bean.getConsumerName());
        if (null == consumer)
            return null;
        URL baseURL = (URL) consumer.getProperty("serviceProvider.baseURL");
        String url = baseURL.toString();
        String path = bean.getPath();
        if (!url.endsWith("/") && !path.startsWith("/"))
            url += "/";
        url += path;
        return url;
    }

    private String invokeGetDeleteMethod(String url, boolean isGet, Properties parameters) throws Throwable {
        String result = null;
        HttpMethodBase method = null;
        InputStream inputStream = null;
        try {
            HttpClient client = new HttpClient();
            if (isGet) {
                method = new GetMethod(url);
            } else {
                method = new DeleteMethod(url);
            }
            this.addQueryString(method, parameters);
            client.executeMethod(method);
            inputStream = method.getResponseBodyAsStream();
            result = IOUtils.toString(inputStream, "UTF-8");
            //byte[] responseBody = method.getResponseBody();
            //result = new String(responseBody);
        } catch (Throwable t) {
            _logger.error("Error invoking Get or Delete Method", t);
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
            if (null != method) {
                method.releaseConnection();
            }
        }
        return result;
    }

    private String invokePostPutMethod(String url, boolean isPost, String body, Properties parameters,
            MediaType mediaType) {
        String result = null;
        EntityEnclosingMethod method = null;
        try {
            HttpClient client = new HttpClient();
            if (isPost) {
                method = new PostMethod(url);
            } else {
                method = new PutMethod(url);
            }
            this.addQueryString(method, parameters);
            method.setRequestBody(body);
            method.setRequestHeader("Content-type", mediaType.toString() + "; charset=UTF-8");
            client.executeMethod(method);
            byte[] responseBody = method.getResponseBody();
            result = new String(responseBody);
        } catch (Throwable t) {
            _logger.error("Error invoking Post or put Method", t);
        } finally {
            if (null != method) {
                method.releaseConnection();
            }
        }
        return result;
    }

    private void addQueryString(HttpMethodBase method, Properties parameters) throws Throwable {
        if (null == parameters)
            return;
        StringBuilder builder = new StringBuilder();
        boolean first = true;
        Iterator<Object> keyIter = parameters.keySet().iterator();
        while (keyIter.hasNext()) {
            Object key = keyIter.next();
            if (!first)
                builder.append("&");
            builder.append(key.toString()).append("=").append(parameters.getProperty(key.toString()));
            if (first)
                first = false;
        }
        method.setQueryString(URIUtil.encodeQuery(builder.toString()));
    }

    private InputStream invokeProtectedGetDeleteApi(ApiMethodRequestBean bean, HttpServletRequest request,
            HttpServletResponse response, boolean handleException, boolean redirectOAuthErrorPage)
            throws RedirectException, Exception {
        InputStream stream = null;
        OAuthConsumer consumer = null;
        try {
            consumer = this.getConsumer(bean.getConsumerName());
            OAuthAccessor accessor = this.getAccessor(bean, request, response, consumer);
            Collection<OAuth.Parameter> parameters = HttpRequestMessage.getParameters(request);
            if (null != bean.getRequestParameters()) {
                Iterator<Object> iter = bean.getRequestParameters().keySet().iterator();
                while (iter.hasNext()) {
                    String paramKey = iter.next().toString();
                    parameters
                            .add(new OAuth.Parameter(paramKey, bean.getRequestParameters().getProperty(paramKey)));
                }
            }
            URL baseURL = (URL) accessor.consumer.getProperty("serviceProvider.baseURL");
            String url = new URL(baseURL, bean.getPath()).toExternalForm();
            OAuthMessage oauthRequest = accessor.newRequestMessage(String.valueOf(bean.getHttpMethod()), url,
                    parameters);
            OAuthClient client = new OAuthClient(new OAuthHttpClient());
            OAuthMessage oauthResponse = client.invoke(oauthRequest, ParameterStyle.AUTHORIZATION_HEADER,
                    redirectOAuthErrorPage);
            stream = oauthResponse.getBodyAsStream();
        } catch (RedirectException re) {
            if (handleException) {
                this.handleException(bean, re, request, response, consumer, true);
                throw re;
            }
        } catch (OAuthProblemException oae) {
            if (handleException) {
                this.handleException(bean, oae, request, response, consumer, redirectOAuthErrorPage);
                throw oae;
            }
        } catch (Throwable t) {
            _logger.error("Error invoking api method", t);
            if (handleException) {
                throw new Exception("Error invoking api method", t);
            }
        }
        return stream;
    }

    private InputStream invokeProtectedPostPutApi(ApiMethodRequestBean bean, HttpServletRequest request,
            HttpServletResponse response, boolean handleException, boolean redirectOAuthErrorPage)
            throws RedirectException, Exception {
        InputStream stream = null;
        OAuthConsumer consumer = null;
        try {
            consumer = this.getConsumer(bean.getConsumerName());
            OAuthAccessor accessor = this.getAccessor(bean, request, response, consumer);
            Collection<OAuth.Parameter> parameters = HttpRequestMessage.getParameters(request);
            if (null != bean.getRequestParameters()) {
                Iterator<Object> iter = bean.getRequestParameters().keySet().iterator();
                while (iter.hasNext()) {
                    String paramKey = iter.next().toString();
                    parameters
                            .add(new OAuth.Parameter(paramKey, bean.getRequestParameters().getProperty(paramKey)));
                }
            }
            URL baseURL = (URL) accessor.consumer.getProperty("serviceProvider.baseURL");
            String url = new URL(baseURL, bean.getPath()).toExternalForm();
            InputStream streamRequest = new ByteArrayInputStream(bean.getRequestBody().getBytes("UTF-8"));
            OAuthMessage oauthRequest = accessor.newRequestMessage(String.valueOf(bean.getHttpMethod()), url,
                    parameters, streamRequest);
            OAuthClient client = new OAuthClient(new OAuthHttpClient());
            OAuthMessage oauthResponse = client.invoke(oauthRequest, ParameterStyle.AUTHORIZATION_HEADER,
                    redirectOAuthErrorPage);
            stream = oauthResponse.getBodyAsStream();
        } catch (RedirectException re) {
            if (handleException) {
                this.handleException(bean, re, request, response, consumer, true);
                throw re;
            }
        } catch (OAuthProblemException oae) {
            if (handleException) {
                this.handleException(bean, oae, request, response, consumer, redirectOAuthErrorPage);
                throw oae;
            }
        } catch (Throwable t) {
            _logger.error("Error invoking api method", t);
            if (handleException) {
                throw new Exception("Error invoking api method", t);
            }
        }
        return stream;
    }

    @Override
    public OAuthConsumer getConsumer(String name) throws IOException {
        return this._consumers.get(name);
    }

    /**
     * Get the access token and token secret for the given consumer. Get them
     * from cookies if possible; otherwise obtain them from the service
     * provider. In the latter case, throw RedirectException.
     * @throws IOException 
     * @throws URISyntaxException 
     */
    private OAuthAccessor getAccessor(ApiMethodRequestBean bean, HttpServletRequest request,
            HttpServletResponse response, OAuthConsumer consumer)
            throws OAuthException, IOException, URISyntaxException {
        OAuthAccessor accessor = null;
        CookieMap cookies = new CookieMap(request, response);
        accessor = this.newAccessor(consumer, cookies);
        if (accessor.accessToken == null) {
            this.getAccessToken(bean, request, cookies, accessor);
        }
        return accessor;
    }

    /**
     * Construct an accessor from cookies. The resulting accessor won't
     * necessarily have any tokens.
     */
    @Override
    public OAuthAccessor newAccessor(OAuthConsumer consumer, CookieMap cookies) throws OAuthException {
        OAuthAccessor accessor = new OAuthAccessor(consumer);
        String consumerName = (String) consumer.getProperty("name");
        accessor.requestToken = cookies.get(consumerName + ".requestToken");
        accessor.accessToken = cookies.get(consumerName + ".accessToken");
        accessor.tokenSecret = cookies.get(consumerName + ".tokenSecret");
        return accessor;
    }

    /** Remove all the cookies that contain accessors' data. */
    @Override
    public void removeAccessors(CookieMap cookies) {
        List<String> names = new ArrayList<String>(cookies.keySet());
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            if (name.endsWith(".requestToken") || name.endsWith(".accessToken") || name.endsWith(".tokenSecret")) {
                cookies.remove(name);
            }
        }
    }

    /**
     * Get a fresh access token from the service provider.
     * @throws IOException 
     * @throws URISyntaxException 
     * @throws RedirectException to obtain authorization
     */
    private void getAccessToken(ApiMethodRequestBean bean, HttpServletRequest request, CookieMap cookies,
            OAuthAccessor accessor) throws OAuthException, IOException, URISyntaxException {
        final String consumerName = (String) accessor.consumer.getProperty("name");
        final String callbackURL = getCallbackURL(bean, request);
        List<OAuth.Parameter> parameters = OAuth.newList(OAuth.OAUTH_CALLBACK, callbackURL);
        // Google needs to know what you intend to do with the access token:
        Object scope = accessor.consumer.getProperty("request.scope");
        if (scope != null) {
            parameters.add(new OAuth.Parameter("scope", scope.toString()));
        }
        OAuthClient client = new OAuthClient(new OAuthHttpClient());
        OAuthMessage response = client.getRequestTokenResponse(accessor, null, parameters);
        cookies.put(consumerName + ".requestToken", accessor.requestToken);
        cookies.put(consumerName + ".tokenSecret", accessor.tokenSecret);
        String authorizationURL = accessor.consumer.serviceProvider.userAuthorizationURL;
        if (authorizationURL.startsWith("/")) {
            authorizationURL = (new URL(new URL(request.getRequestURL().toString()),
                    request.getContextPath() + authorizationURL)).toString();
        }
        authorizationURL = OAuth.addParameters(authorizationURL, OAuth.OAUTH_TOKEN, accessor.requestToken);
        if (response.getParameter(OAuth.OAUTH_CALLBACK_CONFIRMED) == null) {
            authorizationURL = OAuth.addParameters(authorizationURL, OAuth.OAUTH_CALLBACK, callbackURL);
        }
        throw new RedirectException(authorizationURL);
    }

    private String getCallbackURL(ApiMethodRequestBean bean, HttpServletRequest request) throws IOException {
        URL base = new URL(new URL(request.getRequestURL().toString()), request.getContextPath() + Callback.PATH);
        String returnTo = (null != bean && null != bean.getRedirectUrl()) ? bean.getRedirectUrl()
                : this.getRequestPath(request);
        return OAuth.addParameters(base.toExternalForm(), "consumer", bean.getConsumerName(), "returnTo", returnTo);
    }

    /** Reconstruct the requested URL path, complete with query string (if any). */
    private String getRequestPath(HttpServletRequest request) throws MalformedURLException {
        StringBuilder path = new StringBuilder(request.getRequestURL());
        String queryString = request.getQueryString();//url.getQuery();
        if (queryString != null) {
            path.append("?").append(queryString);
        }
        String pathToReturn = path.toString();
        return pathToReturn;
    }

    @Override
    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response,
            OAuthConsumer consumer) throws IOException, ServletException {
        this.handleException(null, e, request, response, consumer, true);
    }

    /**
     * Handle an exception that occurred while processing an HTTP request.
     * Depending on the exception, either send a response, redirect the client
     * or propagate an exception.
     */
    public void handleException(ApiMethodRequestBean bean, Exception e, HttpServletRequest request,
            HttpServletResponse response, OAuthConsumer consumer, boolean redirectOAuthErrorPage)
            throws IOException, ServletException {
        if (e instanceof RedirectException) {
            RedirectException redirect = (RedirectException) e;
            String targetURL = redirect.getTargetURL();
            if (targetURL != null) {
                response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
                response.setHeader("Location", targetURL);
            }
        } else if (e instanceof OAuthProblemException) {
            OAuthProblemException p = (OAuthProblemException) e;
            String problem = p.getProblem();
            List<String> recoverableProblems = Arrays.asList(RECOVERABLE_PROBLEMS);
            if (consumer != null && recoverableProblems.contains(problem)) {
                try {
                    CookieMap cookies = new CookieMap(request, response);
                    OAuthAccessor accessor = this.newAccessor(consumer, cookies);
                    this.getAccessToken(bean, request, cookies, accessor);
                } catch (Exception e2) {
                    handleException(bean, e2, request, response, null, redirectOAuthErrorPage);
                }
            } else if (redirectOAuthErrorPage) {
                try {
                    StringWriter s = new StringWriter();
                    PrintWriter pw = new PrintWriter(s);
                    e.printStackTrace(pw);
                    pw.flush();
                    p.setParameter("stack trace", s.toString());
                } catch (Exception rats) {
                    //nothing to catch
                }
                response.setStatus(p.getHttpStatusCode());
                response.resetBuffer();
                request.setAttribute("OAuthProblemException", p);
                request.getRequestDispatcher("/WEB-INF/plugins/jpoauthclient/OAuthProblemException.jsp")
                        .forward(request, response);
            }
        } else if (e instanceof IOException) {
            throw (IOException) e;
        } else if (e instanceof ServletException) {
            throw (ServletException) e;
        } else if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        } else {
            throw new ServletException(e);
        }
    }

    protected ServletContext getServletContext() {
        return _servletContext;
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        this._servletContext = servletContext;
    }

    protected ConfigInterface getConfigManager() {
        return _configManager;
    }

    public void setConfigManager(ConfigInterface configManager) {
        this._configManager = configManager;
    }

    private ServletContext _servletContext;

    private ConfigInterface _configManager;

    private Map<String, OAuthConsumer> _consumers = new HashMap<String, OAuthConsumer>();

    private static final String[] RECOVERABLE_PROBLEMS = new String[] { "token_revoked", "token_expired",
            "permission_unknown" };

}