TAP.java 15.13 KiB
package tap.resource;
/*
* 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
*/
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import tap.ServiceConnection;
import tap.TAPException;
import tap.ServiceConnection.LimitUnit;
import tap.db.DBConnection;
import tap.error.DefaultTAPErrorWriter;
import tap.formatter.OutputFormat;
import tap.log.TAPLog;
import tap.metadata.TAPMetadata;
import uws.UWSException;
import uws.job.ErrorType;
import uws.job.UWSJob;
import uws.job.user.JobOwner;
import uws.service.UWSService;
import uws.service.UWSUrl;
import uws.service.error.ServiceErrorWriter;
public class TAP<R> implements VOSIResource {
private static final long serialVersionUID = 1L;
protected final ServiceConnection<R> service;
protected final Map<String, TAPResource> resources;
protected String tapBaseURL = null;
protected String homePageURI = null;
protected ServiceErrorWriter errorWriter;
public TAP(ServiceConnection<R> serviceConnection) throws UWSException, TAPException {
service = serviceConnection;
resources = new HashMap<String, TAPResource>();
errorWriter = new DefaultTAPErrorWriter(service);
TAPResource res = new Availability(service);
resources.put(res.getName(), res);
res = new Capabilities(this);
resources.put(res.getName(), res);
res = new Sync(service, (Capabilities)res);
resources.put(res.getName(), res);
res = new ASync(service);
resources.put(res.getName(), res);
getUWS().setErrorWriter(errorWriter);
if (service.uploadEnabled()){
DBConnection<?> dbConn = null;
try {
dbConn = service.getFactory().createDBConnection("TAP(ServiceConnection)");
dbConn.dropSchema("TAP_UPLOAD");
dbConn.createSchema("TAP_UPLOAD");
} catch (TAPException e) {
throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, e, "Error while creating the schema TAP_UPLOAD !");
} finally {
if (dbConn != null)
dbConn.close();
}
}
updateTAPMetadata();
}
public final TAPLog getLogger(){
return service.getLogger();
}
public void setTAPBaseURL(String baseURL){
tapBaseURL = baseURL;
for(TAPResource res : resources.values())
res.setTAPBaseURL(tapBaseURL);
}
public void setTAPBaseURL(HttpServletRequest request){
setTAPBaseURL(request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+request.getServletPath());
}
public final Availability getAvailability(){
return (Availability) resources.get(Availability.RESOURCE_NAME);
}
public final Capabilities getCapabilities(){
return (Capabilities) resources.get(Capabilities.RESOURCE_NAME);
}
public final Sync getSync(){
return (Sync) resources.get(Sync.RESOURCE_NAME);
}
public final ASync getASync(){
return (ASync) resources.get(ASync.RESOURCE_NAME);
}
public final TAPMetadata getTAPMetadata(){
return (TAPMetadata) resources.get(TAPMetadata.RESOURCE_NAME);
}
public final Iterator<TAPResource> getTAPResources(){
return resources.values().iterator();
}
public final ServiceErrorWriter getErrorWriter() {
return errorWriter;
}
public final void setErrorWriter(ServiceErrorWriter errorWriter) {
if (errorWriter != null){
this.errorWriter = errorWriter;
getUWS().setErrorWriter(errorWriter);
}
}
@Override
public String getStandardID() { return "ivo://ivoa.net/std/TAP"; }
@Override
public String getAccessURL() { return tapBaseURL; }
@Override
public String getCapability() {
StringBuffer xml = new StringBuffer();
xml.append("<capability standardID=\"").append(getStandardID()).append("\" xsi:type=\"tr:TableAccess\">\n");
xml.append("\t<interface role=\"std\" xsi:type=\"vs:ParamHTTP\">\n");
xml.append("\t\t<accessURL use=\"base\">").append(getAccessURL()).append("</accessURL>\n");
xml.append("\t</interface>\n");
xml.append("\t<language>\n");
xml.append("\t\t<name>ADQL</name>\n");
xml.append("\t\t<version>2.0</version>\n");
xml.append("\t\t<description>ADQL 2.0</description>\n");
xml.append("\t</language>\n");
Iterator<OutputFormat<R>> itFormats = service.getOutputFormats();
OutputFormat<R> formatter;
while(itFormats.hasNext()){
formatter = itFormats.next();
xml.append("\t<outputFormat>\n");
xml.append("\t\t<mime>").append(formatter.getMimeType()).append("</mime>\n");
if (formatter.getShortMimeType() != null)
xml.append("\t\t<alias>").append(formatter.getShortMimeType()).append("</alias>\n");
if (formatter.getDescription() != null)
xml.append("\t\t<description>").append(formatter.getDescription()).append("</description>\n");
xml.append("\t</outputFormat>\n");
}
int[] retentionPeriod = service.getRetentionPeriod();
if (retentionPeriod != null && retentionPeriod.length >= 2){
if (retentionPeriod[0] > -1 || retentionPeriod[1] > -1){
xml.append("\t<retentionPeriod>\n");
if (retentionPeriod[0] > -1)
xml.append("\t\t<default>").append(retentionPeriod[0]).append("</default>\n");
if (retentionPeriod[1] > -1)
xml.append("\t\t<hard>").append(retentionPeriod[1]).append("</hard>\n");
xml.append("\t</retentionPeriod>\n");
}
}
int[] executionDuration = service.getExecutionDuration();
if (executionDuration != null && executionDuration.length >= 2){
if (executionDuration[0] > -1 || executionDuration[1] > -1){
xml.append("\t<executionDuration>\n");
if (executionDuration[0] > -1)
xml.append("\t\t<default>").append(executionDuration[0]).append("</default>\n");
if (executionDuration[1] > -1)
xml.append("\t\t<hard>").append(executionDuration[1]).append("</hard>\n");
xml.append("\t</executionDuration>\n");
}
}
int[] outputLimit = service.getOutputLimit();
LimitUnit[] outputLimitType = service.getOutputLimitType();
if (outputLimit != null && outputLimit.length >= 2 && outputLimitType != null && outputLimitType.length >= 2){
if (outputLimit[0] > -1 || outputLimit[1] > -1){
xml.append("\t<outputLimit>\n");
if (outputLimit[0] > -1)
xml.append("\t\t<default unit=\"").append(outputLimitType[0]).append("\">").append(outputLimit[0]).append("</default>\n");
if (outputLimit[1] > -1)
xml.append("\t\t<hard unit=\"").append(outputLimitType[1]).append("\">").append(outputLimit[1]).append("</hard>\n");
xml.append("\t</outputLimit>\n");
}
}
if (service.uploadEnabled()){
// Write upload methods: INLINE, HTTP, FTP:
xml.append("<uploadMethod ivo-id=\"ivo://ivoa.org/tap/uploadmethods#inline\" />");
xml.append("<uploadMethod ivo-id=\"ivo://ivoa.org/tap/uploadmethods#http\" />");
xml.append("<uploadMethod ivo-id=\"ivo://ivoa.org/tap/uploadmethods#ftp\" />");
xml.append("<uploadMethod ivo-id=\"ivo://ivoa.net/std/TAPRegExt#upload-inline\" />");
xml.append("<uploadMethod ivo-id=\"ivo://ivoa.net/std/TAPRegExt#upload-http\" />");
xml.append("<uploadMethod ivo-id=\"ivo://ivoa.net/std/TAPRegExt#upload-ftp\" />");
// Write upload limits:
int[] uploadLimit = service.getUploadLimit();
LimitUnit[] uploadLimitType = service.getUploadLimitType();
if (uploadLimit != null && uploadLimit.length >= 2 && uploadLimitType != null && uploadLimitType.length >= 2){
if (uploadLimit[0] > -1 || uploadLimit[1] > -1){
xml.append("\t<uploadLimit>\n");
if (uploadLimit[0] > -1)
xml.append("\t\t<default unit=\"").append(uploadLimitType[0]).append("\">").append(uploadLimit[0]).append("</default>\n");
if (uploadLimit[1] > -1)
xml.append("\t\t<hard unit=\"").append(uploadLimitType[1]).append("\">").append(uploadLimit[1]).append("</hard>\n");
xml.append("\t</uploadLimit>\n");
}
}
}
xml.append("\t</capability>");
return xml.toString();
}
public final UWSService getUWS(){
TAPResource res = resources.get("async");
if (res != null)
return ((ASync)res).getUWS();
else
return null;
}
/**
* @return The homePageURI.
*/
public final String getHomePageURI() {
return homePageURI;
}
public final void setHomePageURI(String uri){
homePageURI = (uri!=null)?uri.trim():uri;
if (homePageURI != null && homePageURI.length() == 0)
homePageURI = null;
}
public void init(ServletConfig config) throws ServletException {
for(TAPResource res : resources.values())
res.init(config);
}
public void destroy() {
for(TAPResource res : resources.values())
res.destroy();
}
public void executeRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
if (tapBaseURL == null)
setTAPBaseURL(request);
JobOwner owner = null;
String resourceName = null;
try{
// Identify the user:
if (service.getUserIdentifier() != null)
owner = service.getUserIdentifier().extractUserId(new UWSUrl(request), request);
String[] resourcePath = (request.getPathInfo() == null) ? null : request.getPathInfo().split("/");
// Display the TAP Main Page:
if (resourcePath == null || resourcePath.length < 1){
resourceName = "homePage";
response.setContentType("text/html");
writeHomePage(response.getWriter(), owner);
}
// or Display/Execute the selected TAP Resource:
else{
resourceName = resourcePath[1].trim().toLowerCase();
TAPResource res = resources.get(resourceName);
if (res != null)
res.executeResource(request, response);
else
errorWriter.writeError("This TAP service does not have a resource named \""+resourceName+"\" !", ErrorType.TRANSIENT, HttpServletResponse.SC_NOT_FOUND, response, request, null, "Get a TAP resource");
}
service.getLogger().httpRequest(request, owner, resourceName, HttpServletResponse.SC_OK, "[OK]", null);
response.flushBuffer();
}catch(IOException ioe){
errorWriter.writeError(ioe, response, request, owner, (resourceName==null)?"Writing the TAP home page":("Executing the TAP resource "+resourceName));
}catch(UWSException ue){
errorWriter.writeError(ue, response, request, owner, (resourceName==null)?"Writing the TAP home page":("Executing the TAP resource "+resourceName));
}catch(TAPException te){
writeError(te, response);
}catch(Throwable t){
errorWriter.writeError(t, response, request, owner, (resourceName==null)?"Writing the TAP home page":("Executing the TAP resource "+resourceName));
}
}
public void writeHomePage(final PrintWriter writer, final JobOwner owner) throws IOException {
// By default, list all available resources:
if (homePageURI == null){
writer.println("<html><head><title>TAP HOME PAGE</title></head><body><h1 style=\"text-align: center\">TAP HOME PAGE</h1><h2>Available resources:</h2><ul>");
for(TAPResource res : resources.values())
writer.println("<li><a href=\""+tapBaseURL+"/"+res.getName()+"\">"+res.getName()+"</a></li>");
writer.println("</ul></body></html>");
}
// or Display the specified home page:
else{
BufferedInputStream input = null;
try{
input = new BufferedInputStream((new URL(homePageURI)).openStream());
}catch(MalformedURLException mue){
input = new BufferedInputStream(new FileInputStream(new File(homePageURI)));
}
if (input == null)
throw new IOException("Incorrect TAP home page URI !");
byte[] buffer = new byte[255];
int nbReads = 0;
while((nbReads=input.read(buffer)) > 0)
writer.print(new String(buffer, 0, nbReads));
}
}
public void writeError(TAPException ex, HttpServletResponse response) throws ServletException, IOException {
service.getLogger().error(ex);
response.reset();
response.setStatus(ex.getHttpErrorCode());
response.setContentType("text/xml");
writeError(ex, response.getWriter());
}
protected void writeError(TAPException ex, PrintWriter output) throws ServletException, IOException {
output.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
output.println("<VOTABLE xmlns=\"http://www.ivoa.net/xml/VOTable/v1.2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.ivoa.net/xml/VOTable/v1.2\" version=\"1.2\">");
output.println("\t<RESOURCE type=\"results\">");
// Print the error:
output.println("\t\t<INFO name=\"QUERY_STATUS\" value=\"ERROR\">");
output.print("\t\t\t<![CDATA[ ");
if (ex.getExecutionStatus()!=null)
output.print("[WHILE "+ex.getExecutionStatus()+"] ");
output.print(ex.getMessage().replace('«', '\"').replace('»', '\"'));
output.println("]]>\t\t</INFO>");
// Print the current date:
DateFormat dateFormat = new SimpleDateFormat(UWSJob.DEFAULT_DATE_FORMAT);
output.print("\t\t<INFO name=\"DATE\" value=\""); output.print(dateFormat.format(new Date())); output.println("\" />");
// Print the provider (if any):
if (service.getProviderName() != null){
output.print("\t\t<INFO name=\"PROVIDER\" value=\""); output.print(service.getProviderName());
if (service.getProviderDescription() != null){
output.print("\">\n\t\t\t<![CDATA[");
output.print(service.getProviderDescription());
output.println("]]>\n\t\t</INFO>");
}else
output.println("\">");
}
// Print the query (if any):
if (ex.getQuery() != null){
output.print("\t\t<INFO name=\"QUERY\">\n\t\t\t<![CDATA[");
output.println(ex.getQuery());
output.println("]]>\t\t</INFO>");
}
output.println("\t</RESOURCE>");
output.println("</VOTABLE>");
output.flush();
}
public final boolean addResource(TAPResource newResource){
if (newResource == null)
return false;
resources.put(newResource.getName(), newResource);
return true;
}
public final boolean addResource(String resourceId, TAPResource newResource){
if (newResource == null)
return false;
resources.put((resourceId==null)?newResource.getName():resourceId, newResource);
return true;
}
public final int getNbResources(){
return resources.size();
}
public final TAPResource getResource(String resourceId){
return resources.get(resourceId);
}
public final boolean hasResource(String resourceId){
return resources.containsKey(resourceId);
}
public final TAPResource removeResource(String resourceId){
return resources.remove(resourceId);
}
public boolean updateTAPMetadata(){
TAPMetadata metadata = service.getTAPMetadata();
if (metadata != null){
resources.put(metadata.getName(), metadata);
return true;
}
return false;
}
}