Java tutorial
/** * Copyright (C) 2005-2009 Alfresco Software Limited. * * This file is part of the Spring Surf Extension project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.extensions.webscripts.servlet; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.extensions.config.ConfigService; import org.springframework.extensions.config.RemoteConfigElement; import org.springframework.extensions.config.RemoteConfigElement.EndpointDescriptor; import org.springframework.extensions.config.RemoteConfigElement.IdentityType; import org.springframework.extensions.surf.exception.WebScriptsPlatformException; import org.springframework.extensions.surf.util.Base64; import org.springframework.extensions.webscripts.connector.Connector; import org.springframework.extensions.webscripts.connector.ConnectorContext; import org.springframework.extensions.webscripts.connector.ConnectorService; import org.springframework.extensions.webscripts.connector.Credentials; import org.springframework.extensions.webscripts.connector.CredentialsImpl; import org.springframework.extensions.webscripts.connector.HttpMethod; import org.springframework.extensions.webscripts.connector.Response; import org.springframework.extensions.webscripts.servlet.mvc.EndPointProxyController; import org.springframework.web.context.support.WebApplicationContextUtils; /** * EndPoint HTTP Proxy Servlet. * * Provides the ability to submit a URL request via a configured end point such as a * remote Alfresco Server. Makes use of the Connector framework so that appropriate * authentication is automatically applied to the proxied request as applicable. * * This servlet accepts URIs of the following format: * * /proxy/<endpointid>[/uri]*[?[<argName>=<argValue>]*] * * Where: * * - endpointid is the ID of a configured EndPoint model object to make a request against * - url is the uri to call on the EndPoint URL e.g. /api/sites * - argName is the name of a URL argument to append to the request * - argValue is the value of URL argument * * E.g. * * /proxy/alfresco/api/sites?name=mysite&desc=description * * The proxy supports all valid HTTP methods. * * @author kevinr * @deprecated * @see EndPointProxyController */ public class EndPointProxyServlet extends HttpServlet { private static final String USER_ID = "_alf_USER_ID"; private static final String PARAM_ALF_TICKET = "alf_ticket"; private static Log logger = LogFactory.getLog(EndPointProxyServlet.class); private static final long serialVersionUID = -176412355613122789L; protected RemoteConfigElement config; protected ConnectorService connectorService; @Override public void init() throws ServletException { super.init(); ApplicationContext context = WebApplicationContextUtils .getRequiredWebApplicationContext(getServletContext()); ConfigService configService = (ConfigService) context.getBean("web.config"); // retrieve the remote configuration this.config = (RemoteConfigElement) configService.getConfig("Remote").getConfigElement("remote"); // retrieve the connector service this.connectorService = (ConnectorService) context.getBean("connector.service"); } @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String uri = req.getRequestURI().substring(req.getContextPath().length()); // validate and return the endpoint id from the URI path - stripping the servlet context StringTokenizer t = new StringTokenizer(uri, "/"); String servletName = t.nextToken(); if (!t.hasMoreTokens()) { throw new IllegalArgumentException("Proxy URL did not specify endpoint id."); } String endpointId = t.nextToken(); // rebuild rest of the URL for the proxy request StringBuilder buf = new StringBuilder(64); if (t.hasMoreTokens()) { do { buf.append('/'); buf.append(t.nextToken()); } while (t.hasMoreTokens()); } else { // allow for an empty uri to be passed in // this could therefore refer to the root of a service i.e. /webapp/axis buf.append('/'); } try { // retrieve the endpoint descriptor - do not allow proxy access to unsecure endpoints EndpointDescriptor descriptor = this.config.getEndpointDescriptor(endpointId); if (descriptor == null || descriptor.getUnsecure()) { // throw an exception if endpoint ID is does not exist or invalid throw new WebScriptsPlatformException("Invalid EndPoint Id: " + endpointId); } // special case for some Flash based apps - they might pass in the alf_ticket directly // as a parameter as POST requests do not correctly pickup the browser cookies and // therefore do not share the same session so we must apply the ticket directly String ticket = req.getParameter(PARAM_ALF_TICKET); // user id from session NOTE: @see org.alfresco.web.site.UserFactory Connector connector; String userId = (String) req.getSession().getAttribute(USER_ID); if (userId != null) { // build an authenticated connector - as we have a userId connector = this.connectorService.getConnector(endpointId, userId, req.getSession()); } else if (ticket != null || descriptor.getIdentity() == IdentityType.NONE || descriptor.getIdentity() == IdentityType.DECLARED || descriptor.getExternalAuth()) { // the authentication for this endpoint is either not required, declared in config or // managed "externally" (i.e. by a servlet filter such as NTLM) - this means we should // proceed on the assumption it will be dealt with later connector = this.connectorService.getConnector(endpointId, req.getSession()); } else if (descriptor.getBasicAuth()) { // check for HTTP authorisation request (i.e. RSS feeds etc.) String authorization = req.getHeader("Authorization"); if (authorization == null || authorization.length() == 0) { res.setStatus(HttpServletResponse.SC_UNAUTHORIZED, "No USER_ID found in session and requested endpoint requires authentication."); res.setHeader("WWW-Authenticate", "Basic realm=\"Alfresco\""); // no further processing as authentication is required but not provided // the browser will now prompt the user for appropriate credentials return; } else { // user has provided authentication details with the request String[] authParts = authorization.split(" "); if (!authParts[0].equalsIgnoreCase("basic")) { throw new WebScriptsPlatformException( "Authorization '" + authParts[0] + "' not supported."); } String[] values = new String(Base64.decode(authParts[1])).split(":"); if (values.length == 2) { if (logger.isDebugEnabled()) logger.debug("Authenticating (BASIC HTTP) user " + values[0]); // assume username and password passed as the parts and // build an unauthenticated authentication connector then // apply the supplied credentials to it connector = this.connectorService.getConnector(endpointId, values[0], req.getSession()); Credentials credentials = new CredentialsImpl(endpointId); credentials.setProperty(Credentials.CREDENTIAL_USERNAME, values[0]); credentials.setProperty(Credentials.CREDENTIAL_PASSWORD, values[1]); connector.setCredentials(credentials); } else { throw new WebScriptsPlatformException("Authorization request did not provide user/pass."); } } } else { res.setStatus(HttpServletResponse.SC_UNAUTHORIZED, "No USER_ID found in session and requested endpoint requires authentication."); return; } // build a connector context, stores information about how we will drive the remote client ConnectorContext context; if (ticket == null) { context = new ConnectorContext(); } else { // special case for some Flash apps - see above Map<String, String> params = new HashMap<String, String>(1, 1.0f); params.put(PARAM_ALF_TICKET, ticket); context = new ConnectorContext(params, null); } context.setContentType(req.getContentType()); context.setMethod(HttpMethod.valueOf(req.getMethod().toUpperCase())); // build proxy URL referencing the endpoint String q = req.getQueryString(); String url = buf.toString() + (q != null && q.length() != 0 ? "?" + q : ""); if (logger.isDebugEnabled()) { logger.debug("EndPointProxyServlet preparing to proxy:"); logger.debug(" - endpointId: " + endpointId); logger.debug(" - userId: " + userId); logger.debug(" - connector: " + connector); logger.debug(" - method: " + context.getMethod()); logger.debug(" - url: " + url); } // call through using our connector to proxy Response response = connector.call(url, context, req, res); if (logger.isDebugEnabled()) { logger.debug("Return code: " + response.getStatus().getCode()); if (response.getStatus().getCode() == 500) { logger.debug("Error detected: " + response.getStatus().getMessage() + "\n" + response.getStatus().getException().toString()); } } } catch (Throwable err) { // TODO: trap and handle errors! throw new WebScriptsPlatformException("Error during endpoint proxy processing: " + err.getMessage(), err); } } }