1.1 --- a/src/java/de/uapcore/lightpit/DatabaseFacade.java Sun Apr 08 16:51:15 2018 +0200 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,178 +0,0 @@ 1.4 -/* 1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 1.6 - * 1.7 - * Copyright 2018 Mike Becker. All rights reserved. 1.8 - * 1.9 - * Redistribution and use in source and binary forms, with or without 1.10 - * modification, are permitted provided that the following conditions are met: 1.11 - * 1.12 - * 1. Redistributions of source code must retain the above copyright 1.13 - * notice, this list of conditions and the following disclaimer. 1.14 - * 1.15 - * 2. Redistributions in binary form must reproduce the above copyright 1.16 - * notice, this list of conditions and the following disclaimer in the 1.17 - * documentation and/or other materials provided with the distribution. 1.18 - * 1.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 1.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1.29 - * POSSIBILITY OF SUCH DAMAGE. 1.30 - * 1.31 - */ 1.32 -package de.uapcore.lightpit; 1.33 - 1.34 -import java.sql.Connection; 1.35 -import java.sql.DatabaseMetaData; 1.36 -import java.sql.SQLException; 1.37 -import java.util.Optional; 1.38 -import javax.naming.Context; 1.39 -import javax.naming.InitialContext; 1.40 -import javax.naming.NamingException; 1.41 -import javax.servlet.ServletContext; 1.42 -import javax.servlet.ServletContextEvent; 1.43 -import javax.servlet.ServletContextListener; 1.44 -import javax.servlet.annotation.WebListener; 1.45 -import javax.sql.DataSource; 1.46 -import org.slf4j.Logger; 1.47 -import org.slf4j.LoggerFactory; 1.48 - 1.49 -/** 1.50 - * Provides access to different privilege layers within the database. 1.51 - */ 1.52 -@WebListener 1.53 -public final class DatabaseFacade implements ServletContextListener { 1.54 - 1.55 - private static final Logger LOG = LoggerFactory.getLogger(DatabaseFacade.class); 1.56 - 1.57 - /** 1.58 - * Timeout in seconds for the validation test. 1.59 - */ 1.60 - private static final int DB_TEST_TIMEOUT = 10; 1.61 - 1.62 - public static enum Dialect { 1.63 - Postgres; 1.64 - } 1.65 - 1.66 - /** 1.67 - * The database dialect to use. 1.68 - * 1.69 - * May be override by context parameter. 1.70 - * 1.71 - * @see Constants#CTX_ATTR_DB_DIALECT 1.72 - */ 1.73 - private Dialect dialect = Dialect.Postgres; 1.74 - 1.75 - /** 1.76 - * The default schema to test against when validating the connection. 1.77 - * 1.78 - * May be overridden by context parameter. 1.79 - * 1.80 - * @see Constants#CTX_ATTR_DB_SCHEMA 1.81 - */ 1.82 - private static final String DB_DEFAULT_SCHEMA = "lightpit"; 1.83 - 1.84 - /** 1.85 - * The attribute name in the Servlet context under which an instance of this class can be found. 1.86 - */ 1.87 - public static final String SC_ATTR_NAME = DatabaseFacade.class.getName(); 1.88 - private ServletContext sc; 1.89 - 1.90 - private static final String DS_JNDI_NAME = "jdbc/lightpit/app"; 1.91 - private Optional<DataSource> dataSource; 1.92 - 1.93 - /** 1.94 - * Returns the data source. 1.95 - * 1.96 - * The Optional returned should never be empty. However, if something goes 1.97 - * wrong during initialization, the data source might be absent. 1.98 - * Hence, users of this data source are forced to check the existence. 1.99 - * 1.100 - * @return a data source 1.101 - */ 1.102 - public Optional<DataSource> getDataSource() { 1.103 - return dataSource; 1.104 - } 1.105 - 1.106 - public Dialect getSQLDialect() { 1.107 - return dialect; 1.108 - } 1.109 - 1.110 - private static void checkConnection(DataSource ds, String testSchema, String errMsg) { 1.111 - try (Connection conn = ds.getConnection()) { 1.112 - if (!conn.isValid(DB_TEST_TIMEOUT)) { 1.113 - throw new SQLException("Validation check failed."); 1.114 - } 1.115 - if (conn.isReadOnly()) { 1.116 - throw new SQLException("Connection is read-only and thus unusable."); 1.117 - } 1.118 - if (!conn.getSchema().equals(testSchema)) { 1.119 - throw new SQLException(String.format("Connection is not configured to use the schema %s.", testSchema)); 1.120 - } 1.121 - DatabaseMetaData metaData = conn.getMetaData(); 1.122 - LOG.info("Connections as {} to {}/{} ready to go.", metaData.getUserName(), metaData.getURL(), conn.getSchema()); 1.123 - } catch (SQLException ex) { 1.124 - LOG.error(errMsg, ex); 1.125 - } 1.126 - } 1.127 - 1.128 - private static Optional<DataSource> retrieveDataSource(Context ctx) { 1.129 - DataSource ret = null; 1.130 - try { 1.131 - ret = (DataSource)ctx.lookup(DS_JNDI_NAME); 1.132 - LOG.info("Data source retrieved."); 1.133 - } catch (NamingException ex) { 1.134 - LOG.error("Data source {} not available.", DS_JNDI_NAME); 1.135 - LOG.error("Reason for the missing data source: ", ex); 1.136 - } 1.137 - return Optional.ofNullable(ret); 1.138 - } 1.139 - 1.140 - @Override 1.141 - public void contextInitialized(ServletContextEvent sce) { 1.142 - sc = sce.getServletContext(); 1.143 - 1.144 - dataSource = null; 1.145 - 1.146 - final String contextName = Optional 1.147 - .ofNullable(sc.getInitParameter(Constants.CTX_ATTR_JNDI_CONTEXT)) 1.148 - .orElse("java:comp/env"); 1.149 - final String dbSchema = Optional 1.150 - .ofNullable(sc.getInitParameter(Constants.CTX_ATTR_DB_SCHEMA)) 1.151 - .orElse(DB_DEFAULT_SCHEMA); 1.152 - final String dbDialect = sc.getInitParameter(Constants.CTX_ATTR_DB_DIALECT); 1.153 - if (dbDialect != null) { 1.154 - try { 1.155 - dialect = Dialect.valueOf(dbDialect); 1.156 - } catch (IllegalArgumentException ex) { 1.157 - LOG.error("Unknown or unsupported database dialect {}. Defaulting to {}.", dbDialect, dialect); 1.158 - } 1.159 - } 1.160 - 1.161 - try { 1.162 - LOG.debug("Trying to access JNDI context {}...", contextName); 1.163 - Context initialCtx = new InitialContext(); 1.164 - Context ctx = (Context) initialCtx.lookup(contextName); 1.165 - 1.166 - dataSource = retrieveDataSource(ctx); 1.167 - 1.168 - dataSource.ifPresent((ds) -> checkConnection(ds, dbSchema, "Checking database connection failed")); 1.169 - } catch (NamingException | ClassCastException ex) { 1.170 - LOG.error("Cannot access JNDI resources.", ex); 1.171 - } 1.172 - 1.173 - sc.setAttribute(SC_ATTR_NAME, this); 1.174 - LOG.info("Database facade injected into ServletContext."); 1.175 - } 1.176 - 1.177 - @Override 1.178 - public void contextDestroyed(ServletContextEvent sce) { 1.179 - dataSource = null; 1.180 - } 1.181 -}