package tap.config; /* * 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 2015-2016 - Astronomisches Rechen Institut (ARI) */ import static tap.config.TAPConfiguration.DEFAULT_TAP_CONF_FILE; import static tap.config.TAPConfiguration.KEY_ADD_TAP_RESOURCES; import static tap.config.TAPConfiguration.KEY_CAPABILITIES_STYLESHEET; import static tap.config.TAPConfiguration.KEY_EXAMPLES; import static tap.config.TAPConfiguration.KEY_HOME_PAGE; import static tap.config.TAPConfiguration.KEY_HOME_PAGE_MIME_TYPE; import static tap.config.TAPConfiguration.KEY_TABLES_STYLESHEET; import static tap.config.TAPConfiguration.TAP_CONF_PARAMETER; import static tap.config.TAPConfiguration.getProperty; import static tap.config.TAPConfiguration.isClassName; import static tap.config.TAPConfiguration.newInstance; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import tap.ServiceConnection; import tap.TAPException; import tap.resource.Examples; import tap.resource.HomePage; import tap.resource.TAP; import tap.resource.TAPResource; /** * <p>HTTP servlet fully configured with a TAP configuration file.</p> * * <p> * This configuration file may be specified in the initial parameter named {@link TAPConfiguration#TAP_CONF_PARAMETER} * of this servlet inside the WEB-INF/web.xml file. If none is specified, the file {@link TAPConfiguration#DEFAULT_TAP_CONF_FILE} * will be searched inside the directories of the classpath, and inside WEB-INF and META-INF. * </p> * * @author Grégory Mantelet (ARI) * @version 2.1 (07/2016) * @since 2.0 */ public class ConfigurableTAPServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** TAP object representing the TAP service. */ private TAP tap = null; @Override public void init(final ServletConfig config) throws ServletException{ // Nothing to do, if TAP is already initialized: if (tap != null) return; /* 1. GET THE FILE PATH OF THE TAP CONFIGURATION FILE */ String tapConfPath = config.getInitParameter(TAP_CONF_PARAMETER); if (tapConfPath == null || tapConfPath.trim().length() == 0) tapConfPath = null; //throw new ServletException("Configuration file path missing! You must set a servlet init parameter whose the name is \"" + TAP_CONF_PARAMETER + "\"."); /* 2. OPEN THE CONFIGURATION FILE */ InputStream input = null; // CASE: No file specified => search in the classpath for a file having the default name "tap.properties". if (tapConfPath == null) input = searchFile(DEFAULT_TAP_CONF_FILE, config); else{ File f = new File(tapConfPath); // CASE: The given path matches to an existing local file. if (f.exists()){ try{ input = new FileInputStream(f); }catch(IOException ioe){ throw new ServletException("Impossible to read the TAP configuration file (" + tapConfPath + ")!", ioe); } } // CASE: The given path seems to be relative to the servlet root directory. else input = searchFile(tapConfPath, config); } // If no file has been found, cancel the servlet loading: if (input == null) throw new ServletException("Configuration file not found with the path: \"" + ((tapConfPath == null) ? DEFAULT_TAP_CONF_FILE : tapConfPath) + "\"! Please provide a correct file path in servlet init parameter (\"" + TAP_CONF_PARAMETER + "\") or put your configuration file named \"" + DEFAULT_TAP_CONF_FILE + "\" in a directory of the classpath or in WEB-INF or META-INF."); /* 3. PARSE IT INTO A PROPERTIES SET */ Properties tapConf = new Properties(); try{ tapConf.load(input); }catch(IOException ioe){ throw new ServletException("Impossible to read the TAP configuration file (" + tapConfPath + ")!", ioe); }finally{ try{ input.close(); }catch(IOException ioe2){} } /* 4. CREATE THE TAP SERVICE */ ServiceConnection serviceConn = null; try{ // Create the service connection: serviceConn = new ConfigurableServiceConnection(tapConf, config.getServletContext().getRealPath("")); // Create all the TAP resources: tap = new TAP(serviceConn); }catch(Exception ex){ tap = null; if (ex instanceof TAPException) throw new ServletException(ex.getMessage(), ex.getCause()); else throw new ServletException("Impossible to initialize the TAP service!", ex); } /* 4Bis. SET THE HOME PAGE */ String propValue = getProperty(tapConf, KEY_HOME_PAGE); if (propValue != null){ // If it is a class path, replace the current home page by an instance of this class: if (isClassName(propValue)){ try{ tap.setHomePage(newInstance(propValue, KEY_HOME_PAGE, HomePage.class, new Class<?>[]{TAP.class}, new Object[]{tap})); }catch(TAPException te){ throw new ServletException(te.getMessage(), te.getCause()); } } // If it is a file URI (null, file inside WebContent, file://..., http://..., etc...): else{ // ...set the given URI: tap.setHomePageURI(propValue); // ...and its MIME type (if any): propValue = getProperty(tapConf, KEY_HOME_PAGE_MIME_TYPE); if (propValue != null) tap.setHomePageMimeType(propValue); } } /* 4Ter. SET THE XSLT for /capabilities and /tables */ initXSLTStylesheet(tapConf); /* 4Quater. SET THE EXAMPLES ENDPOINT (if any) */ propValue = getProperty(tapConf, KEY_EXAMPLES); if (propValue != null) tap.addResource(new Examples(tap, propValue)); /* 5. SET ADDITIONAL TAP RESOURCES */ propValue = getProperty(tapConf, KEY_ADD_TAP_RESOURCES); if (propValue != null){ // split all list items: String[] lstResources = propValue.split(","); for(String addRes : lstResources){ addRes = addRes.trim(); // ignore empty items: if (addRes.length() > 0){ try{ // create an instance of the resource: TAPResource newRes = newInstance(addRes, KEY_ADD_TAP_RESOURCES, TAPResource.class, new Class<?>[]{TAP.class}, new Object[]{tap}); if (newRes.getName() == null || newRes.getName().trim().length() == 0) throw new TAPException("TAP resource name missing for the new resource \"" + addRes + "\"! The function getName() of the new TAPResource must return a non-empty and not NULL name. See the property \"" + KEY_ADD_TAP_RESOURCES + "\"."); // add it into TAP: tap.addResource(newRes); }catch(TAPException te){ throw new ServletException(te.getMessage(), te.getCause()); } } } } /* 6. DEFAULT SERVLET INITIALIZATION */ super.init(config); /* 7. INITIATILIZE THE TAP SERVICE */ tap.init(config); /* 8. FINALLY MAKE THE SERVICE AVAILABLE */ serviceConn.setAvailable(true, "TAP service available."); } /** * Search the given file name/path in the directories of the classpath, then inside WEB-INF and finally inside META-INF. * * @param filePath A file name/path. * @param config Servlet configuration (containing also the context class loader - link with the servlet classpath). * * @return The input stream toward the specified file, or NULL if no file can be found. * * @since 2.0 */ protected final InputStream searchFile(String filePath, final ServletConfig config){ InputStream input = null; // Try to search in the classpath (with just a file name or a relative path): input = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); // If not found, try searching in WEB-INF and META-INF (as this fileName is a file path relative to one of these directories): if (input == null){ if (filePath.startsWith("/")) filePath = filePath.substring(1); // ...try at the root of WEB-INF: input = config.getServletContext().getResourceAsStream("/WEB-INF/" + filePath); // ...and at the root of META-INF: if (input == null) input = config.getServletContext().getResourceAsStream("/META-INF/" + filePath); } return input; } /** * Initialize the XSLT for /capabilities and /tables. * * @param tapConfig The content of the TAP configuration file. * * @since 2.1 */ protected void initXSLTStylesheet(final Properties tapConfig){ // Set the XSLT of /capabilities: String propValue = getProperty(tapConfig, KEY_CAPABILITIES_STYLESHEET); if (propValue != null) tap.getCapabilities().setXSLTPath(propValue); // Set the XSLT of /tables: propValue = getProperty(tapConfig, KEY_TABLES_STYLESHEET); if (propValue != null) tap.getTAPMetadata().setXSLTPath(propValue); } @Override public void destroy(){ // Free all resources used by TAP: if (tap != null){ tap.destroy(); tap = null; } super.destroy(); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ if (tap != null){ try{ tap.executeRequest(req, resp); }catch(Throwable t){ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t.getMessage()); } }else resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "TAP service not yet initialized!"); } }