Java tutorial
/* * Copyright 2016 Danish Maritime Authority. * * 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.niord.web; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.jboss.resteasy.annotations.GZIP; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.security.annotation.SecurityDomain; import org.niord.core.batch.BatchService; import org.niord.core.batch.BatchSetService; import org.niord.core.batch.vo.BatchInstanceVo; import org.niord.core.batch.vo.BatchStatusVo; import org.niord.core.repo.FileTypes; import org.niord.core.repo.IconSize; import org.niord.core.repo.RepositoryService; import org.niord.core.user.Roles; import org.niord.core.user.UserService; import org.niord.model.IJsonSerializable; import org.niord.model.search.PagedSearchResultVo; import org.slf4j.Logger; import javax.annotation.security.PermitAll; import javax.annotation.security.RolesAllowed; import javax.ejb.Stateless; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.HashMap; import java.util.List; /** * API for accessing the batch functionality */ @Path("/batch") @Stateless @SecurityDomain("keycloak") public class BatchRestService { @Inject Logger log; @Inject BatchService batchService; @Inject BatchSetService batchSetService; @Inject FileTypes fileTypes; @Inject UserService userService; @Inject RepositoryService repositoryService; /** * Returns the job names * @return the job names */ @GET @Path("/job-names") @Produces("application/json;charset=UTF-8") @GZIP @NoCache @PermitAll public List<String> getJobNames() { return batchService.getJobNames(); } /** * Stops the given batch job execution * * @param executionId the execution ID */ @PUT @Path("/execution/{executionId}/stop") @NoCache @RolesAllowed(Roles.ADMIN) public void stopExecution(@PathParam("executionId") long executionId) { batchService.stopExecution(executionId); } /** * Restarts the given batch job execution * * @param executionId the execution ID */ @PUT @Path("/execution/{executionId}/restart") @NoCache @RolesAllowed(Roles.ADMIN) public long restartExecution(@PathParam("executionId") long executionId) { return batchService.restartExecution(executionId); } /** * Abandons the given batch job execution * * @param executionId the execution ID */ @PUT @Path("/execution/{executionId}/abandon") @NoCache @RolesAllowed(Roles.ADMIN) public void abandonExecution(@PathParam("executionId") long executionId) { batchService.abandonExecution(executionId); } /** * Returns the paged search result for the given batch type * @param jobName the job name * @param page the page index of the paged search result * @param pageSize the max number of instances per page * @return the paged search result for the given batch type */ @GET @Path("/{jobName}/instances") @Produces("application/json;charset=UTF-8") @GZIP @NoCache @RolesAllowed(Roles.ADMIN) public PagedSearchResultVo<BatchInstanceVo> getJobInstances(@PathParam("jobName") String jobName, @QueryParam("page") @DefaultValue("0") int page, @QueryParam("pageSize") @DefaultValue("10") int pageSize) throws Exception { return batchService.getJobInstances(jobName, page * pageSize, pageSize); } /** * Returns the status of the batch job system * * NB: Since this gets called every 10 seconds, adding a @RolesAllowed(Roles.ADMIN) has * the potential of filling up the server log. * Instead, we allow all to call the endpoint, but return an empty result for non-admin users. * * @return the status of the batch job system */ @GET @Path("/status") @Produces("application/json;charset=UTF-8") @GZIP @NoCache @PermitAll public BatchStatusVo getStatus() { if (!userService.isCallerInRole(Roles.ADMIN)) { return new BatchStatusVo(); } return batchService.getStatus(); } /** * Downloads the batch data associated with the given instance. * First, the callee must call the "/rest/tickets/ticket?role=admin" endpoint using Ajax * to retrieve a valid ticket, and then pass the ticket along in this function * * @param instanceId the instance ID */ @GET @Path("/instance/{instanceId}/download/{fileName:.*}") @PermitAll public Response downloadBatchDataFile(@PathParam("instanceId") long instanceId, @PathParam("fileName") String fileName) { // Check the ticket programmatically if (!userService.isCallerInRole(Roles.ADMIN)) { throw new WebApplicationException(403); } java.nio.file.Path f = batchService.getBatchJobDataFile(instanceId); if (f == null) { log.warn("Failed streaming batch file: " + fileName); return Response.status(HttpServletResponse.SC_NOT_FOUND) .entity("Failed streaming batch file: " + fileName).build(); } // Set expiry to 12 hours Date expirationDate = new Date(System.currentTimeMillis() + 1000L * 60L * 60L * 12L); String mt = fileTypes.getContentType(f); log.trace("Streaming file: " + f); return Response.ok(f.toFile(), mt).expires(expirationDate).build(); } /** * Returns the thumbnail to use for the given batch job instance * @param size the icon size, either 32, 64 or 128 * @return the thumbnail to use for the given batch job instance */ @GET @Path("/instance/{instanceId}/thumbnail.png") @PermitAll public Response getBatchDataFileThumbnail(@PathParam("instanceId") long instanceId, @QueryParam("size") @DefaultValue("32") int size) throws IOException, URISyntaxException { IconSize iconSize = IconSize.getIconSize(size); java.nio.file.Path f = batchService.getBatchJobDataFile(instanceId); // Set expiry to 12 hours Date expirationDate = new Date(System.currentTimeMillis() + 1000L * 60L * 60L * 12L); // NB: Response.temporaryRedirect() uses "/rest" as the root. String thumbUri = ".." + fileTypes.getIcon(f, iconSize); log.trace("Redirecting to thumbnail: " + thumbUri); return Response.temporaryRedirect(new URI(thumbUri)).expires(expirationDate).build(); } /** * Returns the names of log files for the batch job with the given instance. * @param instanceId the instance ID */ @GET @Path("/instance/{instanceId}/logs") @GZIP @NoCache @RolesAllowed(Roles.ADMIN) public List<String> getBatchJobLogFiles(@PathParam("instanceId") long instanceId) throws IOException { return batchService.getBatchJobLogFiles(instanceId); } /** * Returns the contents of the batch job log file with the given name. * * @param instanceId the instance id * @param logFileName the log file name * @param fromLineNo if specified, only the subsequent lines are returned * @return the contents of the log file */ @GET @Path("/instance/{instanceId}/logs/{logFileName}") @Produces("text/plain") @GZIP @NoCache @RolesAllowed(Roles.ADMIN) public String getBatchJobLogFileContent(@PathParam("instanceId") long instanceId, @PathParam("logFileName") String logFileName, @QueryParam("fromLineNo") Integer fromLineNo) throws IOException { return batchService.getBatchJobLogFile(instanceId, logFileName, fromLineNo); } /** * Imports an uploaded messages zip archive * * @param request the servlet request * @return a status */ @POST @Path("/execute-batch-set") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces("text/plain") @RolesAllowed(Roles.SYSADMIN) public String executeBatchSet(@Context HttpServletRequest request) throws Exception { StringBuilder txt = new StringBuilder(); List<FileItem> items = repositoryService.parseFileUploadRequest(request); // Start the batch job for each file item items.stream().filter(item -> !item.isFormField()).forEach(item -> { try { txt.append("Processing batch-set zip-file: ").append(item.getName()).append("\n"); batchSetService.extractAndExecuteBatchSetArchive(item.getInputStream(), txt); } catch (Exception e) { String errorMsg = "Error processing batch-set zip-file " + item.getName() + ": " + e.getMessage(); log.error(errorMsg, e); txt.append(errorMsg); } }); return txt.toString(); } /** * Executes the given javascript via the "script-executor" batch job * * NB: Important to only let sysadmin access this operation. * * @param params the javascript parameter */ @POST @Path("/execute-javascript") @Consumes("application/json;charset=UTF-8") @Produces("text/plain") @RolesAllowed(Roles.SYSADMIN) public String executeJavaScript(ExecuteJavaScriptParams params) throws Exception { if (StringUtils.isNotBlank(params.getJavaScript())) { log.warn("User " + userService.currentUser().getName() + " scheduled execution of JavaScript:\n" + params.getJavaScript()); batchService.startBatchJobWithDataFile("script-executor", IOUtils.toInputStream(params.getJavaScript(), "UTF-8"), StringUtils.defaultIfBlank(params.getScriptName(), "javascript.js"), new HashMap<>()); } return "OK"; } /** * Used when executing a back-end JavaScript */ @SuppressWarnings("unused") public static class ExecuteJavaScriptParams implements IJsonSerializable { String scriptName; String javaScript; public String getScriptName() { return scriptName; } public void setScriptName(String scriptName) { this.scriptName = scriptName; } public String getJavaScript() { return javaScript; } public void setJavaScript(String javaScript) { this.javaScript = javaScript; } } }