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

org.visallo.web.Router Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
package org.visallo.web;

import com.google.common.base.Joiner;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.v5analytics.webster.Handler;
import com.v5analytics.webster.handlers.StaticResourceHandler;
import org.json.JSONArray;
import org.json.JSONObject;
import org.visallo.core.exception.VisalloAccessDeniedException;
import org.visallo.core.exception.VisalloException;
import org.visallo.core.exception.VisalloResourceNotFoundException;
import org.visallo.core.geocoding.DefaultGeocoderRepository;
import org.visallo.core.geocoding.GeocoderRepository;
import org.visallo.core.trace.Trace;
import org.visallo.core.trace.TraceSpan;
import org.visallo.core.util.ServiceLoaderUtil;
import org.visallo.core.util.VisalloLogger;
import org.visallo.core.util.VisalloLoggerFactory;
import org.visallo.web.privilegeFilters.*;
import org.visallo.web.routes.Index;
import org.visallo.web.routes.admin.AdminList;
import org.visallo.web.routes.admin.AdminUploadOntology;
import org.visallo.web.routes.admin.PluginList;
import org.visallo.web.routes.config.Configuration;
import org.visallo.web.routes.dashboard.*;
import org.visallo.web.routes.directory.DirectoryGet;
import org.visallo.web.routes.directory.DirectorySearch;
import org.visallo.web.routes.edge.*;
import org.visallo.web.routes.element.ElementSearch;
import org.visallo.web.routes.longRunningProcess.LongRunningProcessById;
import org.visallo.web.routes.longRunningProcess.LongRunningProcessCancel;
import org.visallo.web.routes.longRunningProcess.LongRunningProcessDelete;
import org.visallo.web.routes.map.GetGeocoder;
import org.visallo.web.routes.map.MapzenTileProxy;
import org.visallo.web.routes.notification.Notifications;
import org.visallo.web.routes.notification.SystemNotificationDelete;
import org.visallo.web.routes.notification.SystemNotificationSave;
import org.visallo.web.routes.notification.UserNotificationMarkRead;
import org.visallo.web.routes.ontology.Ontology;
import org.visallo.web.routes.ping.Ping;
import org.visallo.web.routes.ping.PingStats;
import org.visallo.web.routes.resource.MapMarkerImage;
import org.visallo.web.routes.resource.ResourceExternalGet;
import org.visallo.web.routes.resource.ResourceGet;
import org.visallo.web.routes.search.SearchDelete;
import org.visallo.web.routes.search.SearchList;
import org.visallo.web.routes.search.SearchRun;
import org.visallo.web.routes.search.SearchSave;
import org.visallo.web.routes.user.*;
import org.visallo.web.routes.vertex.*;
import org.visallo.web.routes.workspace.*;

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.vertexium.util.IterableUtils.toList;

public class Router extends HttpServlet {
    private static final long serialVersionUID = 4689515508877380905L;
    private static final VisalloLogger LOGGER = VisalloLoggerFactory.getLogger(Router.class);

    /**
     * Copied from org.eclipse.jetty.server.Request.__MULTIPART_CONFIG_ELEMENT.
     * TODO: Examine why this is necessary and how it can be abstracted to any servlet container.
     */
    private static final String JETTY_MULTIPART_CONFIG_ELEMENT8 = "org.eclipse.multipartConfig";
    private static final String JETTY_MULTIPART_CONFIG_ELEMENT9 = "org.eclipse.jetty.multipartConfig";
    private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
    private static final String GRAPH_TRACE_ENABLE = "graphTraceEnable";
    private WebApp app;
    private org.visallo.core.config.Configuration configuration;
    private GeocoderRepository geocoderRepository;

