org.eclipse.orion.internal.server.servlets.file.FileHandlerV1.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.orion.internal.server.servlets.file.FileHandlerV1.java

Source

/*******************************************************************************
 * Copyright (c) 2010, 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.orion.internal.server.servlets.file;

import java.io.*;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.provider.FileInfo;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.orion.internal.server.core.IOUtilities;
import org.eclipse.orion.internal.server.servlets.ProtocolConstants;
import org.eclipse.orion.internal.server.servlets.ServletResourceHandler;
import org.eclipse.orion.server.core.ServerStatus;
import org.eclipse.orion.server.core.resources.UniversalUniqueIdentifier;
import org.eclipse.orion.server.servlets.JsonURIUnqualificationStrategy;
import org.eclipse.orion.server.servlets.OrionServlet;
import org.eclipse.osgi.util.NLS;
import org.json.*;

/**
 * Handles files in version 1 of eclipse web protocol syntax.
 */
class FileHandlerV1 extends GenericFileHandler {
    final ServletResourceHandler<IStatus> statusHandler;

    FileHandlerV1(ServletResourceHandler<IStatus> statusHandler, ServletContext context) {
        super(context);
        this.statusHandler = statusHandler;
    }

    /**
     * The end of line sequence expected by HTTP.
     */
    private static final String EOL = "\r\n"; //$NON-NLS-1$

    // responseWriter is used, as in some cases response should be
    // appended to response generated earlier (i.e. multipart get)
    protected void handleGetMetadata(HttpServletRequest request, HttpServletResponse response,
            Writer responseWriter, IFileStore file)
            throws IOException, NoSuchAlgorithmException, JSONException, CoreException {
        JSONObject result = ServletFileStoreHandler.toJSON(file, file.fetchInfo(EFS.NONE, null), getURI(request));
        String etag = generateFileETag(file);
        result.put(ProtocolConstants.KEY_ETAG, etag);
        response.setHeader(ProtocolConstants.KEY_ETAG, etag);
        response.setHeader("Cache-Control", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$
        OrionServlet.decorateResponse(request, result, JsonURIUnqualificationStrategy.ALL);
        responseWriter.append(result.toString());
    }

    private void handleMultiPartGet(HttpServletRequest request, HttpServletResponse response, IFileStore file)
            throws IOException, CoreException, NoSuchAlgorithmException, JSONException {
        String boundary = createBoundaryString();
        response.setHeader(ProtocolConstants.HEADER_ACCEPT_PATCH, ProtocolConstants.CONTENT_TYPE_JSON_PATCH);
        response.setHeader(ProtocolConstants.HEADER_CONTENT_TYPE,
                "multipart/related; boundary=\"" + boundary + '"'); //$NON-NLS-1$
        OutputStream outputStream = response.getOutputStream();
        Writer out = new OutputStreamWriter(outputStream);
        out.write("--" + boundary + EOL); //$NON-NLS-1$
        out.write("Content-Type: application/json" + EOL + EOL); //$NON-NLS-1$
        handleGetMetadata(request, response, out, file);
        out.write(EOL + "--" + boundary + EOL); //$NON-NLS-1$
        // headers for file contents go here
        out.write(EOL);
        out.flush();
        IOUtilities.pipe(file.openInputStream(EFS.NONE, null), outputStream, true, false);
        out.write(EOL + "--" + boundary + EOL); //$NON-NLS-1$
        out.flush();
    }

    String createBoundaryString() {
        return new UniversalUniqueIdentifier().toBase64String();
    }

    private void handlePutContents(HttpServletRequest request, BufferedReader requestReader,
            HttpServletResponse response, IFileStore file)
            throws IOException, CoreException, NoSuchAlgorithmException, JSONException {
        String source = request.getParameter(ProtocolConstants.PARM_SOURCE);
        if (source != null) {
            //if source is specified, read contents from different URL rather than from this request stream
            IOUtilities.pipe(new URL(source).openStream(), file.openOutputStream(EFS.NONE, null), true, true);
        } else {
            //read from the request stream
            Writer fileWriter = new BufferedWriter(
                    new OutputStreamWriter(file.openOutputStream(EFS.NONE, null), "UTF-8"));
            IOUtilities.pipe(requestReader, fileWriter, false, true);
        }

        // return metadata with the new Etag
        handleGetMetadata(request, response, response.getWriter(), file);
    }

    private void handlePatchContents(HttpServletRequest request, BufferedReader requestReader,
            HttpServletResponse response, IFileStore file)
            throws IOException, CoreException, NoSuchAlgorithmException, JSONException, ServletException {
        JSONObject changes = OrionServlet.readJSONRequest(request);
        //read file to memory
        Reader fileReader = new InputStreamReader(file.openInputStream(EFS.NONE, null));
        StringWriter oldFile = new StringWriter();
        IOUtilities.pipe(fileReader, oldFile, true, false);
        StringBuffer oldContents = oldFile.getBuffer();

        JSONArray changeList = changes.getJSONArray("diff");
        for (int i = 0; i < changeList.length(); i++) {
            JSONObject change = changeList.getJSONObject(i);
            long start = change.getLong("start");
            long end = change.getLong("end");
            String text = change.getString("text");
            oldContents.replace((int) start, (int) end, text);
        }

        String newContents = oldContents.toString();
        boolean failed = false;
        if (changes.has("contents")) {
            String contents = changes.getString("contents");
            if (!newContents.equals(contents)) {
                failed = true;
                newContents = contents;
            }
        }
        Writer fileWriter = new OutputStreamWriter(file.openOutputStream(EFS.NONE, null), "UTF-8");
        IOUtilities.pipe(new StringReader(newContents), fileWriter, false, true);
        if (failed) {
            statusHandler.handleRequest(request, response,
                    new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_ACCEPTABLE,
                            "Bad File Diffs. Please paste this content in a bug report: \u00A0\u00A0    "
                                    + changes.toString(),
                            null));
            return;
        }

        // return metadata with the new Etag
        handleGetMetadata(request, response, response.getWriter(), file);
    }

