src/main/java/de/uapcore/lightpit/DatabaseFacade.java

changeset 33
fd8c40ff78c3
parent 29
27a0fdd7bca7
child 34
824d4042c857
equal deleted inserted replaced
32:63a31871189e 33:fd8c40ff78c3
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 * 27 *
28 */ 28 */
29 package de.uapcore.lightpit; 29 package de.uapcore.lightpit;
30 30
31 import java.sql.Connection; 31 import org.slf4j.Logger;
32 import java.sql.DatabaseMetaData; 32 import org.slf4j.LoggerFactory;
33 import java.sql.SQLException; 33
34 import java.util.Optional;
35 import javax.naming.Context; 34 import javax.naming.Context;
36 import javax.naming.InitialContext; 35 import javax.naming.InitialContext;
37 import javax.naming.NamingException; 36 import javax.naming.NamingException;
38 import javax.servlet.ServletContext; 37 import javax.servlet.ServletContext;
39 import javax.servlet.ServletContextEvent; 38 import javax.servlet.ServletContextEvent;
40 import javax.servlet.ServletContextListener; 39 import javax.servlet.ServletContextListener;
41 import javax.servlet.annotation.WebListener; 40 import javax.servlet.annotation.WebListener;
42 import javax.sql.DataSource; 41 import javax.sql.DataSource;
43 import org.slf4j.Logger; 42 import java.sql.Connection;
44 import org.slf4j.LoggerFactory; 43 import java.sql.DatabaseMetaData;
44 import java.sql.SQLException;
45 import java.util.Optional;
45 46
46 /** 47 /**
47 * Provides access to different privilege layers within the database. 48 * Provides access to different privilege layers within the database.
48 */ 49 */
49 @WebListener 50 @WebListener
50 public final class DatabaseFacade implements ServletContextListener { 51 public final class DatabaseFacade implements ServletContextListener {
51 52
52 private static final Logger LOG = LoggerFactory.getLogger(DatabaseFacade.class); 53 private static final Logger LOG = LoggerFactory.getLogger(DatabaseFacade.class);
53 54
54 /** 55 /**
55 * Timeout in seconds for the validation test. 56 * Timeout in seconds for the validation test.
56 */ 57 */
57 private static final int DB_TEST_TIMEOUT = 10; 58 private static final int DB_TEST_TIMEOUT = 10;
58 59
59 public static enum Dialect { 60 public enum Dialect {
60 Postgres; 61 Postgres
61 } 62 }
62 63
63 /** 64 /**
64 * The database dialect to use. 65 * The database dialect to use.
65 * 66 * <p>
66 * May be override by context parameter. 67 * May be override by context parameter.
67 * 68 *
68 * @see Constants#CTX_ATTR_DB_DIALECT 69 * @see Constants#CTX_ATTR_DB_DIALECT
69 */ 70 */
70 private Dialect dialect = Dialect.Postgres; 71 private Dialect dialect = Dialect.Postgres;
71 72
72 /** 73 /**
80 81
81 /** 82 /**
82 * The attribute name in the Servlet context under which an instance of this class can be found. 83 * The attribute name in the Servlet context under which an instance of this class can be found.
83 */ 84 */
84 public static final String SC_ATTR_NAME = DatabaseFacade.class.getName(); 85 public static final String SC_ATTR_NAME = DatabaseFacade.class.getName();
85 private ServletContext sc; 86
86
87 private static final String DS_JNDI_NAME = "jdbc/lightpit/app"; 87 private static final String DS_JNDI_NAME = "jdbc/lightpit/app";
88 private Optional<DataSource> dataSource; 88 private DataSource dataSource;
89 89
90 /** 90 /**
91 * Returns the data source. 91 * Returns the data source.
92 * 92 *
93 * The Optional returned should never be empty. However, if something goes 93 * The Optional returned should never be empty. However, if something goes
95 * Hence, users of this data source are forced to check the existence. 95 * Hence, users of this data source are forced to check the existence.
96 * 96 *
97 * @return a data source 97 * @return a data source
98 */ 98 */
99 public Optional<DataSource> getDataSource() { 99 public Optional<DataSource> getDataSource() {
100 return dataSource; 100 // TODO: this should not be an optional, if an empty optional is actually an exception
101 return Optional.ofNullable(dataSource);
101 } 102 }
102 103
103 public Dialect getSQLDialect() { 104 public Dialect getSQLDialect() {
104 return dialect; 105 return dialect;
105 } 106 }
106 107
107 private static void checkConnection(DataSource ds, String testSchema, String errMsg) { 108 private static void checkConnection(DataSource ds, String testSchema) {
108 try (Connection conn = ds.getConnection()) { 109 try (Connection conn = ds.getConnection()) {
109 if (!conn.isValid(DB_TEST_TIMEOUT)) { 110 if (!conn.isValid(DB_TEST_TIMEOUT)) {
110 throw new SQLException("Validation check failed."); 111 throw new SQLException("Validation check failed.");
111 } 112 }
112 if (conn.isReadOnly()) { 113 if (conn.isReadOnly()) {
116 throw new SQLException(String.format("Connection is not configured to use the schema %s.", testSchema)); 117 throw new SQLException(String.format("Connection is not configured to use the schema %s.", testSchema));
117 } 118 }
118 DatabaseMetaData metaData = conn.getMetaData(); 119 DatabaseMetaData metaData = conn.getMetaData();
119 LOG.info("Connections as {} to {}/{} ready to go.", metaData.getUserName(), metaData.getURL(), conn.getSchema()); 120 LOG.info("Connections as {} to {}/{} ready to go.", metaData.getUserName(), metaData.getURL(), conn.getSchema());
120 } catch (SQLException ex) { 121 } catch (SQLException ex) {
121 LOG.error(errMsg, ex); 122 LOG.error("Checking database connection failed", ex);
122 } 123 }
123 } 124 }
124 125
125 private static Optional<DataSource> retrieveDataSource(Context ctx) { 126 private static DataSource retrieveDataSource(Context ctx) {
126 DataSource ret = null; 127 DataSource ret = null;
127 try { 128 try {
128 ret = (DataSource)ctx.lookup(DS_JNDI_NAME); 129 ret = (DataSource) ctx.lookup(DS_JNDI_NAME);
129 LOG.info("Data source retrieved."); 130 LOG.info("Data source retrieved.");
130 } catch (NamingException ex) { 131 } catch (NamingException ex) {
131 LOG.error("Data source {} not available.", DS_JNDI_NAME); 132 LOG.error("Data source {} not available.", DS_JNDI_NAME);
132 LOG.error("Reason for the missing data source: ", ex); 133 LOG.error("Reason for the missing data source: ", ex);
133 } 134 }
134 return Optional.ofNullable(ret); 135 return ret;
135 } 136 }
136 137
137 @Override 138 @Override
138 public void contextInitialized(ServletContextEvent sce) { 139 public void contextInitialized(ServletContextEvent sce) {
139 sc = sce.getServletContext(); 140 ServletContext sc = sce.getServletContext();
140 141
141 dataSource = null; 142 dataSource = null;
142 143
143 final String contextName = Optional 144 final String contextName = Optional
144 .ofNullable(sc.getInitParameter(Constants.CTX_ATTR_JNDI_CONTEXT)) 145 .ofNullable(sc.getInitParameter(Constants.CTX_ATTR_JNDI_CONTEXT))
157 158
158 try { 159 try {
159 LOG.debug("Trying to access JNDI context {}...", contextName); 160 LOG.debug("Trying to access JNDI context {}...", contextName);
160 Context initialCtx = new InitialContext(); 161 Context initialCtx = new InitialContext();
161 Context ctx = (Context) initialCtx.lookup(contextName); 162 Context ctx = (Context) initialCtx.lookup(contextName);
162 163
163 dataSource = retrieveDataSource(ctx); 164 dataSource = retrieveDataSource(ctx);
164 165
165 dataSource.ifPresent((ds) -> checkConnection(ds, dbSchema, "Checking database connection failed")); 166 if (dataSource != null) {
167 checkConnection(dataSource, dbSchema);
168 }
166 } catch (NamingException | ClassCastException ex) { 169 } catch (NamingException | ClassCastException ex) {
167 LOG.error("Cannot access JNDI resources.", ex); 170 LOG.error("Cannot access JNDI resources.", ex);
168 } 171 }
169 172
170 sc.setAttribute(SC_ATTR_NAME, this); 173 sc.setAttribute(SC_ATTR_NAME, this);

mercurial