View Javadoc

1   /*
2    * Copyright (c) 2004 International Decision Systems, Inc.  All Rights Reserved.
3    *
4    * By using this Software, You acknowledge that the Software is a valuable asset
5    * and trade secret of either International Decision Systems, Inc. ("IDSI") or a
6    * third party supplier of IDSI and constitutes confidential and proprietary
7    * information.
8    *
9    * NEITHER IDSI NOR ANY AGENT OR PERSON ACTING FOR OR WITH IDSI HAS MADE OR DOES
10   * MAKE ANY STATEMENTS, AFFIRMATIONS, REPRESENTATIONS OR WARRANTIES WHATSOEVER
11   * TO YOU, WHETHER EXPRESS OR IMPLIED, AS TO THE SOFTWARE, THE QUALITY OR
12   * CONDITION OF THE SOFTWARE, OR THE OPERATING CHARACTERISTICS OR RELIABILITY OF
13   * THE SOFTWARE, OR ITS SUITABILITY FOR ANY GENERAL OR PARTICULAR PURPOSE, OR AS
14   * TO ANY OTHER MATTER WHATSOEVER; ANY AND ALL OTHER WARRANTIES INCLUDING
15   * WITHOUT LIMITATION ANY WARRANTIES IMPLIED BY LAW, SUCH AS THE IMPLIED
16   * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND TITLE,
17   * USE AND NON-INFRINGEMENT; ARE HEREBY EXPRESSLY DISCLAIMED AND EXCLUDED.
18  */
19  package net.sourceforge.addam.ddlrun;
20  
21  
22  import javax.naming.InitialContext;
23  import javax.servlet.ServletContext;
24  import javax.servlet.ServletContextEvent;
25  import javax.servlet.ServletContextListener;
26  import javax.sql.DataSource;
27  
28  import net.sourceforge.addam.ddlrun.custom.IDSCompletedScriptRetriever;
29  import net.sourceforge.addam.ddlrun.custom.IDSRunLogger;
30  import net.sourceforge.addam.ddlrun.custom.IDSStartFolderRetriever;
31  import net.sourceforge.addam.ddlrun.grammars.ScriptGrammar;
32  import net.sourceforge.addam.ddlrun.grammars.ScriptGrammarManager;
33  import net.sourceforge.addam.ddlrun.runners.DeploymentRunner;
34  import net.sourceforge.addam.ddlrun.runners.DeploymentRunnerFactory;
35  import net.sourceforge.addam.ddlrun.utils.ClassLoaderReaderFactory;
36  import net.sourceforge.addam.ddlrun.utils.ResourceReaderFactory;
37  import net.sourceforge.addam.ddlrun.utils.RunLogger;
38  
39  import java.io.File;
40  import java.io.FileWriter;
41  import java.io.IOException;
42  import java.sql.Connection;
43  import java.sql.SQLException;
44  import java.text.MessageFormat;
45  import java.util.ArrayList;
46  import java.util.Date;
47  import java.util.Properties;
48  import java.util.regex.Matcher;
49  import java.util.regex.Pattern;
50  
51  /**
52   * @author TIM3
53   * @since Mar 25, 2005
54   */
55  public class ServletContainerUpgrader implements ServletContextListener, RunLogger {
56  
57      ServletContext sCtx = null;
58      FileWriter errorLog = null;
59      private int scriptsExecuted = 0;
60      protected String dbversion = null;
61  
62      /**
63       * kicks off an UpgradeRunner - requires a configured DataSource and a
64       * configured upgrade folder and upgrade script
65       */
66      public void contextInitialized(final ServletContextEvent event) {
67          sCtx = event.getServletContext();
68          errorLog = getErrorLogFileWriter(sCtx);
69          Connection connection = null;
70          try {
71              InitialContext iCtx = new InitialContext();
72              DataSource dataSource = null;
73              try {
74                  dataSource = (DataSource) iCtx.lookup("java:comp/env/jdbc/AutoUpgradeDS");
75              } catch (Exception e) {
76                  // autoupgrade data source not configured
77                  String logMessage = "unable to locate DataSource, skipping autoupgrade";
78                  log(sCtx, logMessage, errorLog, null);
79                  return;
80              }
81              try {
82                  connection = dataSource.getConnection();
83                  if (connection != null) {
84                      // needed due to weblogic bug
85                      connection.setAutoCommit(false);
86                  }
87              } catch (Exception e) {
88                  // data source not configured correctly
89                  String logMessage = "unable to create connection, skipping autoupgrade";
90                  log(sCtx, logMessage, errorLog, e);
91                  return;
92              }
93              ScriptGrammar grammar = new ScriptGrammarManager().getGrammar(connection);
94              ResourceReaderFactory factory = new ClassLoaderReaderFactory("database/upgrade/" + grammar.name());
95              if (grammar == null) {
96                  String logMessage = "" + connection + " does not use a supported database driver";
97                  log(sCtx, logMessage, errorLog, null);
98                  throw new RuntimeException(logMessage);
99              }
100             DeploymentRunner runner = DeploymentRunnerFactory.getInstance().getUpgradeRunner(factory,
101                     connection,
102                     grammar,
103                     new IDSStartFolderRetriever(connection),
104                     new IDSCompletedScriptRetriever(connection));
105             runner.addRunLogger(new IDSRunLogger(connection, dbversion));
106             runner.addRunLogger(this);
107             runner.run("upgrade.drv");
108             if (scriptsExecuted == 0) {
109                 log(sCtx, "no upgrades necessary", errorLog, null);
110             } else {
111                 log(sCtx, "executed " + scriptsExecuted + " scripts", errorLog, null);
112             }
113 
114         } catch (Exception e) {
115             if (errorLog != null) {
116                 try {
117                     String logMessage = "unable to execute upgrade: " + e.getMessage();
118                     log(sCtx, logMessage, errorLog, null);
119                 } catch (IOException ignored) {
120                     // what are you gonna do... can't log the first error, can't log this one
121                 }
122             }
123             throw new RuntimeException(e);
124         } finally {
125             if (connection != null) {
126                 try {
127                     connection.close();
128                 } catch (SQLException e) {
129                     // ignore
130                 }
131             }
132             if (errorLog != null) {
133                 try {
134                     errorLog.close();
135                 } catch (IOException e) {
136                     // ignore
137                 }
138             }
139         }
140     }
141 
142     public void logRunScript(String script, long time) throws Exception {
143         String logMessage = "executed " + script + " (" + time + "ms)";
144         log(sCtx, logMessage, errorLog, null);
145         scriptsExecuted++;
146     }
147 
148     public void logRunComplete(String group, long time) throws Exception {
149         String logMessage = "completed execution of available scripts in " + group + " (" + time + "ms)";
150         log(sCtx, logMessage, errorLog, null);
151     }
152 
153     public void logRunFailure(String group, String script, Exception e) throws Exception {
154         String logMessage = "failed to execute " + group + "/" + script;
155         log(sCtx, logMessage, errorLog, e);
156         throw new RuntimeException(logMessage, e);
157     }
158 
159     private void log(final ServletContext sCtx, String message, final FileWriter errorLog, Exception e) throws IOException {
160         Throwable t = (e == null) ? null : e.fillInStackTrace();
161         if (t != null) {
162             sCtx.log(message, t);
163         } else {
164             sCtx.log(message);
165         }
166         if (errorLog != null) {
167             if (t != null) {
168                 errorLog.write(message + "\n" + t.toString() + "\n");
169             } else {
170                 errorLog.write(message + "\n");
171             }
172             errorLog.flush();
173         }
174     }
175 
176     /**
177      * 
178      * @param sCtx
179      * @return
180      * @todo defer creation of file until actual writing error
181      */
182     private FileWriter getErrorLogFileWriter(ServletContext sCtx) {
183         FileWriter errorLog = null;
184         String errorLogFormat = sCtx.getInitParameter("error.log");
185         if (errorLogFormat == null) {
186             sCtx.log("dbupgrade log file undefined, using servlet context log only");
187         } else {
188             try {  // todo defer creation of file until actual writing error
189                 String fileName = PropertiesMessageFormat.format(errorLogFormat, System.getProperties());
190                 File file = new File(fileName);
191                 file.getParentFile().mkdirs();
192                 errorLog = new FileWriter(file, true); //append
193             } catch (Exception e) {
194                 sCtx.log("unable to create log file from " + errorLogFormat, e);
195             }
196         }
197         return errorLog;
198     }
199 
200     /**
201      * does nothing; required for ServletContextListener implementation
202      */
203     public void contextDestroyed(ServletContextEvent event) {
204     }
205 
206 
207     static class PropertiesMessageFormat {
208         static String format(String format, Properties properties) {
209             Pattern p = Pattern.compile("\\{([^\\{\\}\\,]*)[\\}|\\,]");
210             Matcher m = p.matcher(format);
211             StringBuffer buf = new StringBuffer();
212             ArrayList args = new ArrayList();
213             while (m.find()) {
214                 int location = args.size(); // MessageFormat is zero-based so start here
215                 String match = m.group(0);
216                 String variable = m.group(1);
217                 String replacement = "{" + location + (match.endsWith(",") ? "," : "}");
218                 args.add(getReplacement(variable, properties));
219                 m.appendReplacement(buf, replacement);
220             }
221             m.appendTail(buf);
222             return MessageFormat.format(buf.toString(), args.toArray());
223         }
224 
225         static Object getReplacement(String variable, Properties properties) {
226             Object replacement = properties.getProperty(variable);
227             if (replacement == null) {
228                 if (variable.equals("current.time")) {
229                     replacement = new Date();
230                 } else {
231                     replacement = "{" + variable + "}";
232                 }
233             }
234             return replacement;
235         }
236     }
237 
238 }