    @SuppressWarnings("unchecked")
    public Router(ServletContext servletContext) {
        try {
            final Injector injector = (Injector) servletContext.getAttribute(Injector.class.getName());
            injector.injectMembers(this);

            app = new WebApp(servletContext, injector);

            AuthenticationHandler authenticatorInstance = new AuthenticationHandler();
            Class authenticator = AuthenticationHandler.class;

            Class csrfProtector = VisalloCsrfHandler.class;

            app.get("/", UserAgentFilter.class, csrfProtector, Index.class);
            app.get("/configuration", csrfProtector, Configuration.class);
            app.post("/logout", csrfProtector, Logout.class);

            app.get("/ontology", authenticator, csrfProtector, ReadPrivilegeFilter.class, Ontology.class);

            app.get("/notification/all", authenticator, csrfProtector, ReadPrivilegeFilter.class, Notifications.class);
            app.post("/notification/mark-read", authenticator, csrfProtector, ReadPrivilegeFilter.class, UserNotificationMarkRead.class);
            app.post("/notification/system", authenticator, csrfProtector, AdminPrivilegeFilter.class, SystemNotificationSave.class);
            app.delete("/notification/system", authenticator, csrfProtector, AdminPrivilegeFilter.class, SystemNotificationDelete.class);

            app.get("/resource", authenticator, csrfProtector, ReadPrivilegeFilter.class, ResourceGet.class);
            app.get("/resource/external", authenticator, csrfProtector, ReadPrivilegeFilter.class, ResourceExternalGet.class);
            app.get("/map/marker/image", csrfProtector, MapMarkerImage.class);  // TODO combine with /resource
            if (!(geocoderRepository instanceof DefaultGeocoderRepository)) {
                configuration.set(org.visallo.core.config.Configuration.WEB_GEOCODER_ENABLED, true);
                app.get("/map/geocode", authenticator, GetGeocoder.class);
            }
            if (configuration.get(org.visallo.core.config.Configuration.MAPZEN_TILE_API_KEY, null) == null) {
                LOGGER.warn("MapZen api key not found: %s", org.visallo.core.config.Configuration.MAPZEN_TILE_API_KEY);
            }
            app.get("/mapzen/{mapzenUri*}", authenticator, MapzenTileProxy.class);

            app.post("/search/save", authenticator, csrfProtector, SearchSave.class);
            app.get("/search/all", authenticator, csrfProtector, SearchList.class);
            app.get("/search/run", authenticator, csrfProtector, SearchRun.class);
            app.post("/search/run", authenticator, csrfProtector, SearchRun.class);
            app.delete("/search", authenticator, csrfProtector, SearchDelete.class);

            app.get("/element/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, ElementSearch.class);
            app.post("/element/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, ElementSearch.class);

            app.delete("/vertex", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexRemove.class);
            app.get("/vertex/highlighted-text", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexHighlightedText.class);
            app.get("/vertex/raw", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexRaw.class);
            app.get("/vertex/exists", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexExists.class);
            app.post("/vertex/exists", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexExists.class);
            app.get("/vertex/thumbnail", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexThumbnail.class);
            app.get("/vertex/poster-frame", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexPosterFrame.class);
            app.get("/vertex/video-preview", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexVideoPreviewImage.class);
            app.get("/vertex/details", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexDetails.class);
            app.get("/vertex/history", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetHistory.class);
            app.get("/vertex/property/details", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexPropertyDetails.class);
            app.post("/vertex/import", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexImport.class);
            app.post("/vertex/resolve-term", authenticator, csrfProtector, EditPrivilegeFilter.class, ResolveTermEntity.class);
            app.post("/vertex/unresolve-term", authenticator, csrfProtector, EditPrivilegeFilter.class, UnresolveTermEntity.class);
            app.post("/vertex/resolve-detected-object", authenticator, csrfProtector, EditPrivilegeFilter.class, ResolveDetectedObject.class);
            app.post("/vertex/unresolve-detected-object", authenticator, csrfProtector, EditPrivilegeFilter.class, UnresolveDetectedObject.class);
            app.get("/vertex/detected-objects", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetDetectedObjects.class);
            app.get("/vertex/property", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetPropertyValue.class);
            app.get("/vertex/property/history", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetPropertyHistory.class);
            app.post("/vertex/property", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexSetProperty.class);
            app.post("/vertex/property/visibility", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexSetPropertyVisibility.class);
            app.post("/vertex/comment", authenticator, csrfProtector, CommentPrivilegeFilter.class, VertexSetProperty.class);
            app.delete("/vertex/property", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexDeleteProperty.class);
            app.delete("/vertex/comment", authenticator, csrfProtector, CommentPrivilegeFilter.class, VertexDeleteProperty.class);
            app.get("/vertex/term-mentions", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetTermMentions.class);
            app.get("/vertex/resolved-to", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetResolvedTo.class);
            app.post("/vertex/visibility", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexSetVisibility.class);
            app.get("/vertex/properties", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexProperties.class);
            app.get("/vertex/edges", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexEdges.class);
            app.post("/vertex/multiple", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexMultiple.class); // this is a post method to allow large data (ie data larger than would fit in the URL)
            app.post("/vertex/new", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexNew.class);
            app.get("/vertex/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexSearch.class);
            app.post("/vertex/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexSearch.class);
            app.get("/vertex/geo-search", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGeoSearch.class);
            app.post("/vertex/upload-image", authenticator, csrfProtector, EditPrivilegeFilter.class, VertexUploadImage.class);
            app.get("/vertex/find-path", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexFindPath.class);
            app.post("/vertex/find-related", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexFindRelated.class);
            app.get("/vertex/counts-by-concept-type", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetCountsByConceptType.class);
            app.get("/vertex/count", authenticator, csrfProtector, ReadPrivilegeFilter.class, VertexGetCount.class);

            app.post("/edge/property", authenticator, csrfProtector, EditPrivilegeFilter.class, EdgeSetProperty.class);
            app.post("/edge/property/visibility", authenticator, csrfProtector, EditPrivilegeFilter.class, EdgeSetPropertyVisibility.class);
            app.post("/edge/comment", authenticator, csrfProtector, CommentPrivilegeFilter.class, EdgeSetProperty.class);
            app.delete("/edge", authenticator, csrfProtector, EditPrivilegeFilter.class, EdgeDelete.class);
            app.delete("/edge/property", authenticator, csrfProtector, EditPrivilegeFilter.class, EdgeDeleteProperty.class);
            app.delete("/edge/comment", authenticator, csrfProtector, CommentPrivilegeFilter.class, EdgeDeleteProperty.class);
            app.get("/edge/history", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeGetHistory.class);
            app.get("/edge/property/history", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeGetPropertyHistory.class);
            app.get("/edge/exists", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeExists.class);
            app.post("/edge/exists", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeExists.class);
            app.post("/edge/multiple", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeMultiple.class);
            app.post("/edge/create", authenticator, csrfProtector, EditPrivilegeFilter.class, EdgeCreate.class);
            app.get("/edge/properties", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeProperties.class);
            app.post("/edge/visibility", authenticator, csrfProtector, EditPrivilegeFilter.class, EdgeSetVisibility.class);
            app.get("/edge/property/details", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgePropertyDetails.class);
            app.get("/edge/details", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeDetails.class);
            app.get("/edge/count", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeGetCount.class);
            app.get("/edge/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeSearch.class);
            app.post("/edge/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, EdgeSearch.class);

            app.get("/workspace/all", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceList.class);
            app.post("/workspace/create", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceCreate.class);
            app.get("/workspace/diff", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceDiff.class);
            app.get("/workspace/edges", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceEdges.class);
            app.post("/workspace/edges", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceEdges.class); // this is a post method to allow large data (ie data larger than would fit in the URL)
            app.get("/workspace/vertices", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceVertices.class);
            app.post("/workspace/update", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceUpdate.class);
            app.get("/workspace", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceById.class);
            app.delete("/workspace", authenticator, csrfProtector, ReadPrivilegeFilter.class, WorkspaceDelete.class);
            app.post("/workspace/publish", authenticator, csrfProtector, PublishPrivilegeFilter.class, WorkspacePublish.class);
            app.post("/workspace/undo", authenticator, csrfProtector, EditPrivilegeFilter.class, WorkspaceUndo.class);

            app.get("/dashboard/all", authenticator, csrfProtector, ReadPrivilegeFilter.class, DashboardAll.class);
            app.post("/dashboard", authenticator, csrfProtector, ReadPrivilegeFilter.class, DashboardUpdate.class);
            app.delete("/dashboard", authenticator, csrfProtector, ReadPrivilegeFilter.class, DashboardDelete.class);
            app.post("/dashboard/item", authenticator, csrfProtector, ReadPrivilegeFilter.class, DashboardItemUpdate.class);
            app.delete("/dashboard/item", authenticator, csrfProtector, ReadPrivilegeFilter.class, DashboardItemDelete.class);

            app.get("/user/me", authenticator, csrfProtector, MeGet.class);
            app.post("/user/ui-preferences", authenticator, csrfProtector, UserSetUiPreferences.class);
            app.get("/user/all", authenticator, csrfProtector, UserList.class);
            app.post("/user/all", authenticator, csrfProtector, UserList.class);
            app.get("/user", authenticator, csrfProtector, AdminPrivilegeFilter.class, UserGet.class);

            app.get("/directory/get", authenticator, csrfProtector, DirectoryGet.class);
            app.get("/directory/search", authenticator, csrfProtector, DirectorySearch.class);

            app.get("/long-running-process", authenticator, csrfProtector, LongRunningProcessById.class);
            app.delete("/long-running-process", authenticator, csrfProtector, LongRunningProcessDelete.class);
            app.post("/long-running-process/cancel", authenticator, csrfProtector, LongRunningProcessCancel.class);

            app.get("/admin/all", authenticator, csrfProtector, AdminPrivilegeFilter.class, AdminList.class);
            app.get("/admin/plugins", authenticator, csrfProtector, AdminPrivilegeFilter.class, PluginList.class);
            app.post("/admin/upload-ontology", authenticator, csrfProtector, AdminPrivilegeFilter.class, AdminUploadOntology.class);

            app.get("/ping", RateLimitFilter.class, Ping.class);
            app.get("/ping/stats", authenticator, AdminPrivilegeFilter.class, PingStats.class);

            List webAppPlugins = toList(ServiceLoaderUtil.load(WebAppPlugin.class, configuration));
            for (WebAppPlugin webAppPlugin : webAppPlugins) {
                LOGGER.info("Loading webapp plugin: %s", webAppPlugin.getClass().getName());
                try {
                    webAppPlugin.init(app, servletContext, authenticatorInstance);
                } catch (Exception e) {
                    throw new VisalloException("Could not initialize webapp plugin: " + webAppPlugin.getClass().getName(), e);
                }
            }

            app.get("/css/images/ui-icons_222222_256x240.png",
                    new StaticResourceHandler(
                            this.getClass(),
                            "/org/visallo/web/routes/resource/ui-icons_222222_256x240.png",
                            "image/png")
            );

            app.onException(VisalloAccessDeniedException.class, new ErrorCodeHandler(HttpServletResponse.SC_FORBIDDEN));
        } catch (Exception ex) {
            LOGGER.error("Failed to initialize Router", ex);
            throw new RuntimeException("Failed to initialize " + getClass().getName(), ex);
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        LOGGER.debug("servicing %s", request.getRequestURI());
        TraceSpan trace = null;
        CurrentUser.setUserInLogMappedDiagnosticContexts(request);
        try {
            if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
                request.setAttribute(JETTY_MULTIPART_CONFIG_ELEMENT8, MULTI_PART_CONFIG);
                request.setAttribute(JETTY_MULTIPART_CONFIG_ELEMENT9, MULTI_PART_CONFIG);
            }

            if (isGraphTraceEnabled(request)) {
                String traceDescription = request.getRequestURI();
                Map parameters = new HashMap<>();
                for (Map.Entry reqParameters : request.getParameterMap().entrySet()) {
                    parameters.put(reqParameters.getKey(), Joiner.on(", ").join(reqParameters.getValue()));
                }
                trace = Trace.on(traceDescription, parameters);
            }

            response.addHeader("Accept-Ranges", "bytes");
            app.handle(request, response);
        } catch (ConnectionClosedException cce) {
            LOGGER.debug("Connection closed by client", cce);
        } catch (Throwable e) {
            handleException(request, response, e);
        } finally {
            if (trace != null) {
                trace.close();
            }
            Trace.off();
            CurrentUser.clearUserFromLogMappedDiagnosticContexts();
        }
    }

    private void handleException(HttpServletRequest request, HttpServletResponse response, Throwable e)
            throws IOException, ServletException {
        if (e.getCause() instanceof VisalloResourceNotFoundException) {
            handleNotFound(response, (VisalloResourceNotFoundException) e.getCause());
            return;
        }
        if (e.getCause() instanceof BadRequestException) {
            handleBadRequest(response, (BadRequestException) e.getCause());
            return;
        }
        if (e.getCause() instanceof VisalloAccessDeniedException) {
            handleAccessDenied(response, (VisalloAccessDeniedException) e.getCause());
            return;
        }

        String message = String.format("Unhandled exception for %s %s", request.getMethod(), request.getRequestURI());
        if (app.isDevModeEnabled()) {
            throw new ServletException(message, e);
        } else {
            LOGGER.warn(message, e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }

    private void handleAccessDenied(HttpServletResponse response, VisalloAccessDeniedException accessDenied) throws IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDenied.getMessage());
    }

    private void handleNotFound(HttpServletResponse response, VisalloResourceNotFoundException notFoundException) throws IOException {
        response.sendError(HttpServletResponse.SC_NOT_FOUND, notFoundException.getMessage());
    }

    private void handleBadRequest(HttpServletResponse response, BadRequestException badRequestException) {
        LOGGER.error("bad request", badRequestException);
        JSONObject error = new JSONObject();
        error.put(badRequestException.getParameterName(), badRequestException.getMessage());
        if (badRequestException.getInvalidValues() != null) {
            JSONArray values = new JSONArray();
            for (String v : badRequestException.getInvalidValues()) {
                values.put(v);
            }
            error.put("invalidValues", values);
        }
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        VisalloResponse.configureResponse(ResponseTypes.JSON_OBJECT, response, error);
    }

    private boolean isGraphTraceEnabled(ServletRequest req) {
        return req.getParameter(GRAPH_TRACE_ENABLE) != null || req instanceof HttpServletRequest && ((HttpServletRequest) req).getHeader(GRAPH_TRACE_ENABLE) != null;
    }

    @Inject
    public void setConfiguration(org.visallo.core.config.Configuration configuration) {
        this.configuration = configuration;
    }

    @Inject
    public void setGeocoderRepository(GeocoderRepository geocoderRepository) {
        this.geocoderRepository = geocoderRepository;
    }

    public WebApp getApp() {
        return app;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy