package com.ibm.sbt.service.basic; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.CoreProtocolPNames; import com.ibm.commons.runtime.NoAccessSignal; import com.ibm.commons.runtime.mime.MIME; import com.ibm.commons.util.PathUtil; import com.ibm.commons.util.StringUtil; import com.ibm.sbt.services.client.ClientService; import com.ibm.sbt.services.client.ClientService.Args; import com.ibm.sbt.services.client.ClientService.Content; import com.ibm.sbt.services.client.ClientService.ContentFile; import com.ibm.sbt.services.client.ClientService.Handler; import com.ibm.sbt.services.client.ClientServicesException; import com.ibm.sbt.services.endpoints.Endpoint; import com.ibm.sbt.services.endpoints.EndpointFactory; import com.ibm.sbt.services.util.AuthUtil; import com.ibm.sbt.services.util.SSLUtil; /** * * @author Vineet Kanwal **/ public abstract class AbstractFileProxyService extends ProxyEndpointService { protected enum Operations { UPLOAD_FILE("UploadFile"), DOWNLOAD_FILE("DownloadFile"), UPDATE_PROFILE_PHOTO("UpdateProfilePhoto"), UPLOAD_NEW_VERSION("UploadNewVersion"), UPDATE_COMMUNITY_LOGO("UpdateCommunityLogo"), UPLOAD_COMMUNITY_FILE("UploadCommunityFile"); private Operations(final String text) { this.text = text; } private final String text; @Override public String toString() { return text; } } protected Map<String, String> parameters = new HashMap<String, String>(); protected String operation = null; protected String method = null; private Long length = 0L; protected static String MIMETYPE_DEFAULT = "application/octet-stream"; protected static String MIMETYPE_UNKNOWN = "unknown/unknown"; protected abstract String getRequestURI(String smethod, String authType, Map<String, String[]> params) throws ServletException; @Override protected void initProxy(HttpServletRequest request, HttpServletResponse response) throws ServletException { // TODO can this be moved to parametrers String pathinfo = request.getPathInfo().substring(request.getPathInfo().indexOf("/files")); String[] pathTokens = pathinfo.split("/"); if (pathTokens.length >= 6) { // /files/<<endpointName>>/serviceTpe/fileName String endPointName = pathTokens[2]; this.endpoint = (Endpoint) EndpointFactory.getEndpoint(endPointName); if (!endpoint.isAllowClientAccess()) { throw new ServletException(StringUtil.format("Client access forbidden for the specified endpoint {0}", endPointName)); } operation = pathTokens[4]; getParameters(pathTokens); method = request.getMethod(); requestURI = getRequestURI(request.getMethod(), AuthUtil.INSTANCE.getAuthValue(endpoint), request.getParameterMap()); return; } throw new ServletException(StringUtil.format("Invalid url {0}", pathinfo)); } protected abstract void getParameters(String[] tokens) throws ServletException; @Override public void serviceProxy(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { initProxy(request, response); String smethod = request.getMethod(); DefaultHttpClient client = getClient(request, getSocketReadTimeout()); URI url = getRequestURI(request); HttpRequestBase method = createMethod(smethod, url, request); if (prepareForwardingMethod(method, request, client)) { if (smethod.equalsIgnoreCase("POST") || (smethod.equalsIgnoreCase("PUT"))) { FileItemFactory factory = new DiskFileItemFactory(); // Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory); // Parse the request @SuppressWarnings("unchecked") List<FileItem> fileItems = upload.parseRequest(request); if (fileItems.size() == 0) { writeErrorResponse(HttpServletResponse.SC_BAD_REQUEST, "No File Item found in Multipart Form data", new String[] {}, new String[] {}, response, request); return; } for (FileItem uploadedFile : fileItems) { InputStream uploadedFileContent = uploadedFile.getInputStream(); Map<String, String[]> params = request.getParameterMap() != null ? request.getParameterMap() : new HashMap<String, String[]>(); String contentType = uploadedFile.getContentType(); if (StringUtil.isEmpty(contentType) || MIMETYPE_UNKNOWN.equalsIgnoreCase(contentType)) { String fileExt = MIME.getFileExtension(parameters.get("FileName")); contentType = MIME.getMIMETypeFromExtension(fileExt); } Content content = new com.ibm.sbt.services.client.ClientService.ContentStream(uploadedFile.getName(), uploadedFileContent, uploadedFile.getSize(), contentType); Map<String, String> headers = createHeaders(); headers.put("Content-Type", contentType); xhr(request, response, url.getPath(), params, headers, content, getFormat()); } } else if (smethod.equalsIgnoreCase("GET")) { xhr(request, response, requestURI, new HashMap<String, String[]>(), new HashMap<String, String>(), null, ClientService.FORMAT_INPUTSTREAM); } } } catch (Exception e) { if (e instanceof ClientServicesException) { writeErrorResponse(((ClientServicesException) e).getResponseStatusCode(), "Unexpected Exception", new String[] { "exception" }, new String[] { e.toString() }, response, request); } else { writeErrorResponse("Unexpected Exception", new String[] { "exception" }, new String[] { e.toString() }, response, request); } } finally { termProxy(request, response); } } protected abstract Handler getFormat(); protected abstract Map<String, String> createHeaders(); protected abstract Content getFileContent(File file, long length, String name); private boolean addParameter(StringBuilder b, boolean first, String name, String value) throws ClientServicesException { try { if (value != null) { b.append(first ? '?' : '&'); b.append(name); b.append('='); b.append(URLEncoder.encode(value, "UTF-8")); return false; } return first; } catch (UnsupportedEncodingException ex) { throw new ClientServicesException(ex); } } private String composeRequestUrl(Args args, Map<String, String[]> parameters) throws ClientServicesException { // Compose the URL StringBuilder b = new StringBuilder(256); String baseUrl = endpoint.getUrl(); String serviceUrl = args.getServiceUrl(); String url = PathUtil.concat(baseUrl, serviceUrl, '/'); if (url.charAt(url.length() - 1) == '/') { url = url.substring(0, url.length() - 1); } b.append(url); if (parameters != null) { boolean first = true; for (Map.Entry<String, String[]> e : parameters.entrySet()) { String name = e.getKey(); if (StringUtil.isNotEmpty(name)) { String[] values = e.getValue(); for (String value : values) { first = addParameter(b, first, name, value); } } } } return b.toString(); } protected void forceAuthentication(Args args) throws ClientServicesException { if (endpoint != null) { endpoint.authenticate(true); } else { throw new NoAccessSignal(StringUtil.format("Authorization needed for service {0}", getUrlPath(args))); } } protected String getBaseUrl() { if (endpoint != null) { return endpoint.getUrl(); } return null; } protected String getUrlPath(Args args) { String baseUrl = getBaseUrl(); String serviceUrl = args.getServiceUrl(); return PathUtil.concat(baseUrl, serviceUrl, '/'); } private void xhr(HttpServletRequest request, HttpServletResponse response, String serviceUrl, Map<String, String[]> parameters, Map<String, String> headers, Content content, Handler format) throws ClientServicesException, ClientProtocolException, IOException, ServletException, URISyntaxException { Args args = new Args(); args.setServiceUrl(serviceUrl); args.setHandler(format); args.setHeaders(headers); String smethod = request.getMethod(); HttpRequestBase method = createMethod(smethod, new URI(composeRequestUrl(args, parameters)), request); DefaultHttpClient defaultHttpClient = new DefaultHttpClient(); if (endpoint.isForceTrustSSLCertificate()) { defaultHttpClient = SSLUtil.wrapHttpClient(defaultHttpClient); } if (endpoint.isForceDisableExpectedContinue()) { defaultHttpClient.getParams().setParameter( CoreProtocolPNames.USE_EXPECT_CONTINUE, false); } endpoint.initialize(defaultHttpClient); for (Map.Entry<String, String> e : args.getHeaders().entrySet()) { String headerName = e.getKey(); String headerValue = e.getValue(); method.addHeader(headerName, headerValue); } if (content != null) { content.initRequestContent(defaultHttpClient, method, args); } endpoint.updateHeaders(defaultHttpClient, method); //getting to interface to avoid //java.lang.NoSuchMethodError: org/apache/http/impl/client/DefaultHttpClient.execute(Lorg/apache/http/client/methods/HttpUriRequest;)Lorg/apache/http/client/methods/CloseableHttpResponse; //when run from different version of HttpClient (that's why it is deprecated) HttpClient httpClient = defaultHttpClient; HttpResponse clientResponse = httpClient.execute(method); if ("get".equalsIgnoreCase(smethod) && (clientResponse.getStatusLine().getStatusCode() == HttpServletResponse.SC_UNAUTHORIZED) || (endpoint != null && endpoint.getAuthenticationErrorCode() == clientResponse.getStatusLine().getStatusCode())) { forceAuthentication(args); } prepareResponse(method, request, response, clientResponse, true); } }