    private void handleMultiPartPut(HttpServletRequest request, HttpServletResponse response, IFileStore file)
            throws IOException, CoreException, JSONException, NoSuchAlgorithmException {
        String typeHeader = request.getHeader(ProtocolConstants.HEADER_CONTENT_TYPE);
        String boundary = typeHeader.substring(typeHeader.indexOf("boundary=\"") + 10, typeHeader.length() - 1); //$NON-NLS-1$
        BufferedReader requestReader = request.getReader();
        handlePutMetadata(requestReader, boundary, file);
        // next come the headers for the content
        Map<String, String> contentHeaders = new HashMap<String, String>();
        String line;
        while ((line = requestReader.readLine()) != null && line.length() > 0) {
            String[] header = line.split(":"); //$NON-NLS-1$
            if (header.length == 2)
                contentHeaders.put(header[0], header[1]);
        }
        // now for the file contents
        handlePutContents(request, requestReader, response, file);
    }

    private void handlePutMetadata(BufferedReader reader, String boundary, IFileStore file)
            throws IOException, CoreException, JSONException {
        StringBuffer buf = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null && !line.equals(boundary))
            buf.append(line);
        //merge with existing metadata
        FileInfo info = (FileInfo) file.fetchInfo(EFS.NONE, null);
        ServletFileStoreHandler.copyJSONToFileInfo(new JSONObject(buf.toString()), info);
        file.putInfo(info, EFS.SET_ATTRIBUTES, null);
    }

    @Override
    public boolean handleRequest(HttpServletRequest request, HttpServletResponse response, IFileStore file)
            throws ServletException {
        try {
            String receivedETag = request.getHeader(ProtocolConstants.HEADER_IF_MATCH);
            if (receivedETag != null && !receivedETag.equals(generateFileETag(file))) {
                response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
                return true;
            }
            String parts = IOUtilities.getQueryParameter(request, "parts");
            if (parts == null || "body".equals(parts)) { //$NON-NLS-1$
                switch (getMethod(request)) {
                case DELETE:
                    file.delete(EFS.NONE, null);
                    break;
                case PUT:
                    handlePutContents(request, request.getReader(), response, file);
                    break;
                case POST:
                    if ("PATCH".equals(request.getHeader(ProtocolConstants.HEADER_METHOD_OVERRIDE))) {
                        handlePatchContents(request, request.getReader(), response, file);
                    }
                    break;
                default:
                    return handleFileContents(request, response, file);
                }
                return true;
            }
            if ("meta".equals(parts)) { //$NON-NLS-1$
                switch (getMethod(request)) {
                case GET:
                    response.setCharacterEncoding("UTF-8");
                    handleGetMetadata(request, response, response.getWriter(), file);
                    return true;
                case PUT:
                    handlePutMetadata(request.getReader(), null, file);
                    response.setStatus(HttpServletResponse.SC_NO_CONTENT);
                    return true;
                }
                return false;
            }
            if ("meta,body".equals(parts) || "body,meta".equals(parts)) { //$NON-NLS-1$ //$NON-NLS-2$
                switch (getMethod(request)) {
                case GET:
                    handleMultiPartGet(request, response, file);
                    return true;
                case PUT:
                    handleMultiPartPut(request, response, file);
                    return true;
                }
                return false;
            }
        } catch (JSONException e) {
            return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR,
                    HttpServletResponse.SC_BAD_REQUEST, "Syntax error in request", e));
        } catch (Exception e) {
            if (!handleAuthFailure(request, response, e))
                throw new ServletException(NLS.bind("Error retrieving file: {0}", file), e);
        }
        return false;
    }
}