Java tutorial
/* ************************************************************************ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNES ASTRONOMIQUES ************** * * (c) 2016. (c) 2016. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 * All rights reserved Tous droits rservs * * NRC disclaims any warranties, Le CNRC dnie toute garantie * expressed, implied, or nonce, implicite ou lgale, * statutory, of any kind with de quelque nature que ce * respect to the software, soit, concernant le logiciel, * including without limitation y compris sans restriction * any warranty of merchantability toute garantie de valeur * or fitness for a particular marchande ou de pertinence * purpose. NRC shall not be pour un usage particulier. * liable in any event for any Le CNRC ne pourra en aucun cas * damages, whether direct or tre tenu responsable de tout * indirect, special or general, dommage, direct ou indirect, * consequential or incidental, particulier ou gnral, * arising from the use of the accessoire ou fortuit, rsultant * software. Neither the name de l'utilisation du logiciel. Ni * of the National Research le nom du Conseil National de * Council of Canada nor the Recherches du Canada ni les noms * names of its contributors may de ses participants ne peuvent * be used to endorse or promote tre utiliss pour approuver ou * products derived from this promouvoir les produits drivs * software without specific prior de ce logiciel sans autorisation * written permission. pralable et particulire * par crit. * * This file is part of the Ce fichier fait partie du projet * OpenCADC project. OpenCADC. * * OpenCADC is free software: OpenCADC est un logiciel libre ; * you can redistribute it and/or vous pouvez le redistribuer ou le * modify it under the terms of modifier suivant les termes de * the GNU Affero General Public la GNU Affero General Public * License as published by the License? telle que publie * Free Software Foundation, par la Free Software Foundation * either version 3 of the : soit la version 3 de cette * License, or (at your option) licence, soit ( votre gr) * any later version. toute version ultrieure. * * OpenCADC is distributed in the OpenCADC est distribu * hope that it will be useful, dans lespoir quil vous * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE * without even the implied GARANTIE : sans mme la garantie * warranty of MERCHANTABILITY implicite de COMMERCIALISABILIT * or FITNESS FOR A PARTICULAR ni dADQUATION UN OBJECTIF * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence * General Public License for Gnrale Publique GNU Affero * more details. pour plus de dtails. * * You should have received Vous devriez avoir reu une * a copy of the GNU Affero copie de la Licence Gnrale * General Public License along Publique GNU Affero avec * with OpenCADC. If not, see OpenCADC ; si ce nest * <http://www.gnu.org/licenses/>. pas le cas, consultez : * <http://www.gnu.org/licenses/>. * * ************************************************************************ */ package ca.nrc.cadc.web; import ca.nrc.cadc.auth.HTTPIdentityManager; import ca.nrc.cadc.config.ApplicationConfiguration; import ca.nrc.cadc.auth.IdentityManager; import ca.nrc.cadc.auth.AuthenticationUtil; import ca.nrc.cadc.caom2.CAOMQueryGeneratorImpl; import ca.nrc.cadc.caom2.ObsCoreQueryGeneratorImpl; import ca.nrc.cadc.date.DateUtil; import ca.nrc.cadc.net.TransientException; import ca.nrc.cadc.reg.client.RegistryClient; import ca.nrc.cadc.search.ObsModel; import ca.nrc.cadc.search.QueryGenerator; import ca.nrc.cadc.search.TargetNameResolverClientImpl; import ca.nrc.cadc.search.parser.exception.PositionParserException; import ca.nrc.cadc.search.upload.StreamingVOTableWriter; import ca.nrc.cadc.search.upload.UploadResults; import ca.nrc.cadc.tap.SyncTAPClient; import ca.nrc.cadc.tap.impl.SyncTAPClientImpl; import ca.nrc.cadc.tap.impl.TAPSearcherImpl; import ca.nrc.cadc.uws.*; import ca.nrc.cadc.uws.server.*; import ca.nrc.cadc.uws.server.impl.PostgresJobPersistence; import ca.nrc.cadc.uws.web.JobCreator; import org.apache.commons.fileupload.FileUploadException; import javax.security.auth.Subject; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URI; import java.security.PrivilegedExceptionAction; import java.sql.PreparedStatement; import java.sql.Types; import java.util.*; public class SearchJobServlet extends SyncServlet { static final String TAP_SERVICE_URI_PROPERTY_KEY = "org.opencadc.search.tap-service-id"; static final URI DEFAULT_TAP_SERVICE_URI = URI.create("ivo://cadc.nrc.ca/tap"); private JobManager jobManager; private JobUpdater jobUpdater; private ApplicationConfiguration applicationConfiguration; @Override public void init(final ServletConfig config) throws ServletException { super.init(config); final DatabaseJobPersistence jobPersistence = new PostgresJobPersistence(new HTTPIdentityManager()); jobManager = createJobManager(config); jobManager.setJobPersistence(jobPersistence); jobUpdater = jobPersistence; applicationConfiguration = new ApplicationConfiguration(Configuration.DEFAULT_CONFIG_FILE_PATH); } /** * Called by the server (via the <code>service</code> method) * to allow a servlet to handle a POST request. * * The HTTP POST method allows the client to send * data of unlimited length to the Web server a single time * and is useful when posting information such as * credit card numbers. * *When overriding this method, read the request data, * write the response headers, get the response's writer or output * stream object, and finally, write the response data. It's best * to include content type and encoding. When using a * <code>PrintWriter</code> object to return the response, set the * content type before accessing the <code>PrintWriter</code> object. * *The servlet container must write the headers before committing the * response, because in HTTP the headers must be sent before the * response body. * *Where possible, set the Content-Length header (with the * {@link ServletResponse#setContentLength} method), * to allow the servlet container to use a persistent connection * to return its response to the client, improving performance. * The content length is automatically set if the entire response fits * inside the response buffer. * *When using HTTP 1.1 chunked encoding (which means that the response * has a Transfer-Encoding header), do not set the Content-Length header. * *This method does not need to be either safe or idempotent. * Operations requested through POST can have side effects for * which the user can be held accountable, for example, * updating stored data or buying items online. * *If the HTTP POST request is incorrectly formatted, * <code>doPost</code> returns an HTTP "Bad Request" message. * * @param request an {@link HttpServletRequest} object that * contains the request the client has made * of the servlet * @param response an {@link HttpServletResponse} object that * contains the response the servlet sends * to the client * @throws IOException if an input or output error is * detected when the servlet handles * the request * @throws ServletException if the request for the POST * could not be handled * @see ServletOutputStream * @see ServletResponse#setContentType */ @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { try { final Subject subject = AuthenticationUtil.getSubject(request); if ((subject == null) || (subject.getPrincipals().isEmpty())) { processRequest(request, response); } else { Subject.doAs(subject, new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { processRequest(request, response); return null; } }); } } catch (TransientException ex) { // OutputStream not open, write an error response response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); response.addHeader("Retry-After", Integer.toString(ex.getRetryDelay())); response.setContentType("text/plain"); PrintWriter w = response.getWriter(); w.println("failed to get or persist job state."); w.println(" reason: " + ex.getMessage()); w.close(); } catch (JobPersistenceException ex) { // OutputStream not open, write an error response response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setContentType("text/plain"); PrintWriter w = response.getWriter(); w.println("failed to get or persist job state."); w.println(" reason: " + ex.getMessage()); w.close(); } catch (Throwable t) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setContentType("text/plain"); PrintWriter w = response.getWriter(); w.println("Unable to proceed with job execution.\n"); w.println("Reason: " + t.getMessage()); w.close(); } } /** * Obtain the current date in UTC. * * @return The current Date in UTC. */ private Date currentDateUTC() { final Calendar calendar = Calendar.getInstance(DateUtil.UTC); return calendar.getTime(); } private void processRequest(final HttpServletRequest request, final HttpServletResponse response) throws JobPersistenceException, TransientException, FileUploadException, IOException, PositionParserException, JobNotFoundException { final Map<String, Object> uploadPayload = new HashMap<>(); final List<Parameter> extraJobParameters = new ArrayList<>(); // final JobUpdater jobUpdater = // new PostgresJobPersistence(new ACIdentityManager()); final JobCreator jobCreator = new JobCreator(getInlineContentHandler()) { @Override protected void processStream(final String name, final String contentType, final InputStream inputStream) throws IOException { try { final String[] nameParts = name.split("\\."); final String paramName = nameParts[0]; final File uploadFile = new File(paramName); final FileOutputStream fos = new FileOutputStream(uploadFile); final String resolver = (nameParts.length == 2) ? nameParts[1] : "ALL"; final UploadResults uploadResults = new UploadResults(resolver, 0, 0); final StreamingVOTableWriter tableWriter = new StreamingVOTableWriter(uploadResults, new TargetNameResolverClientImpl()); tableWriter.write(inputStream, fos); extraJobParameters.add(new Parameter(UploadResults.UPLOAD_RESOLVER, resolver)); fos.flush(); fos.close(); if (uploadFile.length() > 0) { uploadPayload.put(paramName, uploadFile); } } catch (IOException e) { throw new RuntimeException(e); } } }; // Create the audit job. final Job auditJob = jobManager.create(jobCreator.create(request)); auditJob.getParameterList().addAll(extraJobParameters); final SyncOutput syncOutput = new HTTPResponseSyncOutput(response); final SyncTAPClient tapClient = new SyncTAPClientImpl(false, new RegistryClient()) { /** * Build the payload to POST. * * @param job The Job to get the payload for. * @return Map of Parameter name -> value. */ @Override protected Map<String, Object> getQueryPayload(Job job) { final Map<String, Object> queryPayload = super.getQueryPayload(job); queryPayload.putAll(uploadPayload); return queryPayload; } }; jobUpdater.setPhase(auditJob.getID(), ExecutionPhase.PENDING, ExecutionPhase.QUEUED, currentDateUTC()); // Create the TAP job to prepare to be executed. final JobRunner runner = new AdvancedRunner(auditJob, jobUpdater, syncOutput, new TAPSearcherImpl(new SyncResponseWriterImpl(syncOutput), jobUpdater, tapClient, getQueryGenerator(auditJob)), applicationConfiguration.lookupServiceURI(TAP_SERVICE_URI_PROPERTY_KEY, DEFAULT_TAP_SERVICE_URI)); runner.run(); response.setStatus(HttpServletResponse.SC_OK); } /** * Obtain the appropriate query generator. * * @return QueryGenerator instance. */ private QueryGenerator getQueryGenerator(final Job job) { // Look for parameters starting with obscore to determine if // querying CAOM2 or ObsCore. for (final Parameter parameter : job.getParameterList()) { if (ObsModel.isObsCore(parameter.getName())) { return new ObsCoreQueryGeneratorImpl(job); } } return new CAOMQueryGeneratorImpl(job); } }