/******************************************************************************* * Copyright (c) 2012-2016 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.everrest.core.impl.async; import org.everrest.core.ApplicationContext; import org.everrest.core.GenericContainerRequest; import org.everrest.core.impl.ProviderBinder; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Providers; import java.security.Principal; import java.util.ArrayList; import java.util.List; /** * Service to get results of invocation asynchronous job from {@link AsynchronousJobPool}. Instance of AsynchronousJobPool obtained in * in this class via mechanism of injections. This resource must always be deployed as per-request resource. * * @author andrew00x */ @Path("async") public class AsynchronousJobService { @Context private Providers providers; @GET @Path("{job}") public Object get(@PathParam("job") Long jobId, @Context UriInfo uriInfo, @Context SecurityContext securityContext) { final AsynchronousJobPool pool = getJobPool(); final AsynchronousJob job = pool.getJob(jobId); if (job == null) { throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND) .entity(String.format("Job %d not found. ", jobId)) .type(MediaType.TEXT_PLAIN).build()); } // Get original request which initialize asynchronous job. final GenericContainerRequest request = (GenericContainerRequest)job.getContext().get("org.everrest.async.request"); if (securityContext.isUserInRole("administrators") || principalMatched(request.getUserPrincipal(), securityContext.getUserPrincipal())) { if (job.isDone()) { Object result; try { result = job.getResult(); } finally { pool.removeJob(jobId); // Restore resource specific set of providers. ApplicationContext.getCurrent().setProviders((ProviderBinder)job.getContext().get("org.everrest.async.providers")); } // This response will be sent to client side. Response response; if (result == null || result.getClass() == void.class || result.getClass() == Void.class) { response = Response.noContent().build(); } else if (Response.class.isAssignableFrom(result.getClass())) { response = (Response)result; if (response.getEntity() != null && response.getMetadata().getFirst(HttpHeaders.CONTENT_TYPE) == null) { MediaType contentType = request.getAcceptableMediaType(job.getResourceMethod().produces()); response.getMetadata().putSingle(HttpHeaders.CONTENT_TYPE, contentType); } } else { MediaType contentType = request.getAcceptableMediaType(job.getResourceMethod().produces()); response = Response.ok(result, contentType).build(); } // Result of job. Client get this response. ApplicationContext.getCurrent().getContainerResponse().setResponse(response); // This response (204) never sent to client side. return null; } else { final String jobUri = uriInfo.getRequestUri().toString(); return Response.status(Response.Status.ACCEPTED) .header(HttpHeaders.LOCATION, jobUri) .entity(jobUri) .type(MediaType.TEXT_PLAIN).build(); } } else { throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN) .entity(String.format("GET: (%d) - Operation not permitted. ", jobId)) .type(MediaType.TEXT_PLAIN).build()); } } @GET @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public GenericEntity<List<AsynchronousProcess>> list() { AsynchronousJobPool pool = getJobPool(); List<AsynchronousJob> jobs = pool.getAll(); List<AsynchronousProcess> processes = new ArrayList<>(jobs.size()); for (AsynchronousJob job : jobs) { GenericContainerRequest request = (GenericContainerRequest)job.getContext().get("org.everrest.async.request"); Principal principal = request.getUserPrincipal(); processes.add(new AsynchronousProcess( principal != null ? principal.getName() : null, job.getJobId(), request.getRequestUri().getPath(), job.isDone() ? "done" : "running")); } return new GenericEntity<List<AsynchronousProcess>>(processes) { }; } @DELETE @Path("{job}") public void remove(@PathParam("job") Long jobId, @Context SecurityContext securityContext) { AsynchronousJobPool pool = getJobPool(); AsynchronousJob job = pool.getJob(jobId); if (job == null) { throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND) .entity(String.format("Job %d not found. ", jobId)) .type(MediaType.TEXT_PLAIN).build()); } if (securityContext.isUserInRole("administrators") || principalMatched(((GenericContainerRequest)job.getContext().get("org.everrest.async.request")).getUserPrincipal(), securityContext.getUserPrincipal())) { pool.removeJob(jobId); } else { throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN) .entity(String.format("DELETE: (%d) - Operation not permitted. ", jobId)) .type(MediaType.TEXT_PLAIN).build()); } } private boolean principalMatched(Principal principal1, Principal principal2) { if (principal1 == null) { return true; } else { if (principal2 != null) { String name1 = principal1.getName(); String name2 = principal2.getName(); if (name1 == null && name2 == null || name1 != null && name1.equals(name2)) { return true; } } return false; } } private AsynchronousJobPool getJobPool() { if (providers != null) { ContextResolver<AsynchronousJobPool> asyncJobsResolver = providers.getContextResolver(AsynchronousJobPool.class, null); if (asyncJobsResolver != null) { return asyncJobsResolver.getContext(null); } } throw new IllegalStateException("Asynchronous jobs feature is not configured properly. "); } }