Newer
Older
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.ADQLQueryFactory;
import adql.parser.QueryChecker;
/**
* <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 check ADQL queries? <i>({@link QueryChecker})</i></li>
* </ul>
*
* @author Grégory Mantelet (CDS;ARI)
* @version 2.0 (02/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 */
/* ******************* */
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* <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();
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* *************** */
/* 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 factory able to build every part of an {@link ADQLQuery} object.</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;
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/**
* <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>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 uws.service.AbstractUWSFactory#extractParameters(javax.servlet.http.HttpServletRequest, uws.service.UWS)
* @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);
}