package tap; /* * This file is part of TAPLibrary. * * TAPLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * TAPLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012-2015 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import tap.db.DBConnection; import tap.metadata.TAPSchema; import tap.parameters.TAPParameters; import tap.upload.Uploader; import uws.UWSException; import uws.job.ErrorSummary; import uws.job.JobThread; import uws.job.Result; import uws.job.UWSJob; import uws.job.parameters.UWSParameters; import uws.job.user.JobOwner; import uws.service.UWSFactory; import uws.service.UWSService; import uws.service.backup.UWSBackupManager; import uws.service.error.ServiceErrorWriter; import uws.service.file.UWSFileManager; import uws.service.request.RequestParser; import adql.parser.ADQLParser; import adql.parser.ADQLQueryFactory; import adql.parser.QueryChecker; import adql.query.ADQLQuery; /** * <p>Let build essential objects of the TAP service.</p> * * <p>Basically, it means answering to the following questions:</p> * <ul> * <li>how to connect to the database? <i>({@link DBConnection})</i></li> * <li>which UWS implementation (default implementation provided by default) to use? <i>({@link UWSService})</i></li> * <li>whether and how UWS/asynchronous jobs must be backuped and restored? <i>({@link UWSBackupManager})</i></li> * <li>how to create asynchronous jobs? <i>({@link TAPJob})</i></li> * <li>whether and how tables must be updated? <i>({@link Uploader})</i></li> * <li>how to execute an ADQL query? <i>({@link ADQLExecutor})</i> * <li>how to parser an ADQL query? <i>({@link ADQLParser})</i></li> * <li>how to check ADQL queries? <i>({@link QueryChecker})</i></li> * </ul> * * @author Grégory Mantelet (CDS;ARI) * @version 2.0 (04/2015) */ public abstract class TAPFactory implements UWSFactory { /** Connection to the TAP service ; it provides all important service configuration information. */ protected final ServiceConnection service; /** * Build a basic {@link TAPFactory}. * Nothing is done except setting the service connection. * * @param service Configuration of the TAP service. <i>MUST NOT be NULL</i> * * @throws NullPointerException If the given {@link ServiceConnection} is NULL. */ protected TAPFactory(final ServiceConnection service) throws NullPointerException{ if (service == null) throw new NullPointerException("Can not create a TAPFactory without a ServiceConnection instance !"); this.service = service; } /** * <p>Get the object to use when an error must be formatted and written to the user.</p> * * <p>This formatted error will be either written in an HTTP response or in a job error summary.</p> * * @return The error writer to use. * * @since 2.0 */ public abstract ServiceErrorWriter getErrorWriter(); /* ******************* */ /* DATABASE CONNECTION */ /* ******************* */ /** * <p>Get a free database connection.</p> * * <p> * <i>Free</i> means this connection is not currently in use and will be exclusively dedicated to the function/process/thread * which has asked for it by calling this function. * </p> * * <p><i>Note: * This function can create on the fly a new connection OR get a free one from a connection pool. Considering the * creation time of a database connection, the second way is recommended. * </i></p> * * <p><b>IMPORTANT:</b> * The returned connection MUST be freed after having used it. * </p> * * <p><i><b>WARNING:</b> * Some implementation may free the connection automatically when not used for a specific time. * So, do not forget to free the connection after use! * </i></p> * * @param jobID ID of the job/thread/process which has asked for this connection. <i>note: The returned connection must then be identified thanks to this ID.</i> * * @return A new and free connection to the database. <b>MUST BE NOT NULL, or otherwise a TAPException should be returned.</b> * * @throws TAPException If there is any error while getting a free connection. * * @since 2.0 */ public abstract DBConnection getConnection(final String jobID) throws TAPException; /** * <p>Free the given connection.</p> * * <p> * This function is called by the TAP library when a job/thread does not need this connection any more. It aims * to free resources associated to the given database connection. * </p> * * <p><i>Note: * This function can just close definitely the connection OR give it back to a connection pool. The implementation is * here totally free! * </i></p> * * @param conn The connection to close. * * @since 2.0 */ public abstract void freeConnection(final DBConnection conn); /** * <p>Destroy all resources (and particularly DB connections and JDBC driver) allocated in this factory.</p> * * <p><i>Note: * This function is called when the TAP service is shutting down. * After this call, the factory may not be able to provide any closed resources ; its behavior may be unpredictable. * </i></p> * * @since 2.0 */ public abstract void destroy(); /* *************** */ /* ADQL MANAGEMENT */ /* *************** */ /** * <p>Create the object able to execute an ADQL query and to write and to format its result.</p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory} * </i></p> * * @return An ADQL executor. * * @throws TAPException If any error occurs while creating an ADQL executor. */ public abstract ADQLExecutor createADQLExecutor() throws TAPException; /** * <p>Create a parser of ADQL query.</p> * * <p><i>Warning: * This parser can be created with a query factory and/or a query checker. * {@link #createQueryFactory()} will be used only if the default query factory (or none) is set * in the ADQL parser returned by this function. * Idem for {@link #createQueryChecker(TAPSchema)}: it will used only if no query checker is set * in the returned ADQL parser. * </i></p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @return An ADQL query parser. * * @throws TAPException If any error occurs while creating an ADQL parser. * * @since 2.0 */ public abstract ADQLParser createADQLParser() throws TAPException; /** * <p>Create a factory able to build every part of an {@link ADQLQuery} object.</p> * * <p><i>Warning: * This function is used only if the default query factory (or none) is set in the ADQL parser * returned by {@link #createADQLParser()}. * </i></p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory} * </i></p> * * @return An {@link ADQLQuery} factory. * * @throws TAPException If any error occurs while creating the factory. */ public abstract ADQLQueryFactory createQueryFactory() throws TAPException; /** * <p>Create an object able to check the consistency between the ADQL query and the database. * That's to say, it checks whether the tables and columns used in the query really exist * in the database.</p> * * <p><i>Warning: * This function is used only if no query checker is set in the ADQL parser * returned by {@link #createADQLParser()}. * </i></p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory} * </i></p> * * @param uploadSchema ADQL schema containing the description of all uploaded tables. * * @return A query checker. * * @throws TAPException If any error occurs while creating a query checker. */ public abstract QueryChecker createQueryChecker(final TAPSchema uploadSchema) throws TAPException; /* ****** */ /* UPLOAD */ /* ****** */ /** * <p>Create an object able to manage the creation of submitted user tables (in VOTable) into the database.</p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @param dbConn The database connection which has requested an {@link Uploader}. * * @return An {@link Uploader}. * * @throws TAPException If any error occurs while creating an {@link Uploader} instance. */ public abstract Uploader createUploader(final DBConnection dbConn) throws TAPException; /* ************** */ /* UWS MANAGEMENT */ /* ************** */ /** * <p>Create the object which will manage the asynchronous resource of the TAP service. * This resource is a UWS service.</p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @return A UWS service which will be the asynchronous resource of this TAP service. * * @throws TAPException If any error occurs while creating this UWS service. */ public abstract UWSService createUWS() throws TAPException; /** * <p>Create the object which will manage the backup and restoration of all asynchronous jobs.</p> * * <p><i>Note: * This function may return NULL. If it does, asynchronous jobs won't be backuped. * </i></p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @param uws The UWS service which has to be backuped and restored. * * @return The backup manager to use. <i>MAY be NULL</i> * * @throws TAPException If any error occurs while creating this backup manager. */ public abstract UWSBackupManager createUWSBackupManager(final UWSService uws) throws TAPException; /** * <p>Creates a (PENDING) UWS job from the given HTTP request.</p> * * <p> * This implementation just call {@link #createTAPJob(HttpServletRequest, JobOwner)} * with the given request, in order to ensure that the returned object is always a {@link TAPJob}. * </p> * * @see uws.service.AbstractUWSFactory#createJob(javax.servlet.http.HttpServletRequest, uws.job.user.JobOwner) * @see #createTAPJob(HttpServletRequest, JobOwner) */ @Override public final UWSJob createJob(HttpServletRequest request, JobOwner owner) throws UWSException{ return createTAPJob(request, owner); } /** * <p>Create a PENDING asynchronous job from the given HTTP request.</p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @param request Request which contains all parameters needed to set correctly the asynchronous job to create. * @param owner The user which has requested the job creation. * * @return A new PENDING asynchronous job. * * @throws UWSException If any error occurs while reading the parameters in the request or while creating the job. */ protected abstract TAPJob createTAPJob(final HttpServletRequest request, final JobOwner owner) throws UWSException; /** * <p>Creates a UWS job with the following attributes.</p> * * <p> * This implementation just call {@link #createTAPJob(String, JobOwner, TAPParameters, long, long, long, List, ErrorSummary)} * with the given parameters, in order to ensure that the returned object is always a {@link TAPJob}. * </p> * * <p><i>Note 1: * This function is mainly used to restore a UWS job at the UWS initialization. * </i></p> * * <p><i>Note 2: * The job phase is chosen automatically from the given job attributes (i.e. no endTime => PENDING, no result and no error => ABORTED, ...). * </i></p> * * @see uws.service.AbstractUWSFactory#createJob(java.lang.String, uws.job.user.JobOwner, uws.job.parameters.UWSParameters, long, long, long, java.util.List, uws.job.ErrorSummary) * @see #createTAPJob(String, JobOwner, TAPParameters, long, long, long, List, ErrorSummary) */ @Override public final UWSJob createJob(String jobId, JobOwner owner, final UWSParameters params, long quote, long startTime, long endTime, List<Result> results, ErrorSummary error) throws UWSException{ return createTAPJob(jobId, owner, (TAPParameters)params, quote, startTime, endTime, results, error); } /** * <p>Create a PENDING asynchronous job with the given parameters.</p> * * <p><i>Note: * A default implementation is provided in {@link AbstractTAPFactory}. * </i></p> * * @param jobId ID of the job (NOT NULL). * @param owner Owner of the job. * @param params List of all input job parameters. * @param quote Its quote (in seconds). * @param startTime Date/Time of the start of this job. * @param endTime Date/Time of the end of this job. * @param results All results of this job. * @param error The error which ended the job to create. * * @return A new PENDING asynchronous job. * * @throws UWSException If there is an error while creating the job. */ protected abstract TAPJob createTAPJob(final String jobId, final JobOwner owner, final TAPParameters params, final long quote, final long startTime, final long endTime, final List<Result> results, final ErrorSummary error) throws UWSException; /** * <p>Create the thread which will execute the task described by the given UWSJob instance.</p> * * <p> * This function is definitely implemented here and can not be overridden. The processing of * an ADQL query must always be the same in a TAP service ; it is completely done by {@link AsyncThread}. * </p> * * @see uws.service.UWSFactory#createJobThread(uws.job.UWSJob) * @see AsyncThread */ @Override public final JobThread createJobThread(final UWSJob job) throws UWSException{ try{ return new AsyncThread((TAPJob)job, createADQLExecutor(), getErrorWriter()); }catch(TAPException te){ if (te.getCause() != null && te.getCause() instanceof UWSException) throw (UWSException)te.getCause(); else throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, te, "Impossible to create an AsyncThread !"); } } /** * <p>Extract the parameters from the given request (multipart or not).</p> * * <p> * This function is used only to create the set of parameters for a TAP job (synchronous or asynchronous). * Thus, it just call {@link #createTAPParameters(HttpServletRequest)} with the given request, in order to ensure * that the returned object is always a {@link TAPParameters}. * </p> * * @see #createTAPParameters(HttpServletRequest) */ @Override public final UWSParameters createUWSParameters(HttpServletRequest request) throws UWSException{ try{ return createTAPParameters(request); }catch(TAPException te){ if (te.getCause() != null && te.getCause() instanceof UWSException) throw (UWSException)te.getCause(); else throw new UWSException(te.getHttpErrorCode(), te); } } /** * <p>Extract all the TAP parameters from the given HTTP request (multipart or not) and return them.</p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @param request The HTTP request containing the TAP parameters to extract. * * @return An object gathering all successfully extracted TAP parameters. * * @throws TAPException If any error occurs while extracting the parameters. */ public abstract TAPParameters createTAPParameters(final HttpServletRequest request) throws TAPException; /** * <p>Identify and gather all identified parameters of the given map inside a {@link TAPParameters} object.</p> * * <p> * This implementation just call {@link #createTAPParameters(Map)} with the given map, in order to ensure * that the returned object is always a {@link TAPParameters}. * </p> * * @see uws.service.AbstractUWSFactory#createUWSParameters(java.util.Map) * @see #createTAPParameters(Map) */ @Override public final UWSParameters createUWSParameters(Map<String,Object> params) throws UWSException{ try{ return createTAPParameters(params); }catch(TAPException te){ if (te.getCause() != null && te.getCause() instanceof UWSException) throw (UWSException)te.getCause(); else throw new UWSException(te.getHttpErrorCode(), te); } } /** * <p>Identify all TAP parameters and gather them inside a {@link TAPParameters} object.</p> * * <p><i>Note: * A default implementation is provided by {@link AbstractTAPFactory}. * </i></p> * * @param params Map containing all parameters. * * @return An object gathering all successfully identified TAP parameters. * * @throws TAPException If any error occurs while creating the {@link TAPParameters} object. */ public abstract TAPParameters createTAPParameters(final Map<String,Object> params) throws TAPException; @Override public RequestParser createRequestParser(final UWSFileManager fileManager) throws UWSException{ return new TAPRequestParser(fileManager); } }