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-2016 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import tap.db.DBConnection; import tap.error.DefaultTAPErrorWriter; import tap.metadata.TAPMetadata; import tap.metadata.TAPSchema; import tap.metadata.TAPTable; import tap.parameters.TAPParameters; import tap.upload.Uploader; import uws.UWSException; import uws.job.ErrorSummary; import uws.job.Result; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSService; import uws.service.backup.UWSBackupManager; import uws.service.error.ServiceErrorWriter; import adql.db.DBChecker; import adql.parser.ADQLParser; import adql.parser.ADQLQueryFactory; import adql.parser.ParseException; import adql.parser.QueryChecker; import adql.query.ADQLQuery; /** * Default implementation of most of the {@link TAPFactory} function. * Only the functions related with the database connection stay abstract. * * @author Grégory Mantelet (CDS;ARI) * @version 2.1 (01/2016) */ public abstract class AbstractTAPFactory extends TAPFactory { /** The error writer to use when any error occurs while executing a resource or to format an error occurring while executing an asynchronous job. */ protected final ServiceErrorWriter errorWriter; /** * Build a basic 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. * * @see AbstractTAPFactory#AbstractTAPFactory(ServiceConnection, ServiceErrorWriter) */ protected AbstractTAPFactory(ServiceConnection service) throws NullPointerException{ this(service, new DefaultTAPErrorWriter(service)); } /** * <p>Build a basic TAPFactory. * Nothing is done except setting the service connection and the given error writer.</p> * * <p>Then the error writer will be used when creating a UWS service and a job thread.</p> * * @param service Configuration of the TAP service. <i>MUST NOT be NULL</i> * @param errorWriter Object to use to format and write the errors for the user. * * @throws NullPointerException If the given {@link ServiceConnection} is NULL. * * @see TAPFactory#TAPFactory(ServiceConnection) */ protected AbstractTAPFactory(final ServiceConnection service, final ServiceErrorWriter errorWriter) throws NullPointerException{ super(service); this.errorWriter = errorWriter; } @Override public final ServiceErrorWriter getErrorWriter(){ return errorWriter; } /* *************** */ /* ADQL MANAGEMENT */ /* *************** */ /** * <p><i>Note: * Unless the standard implementation - {@link ADQLExecutor} - does not fit exactly your needs, * it should not be necessary to extend this class and to extend this function (implemented here by default). * </i></p> */ @Override public ADQLExecutor createADQLExecutor() throws TAPException{ return new ADQLExecutor(service); } /** * <p><i>Note: * This function should be extended if you want to customize the ADQL grammar. * </i></p> */ @Override public ADQLParser createADQLParser() throws TAPException{ return new ADQLParser(); } /** * <p><i>Note: * This function should be extended if you have customized the creation of any * {@link ADQLQuery} part ; it could be the addition of one or several user defined function * or the modification of any ADQL function or clause specific to your implementation. * </i></p> */ @Override public ADQLQueryFactory createQueryFactory() throws TAPException{ return new ADQLQueryFactory(); } /** * <p>This implementation gathers all tables published in this TAP service and those uploaded * by the user. Then it calls {@link #createQueryChecker(Collection)} with this list in order * to create a query checked. * </p> * * <p><i>Note: * This function can not be overridded, but {@link #createQueryChecker(Collection)} can be. * </i></p> */ @Override public final QueryChecker createQueryChecker(final TAPSchema uploadSchema) throws TAPException{ // Get all tables published in this TAP service: TAPMetadata meta = service.getTAPMetadata(); // Build a list in order to gather all these with the uploaded ones: ArrayList<TAPTable> tables = new ArrayList<TAPTable>(meta.getNbTables()); // Add all tables published in TAP: Iterator<TAPTable> it = meta.getTables(); while(it.hasNext()) tables.add(it.next()); // Add all tables uploaded by the user: if (uploadSchema != null){ for(TAPTable table : uploadSchema) tables.add(table); } // Finally, create the query checker: return createQueryChecker(tables); } /** * <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: * This implementation just create a {@link DBChecker} instance with the list given in parameter. * </i></p> * * @param tables List of all available tables (and indirectly, columns). * * @return A new ADQL query checker. * * @throws TAPException If any error occurs while creating the query checker. */ protected QueryChecker createQueryChecker(final Collection<TAPTable> tables) throws TAPException{ try{ return new DBChecker(tables, service.getUDFs(), service.getGeometries(), service.getCoordinateSystems()); }catch(ParseException e){ throw new TAPException("Unable to build a DBChecker instance! " + e.getMessage(), e, UWSException.INTERNAL_SERVER_ERROR); } } /* ****** */ /* UPLOAD */ /* ****** */ /** * <p>This implementation just create an {@link Uploader} instance with the given database connection.</p> * * <p><i>Note: * This function should be overrided if you need to change the DB name of the TAP_UPLOAD schema. * Indeed, by overriding this function you can specify a given TAPSchema to use as TAP_UPLOAD schema * in the constructor of {@link Uploader}. But do not forget that this {@link TAPSchema} instance MUST have * an ADQL name equals to "TAP_UPLOAD", otherwise, a TAPException will be thrown. * </i></p> */ @Override public Uploader createUploader(final DBConnection dbConn) throws TAPException{ return new Uploader(service, dbConn); } /* ************** */ /* UWS MANAGEMENT */ /* ************** */ /** * <p>This implementation just create a {@link UWSService} instance.</p> * * <p><i>Note: * This implementation is largely enough for a TAP service. It is not recommended to override * this function. * </i></p> */ @Override public UWSService createUWS() throws TAPException{ try{ UWSService uws = new UWSService(this, this.service.getFileManager(), this.service.getLogger()); uws.setName("TAP/async"); uws.setErrorWriter(errorWriter); return uws; }catch(UWSException ue){ throw new TAPException("Can not create a UWS service (asynchronous resource of TAP)!", ue, UWSException.INTERNAL_SERVER_ERROR); } } /** * <p>This implementation does not provided a backup manager. * It means that no asynchronous job will be restored and backuped.</p> * * <p>You must override this function if you want enable the backup feature.</p> */ @Override public UWSBackupManager createUWSBackupManager(final UWSService uws) throws TAPException{ return null; } /** * <p>This implementation provides a basic {@link TAPJob} instance.</p> * * <p> * If you need to add or modify the behavior of some functions of a {@link TAPJob}, * you must override this function and return your own extension of {@link TAPJob}. * </p> */ @Override protected TAPJob createTAPJob(final HttpServletRequest request, final JobOwner owner) throws UWSException{ try{ // Extract the HTTP request ID (the job ID should be the same, if not already used by another job): String requestID = null; if (request.getAttribute(UWS.REQ_ATTRIBUTE_ID) != null && request.getAttribute(UWS.REQ_ATTRIBUTE_ID) instanceof String) requestID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID).toString(); // Extract the TAP parameters from the HTTP request: TAPParameters tapParams = createTAPParameters(request); // Create the job: return new TAPJob(owner, tapParams, requestID); }catch(TAPException te){ if (te.getCause() != null && te.getCause() instanceof UWSException) throw (UWSException)te.getCause(); else throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, te, "Can not create a TAP asynchronous job!"); } } /** * <p>This implementation provides a basic {@link TAPJob} instance.</p> * * <p> * If you need to add or modify the behavior of some functions of a {@link TAPJob}, * you must override this function and return your own extension of {@link TAPJob}. * </p> */ @Override protected 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{ try{ return new TAPJob(jobId, owner, params, quote, startTime, endTime, results, error); }catch(TAPException te){ if (te.getCause() != null && te.getCause() instanceof UWSException) throw (UWSException)te.getCause(); else throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, te, "Can not create a TAP asynchronous job !"); } } /** * <p>This implementation extracts standard TAP parameters from the given request.</p> * * <p> * Non-standard TAP parameters are added in a map inside the returned {@link TAPParameters} object * and are accessible with {@link TAPParameters#get(String)} and {@link TAPParameters#getAdditionalParameters()}. * However, if you want to manage them in another way, you must extend {@link TAPParameters} and override * this function in order to return an instance of your extension. * </p> */ @Override public TAPParameters createTAPParameters(final HttpServletRequest request) throws TAPException{ return new TAPParameters(request, service); } /** * <p>This implementation extracts standard TAP parameters from the given request.</p> * * <p> * Non-standard TAP parameters are added in a map inside the returned {@link TAPParameters} object * and are accessible with {@link TAPParameters#get(String)} and {@link TAPParameters#getAdditionalParameters()}. * However, if you want to manage them in another way, you must extend {@link TAPParameters} and override * this function in order to return an instance of your extension. * </p> */ @Override public TAPParameters createTAPParameters(final Map<String,Object> params) throws TAPException{ return new TAPParameters(service, params); } }