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;
20  
21  
22  import java.io.*;
23  import java.lang.reflect.Method;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.sql.Connection;
27  import java.sql.Driver;
28  import java.sql.SQLException;
29  import java.util.*;
30  
31  import net.sourceforge.addam.ddlrun.custom.IDSCompletedScriptRetriever;
32  import net.sourceforge.addam.ddlrun.custom.IDSRunLogger;
33  import net.sourceforge.addam.ddlrun.custom.IDSStartFolderRetriever;
34  import net.sourceforge.addam.ddlrun.grammars.ScriptGrammar;
35  import net.sourceforge.addam.ddlrun.grammars.ScriptGrammarManager;
36  import net.sourceforge.addam.ddlrun.runners.DeploymentRunner;
37  import net.sourceforge.addam.ddlrun.runners.DeploymentRunnerFactory;
38  import net.sourceforge.addam.ddlrun.utils.ClassLoaderReaderFactory;
39  import net.sourceforge.addam.ddlrun.utils.ResourceReaderFactory;
40  import net.sourceforge.addam.ddlrun.utils.RunLogger;
41  
42  /**
43   * this class is intended to be bundled as the main for a jar file that includes
44   * the scripts necessary to run the upgrade
45   */
46  public class Main {
47  
48      private static final String separator = "/";
49      // getting the VERSION only works when the classes in addam.jar have been copied to
50      // another jar file with a valid manifest
51      private static final Package PKG = Main.class.getPackage();
52      private static final String VERSION = PKG.getSpecificationTitle() + " " + PKG.getSpecificationVersion() + " (" + PKG.getImplementationVersion() + ")";
53      private static final String URLPREFIX = "jar:file:/";
54      private static final String JAR_FILE_NAME = getJarFileName();
55      private static final String JAR_FILE_DIR = getJarFileDir();
56  
57      private Main() {
58      }
59  
60      public static void main(String args[]) {
61  
62          String configFileName = JAR_FILE_NAME.replace(".jar",".conf");
63  
64          if (args.length < 1 || args.length > 2) {
65              usage(configFileName);
66              return;
67          }
68  
69          if (args.length == 2) {
70              configFileName = args[1];
71          }
72  
73          Map<String,String> conf;
74          try {
75              conf = loadConfig(configFileName);
76          } catch (IOException e) {
77              System.err.println("unable to load file " + configFileName);
78              e.printStackTrace();
79              return;
80          }
81  
82          String url = getConf(conf,"url");
83          String username = getConf(conf,"username");
84          String password = getConf(conf,"password");
85          String driverjar = getConf(conf,"driverjar");
86          String driverName = getConf(conf,"driver");
87          ScriptGrammar grammar;
88          Driver driver;
89  
90          try {
91              ClassPathHacker.addFile(driverjar);
92              Class driverClass = Class.forName(driverName);
93              driver = (Driver) driverClass.newInstance();
94          } catch (ClassNotFoundException e) {
95              throw new RuntimeException("unknown driver class " + driverName + "\n" +
96                      "make sure to place the driver .jar file in the same directory as the script jar file");
97          } catch (IllegalAccessException e) {
98              throw new RuntimeException("can't instantiate driver " + driverName, e);
99          } catch (InstantiationException e) {
100             throw new RuntimeException("can't instantiate driver " + driverName, e);
101         } catch (IOException e) {
102             throw new RuntimeException("can't instantiate driver " + driverName, e);
103         }
104 
105         String type;
106         if ("-install".equals(args[0])) {
107             type = "install";
108         } else if ("-upgrade".equals(args[0])) {
109             type = "upgrade";
110         } else if ("-version".equals(args[0])) {
111             System.out.println(VERSION);
112             return;
113         } else {
114             usage(configFileName);
115             return;
116         }
117 
118         // use internal scripts, e.g. upgrade/oracle/upgrade.drv
119         String script = type + ".drv";
120         DeploymentRunner runner;
121         Connection connection = null;
122         try {
123             System.out.println("connecting to " + url);
124             Properties props = new Properties();
125             props.put("user", username);
126             props.put("password", password);
127             connection = driver.connect(url, props);
128 
129             grammar = new ScriptGrammarManager().getGrammar(connection);
130             if (grammar == null) {
131                 throw new RuntimeException("unsupported database vendor");
132             }
133             String resourcePath = "database" + separator + type + separator + grammar.name();
134             ResourceReaderFactory factory = new ClassLoaderReaderFactory(resourcePath);
135 
136             if ("install".equals(type)) {
137                 runner = DeploymentRunnerFactory.getInstance().getInstallRunner(factory,
138                         connection,
139                         grammar);
140             } else if ("upgrade".equals(type)) {
141                 runner = DeploymentRunnerFactory.getInstance().getUpgradeRunner(factory,
142                         connection,
143                         grammar,
144                         new IDSStartFolderRetriever(connection),
145                         new IDSCompletedScriptRetriever(connection));
146                 runner.addRunLogger(new IDSRunLogger(connection, VERSION));
147             } else {
148                 throw new RuntimeException("action must be install or upgrade");
149             }
150             runner.addRunLogger(new RunLogger() {
151                 int count = 0;
152                 public void logRunScript(String script, long time) throws Exception {
153                     System.out.println("executed " + script + " (" + time + "ms)");
154                 }
155 
156                 public void logRunComplete(String group, long time) throws Exception {
157                     System.out.println("completed execution of available scripts in " + group + " (" + time + "ms)");
158                 }
159 
160                 public void logRunFailure(String group, String script, Exception e) throws Exception {
161                     System.out.println(group + separator + script + " failed!");
162                 }
163             });
164             runner.run(script);
165         } catch (Exception e) {
166             System.err.println(e.getMessage());
167             e.printStackTrace(System.err);
168             System.exit(1);
169         } finally {
170             try {
171                 connection.close();
172             } catch (SQLException e) {
173                 e.printStackTrace();
174             }
175         }
176     }
177 
178     private static String getConf(Map<String,String> map, String key) {
179         String value = map.get(key);
180         if (value == null || value.length()==0) {
181             System.err.println("missing config entry for " + key);
182             System.exit(-1);
183         }
184         return value;
185     }
186 
187     private static Map<String,String> loadConfig(String configFileName) throws IOException {
188         Properties props = new Properties();
189         File file = new File(configFileName);
190         if (!file.exists()) {
191              file = new File(JAR_FILE_DIR,configFileName);
192         }
193         FileInputStream fis = new FileInputStream(file);
194         props.load(fis);
195 
196         Map<String,String> config = new HashMap<String,String>();
197         for (Map.Entry entry : props.entrySet()) {
198             config.put(entry.getKey().toString(),entry.getValue().toString());
199         }
200 
201         String password = props.getProperty("password");
202         if (password != null && password.length()>0) {
203             props.setProperty("password","");
204             System.out.println("password stripped from " + configFileName + " for security purposes");
205             FileOutputStream fos = new FileOutputStream(file);
206             props.store(fos,"updated " + new Date() + " by " + JAR_FILE_NAME);
207         }
208         return config;
209     }
210 
211     private static void usage(String configFileName) {
212         System.err.printf("usage " + JAR_FILE_NAME + " -version | -install | -upgrade [config file]\n");
213         System.err.println();
214         System.err.println("if config file is not specified, \"" + configFileName + "\" will be used");
215         System.err.println("config file must contain elements from the following example:");
216         System.err.println(" url = jdbc:oracle:thin:@host:1521:sid");
217         System.err.println(" driver = oracle.jdbc.driver.OracleDriver");
218         System.err.println(" driverjar = ojdbc14.jar");
219         System.err.println(" username = " + System.getProperty("user.name"));
220         System.err.println(" password = <password>");
221     }
222 
223     private static String getJarFileName() {
224         String jarURL = ClassLoader.getSystemResource("net/sourceforge/addam/Main.class").toString();
225         int i = jarURL.indexOf('!');
226         if (i > 0) jarURL = jarURL.substring(0, i);
227         i = jarURL.lastIndexOf('/');
228         if (i > 0 && jarURL.length() > 1) jarURL = jarURL.substring(i + 1);
229         return jarURL;
230     }
231 
232     private static String getJarFileDir() {
233         String jarURL = ClassLoader.getSystemResource("net/sourceforge/addam/Main.class").toString();
234         int i = jarURL.indexOf('!');
235         if (i > 0) jarURL = jarURL.substring(0, i);
236         if (jarURL.startsWith(URLPREFIX) && jarURL.length() > URLPREFIX.length()) {
237             jarURL = jarURL.substring(URLPREFIX.length());
238         }
239         i = jarURL.lastIndexOf('/');
240         if (i > 0) jarURL = jarURL.substring(0,i);
241 
242         return jarURL;
243     }
244 
245     public static class ClassPathHacker {
246 
247         public static void addFile(String fileName) throws IOException {
248             File file = new File(fileName);
249 
250             if (!file.exists() && !file.isAbsolute()) {
251                 file = new File(JAR_FILE_DIR,fileName);
252             }
253 
254             if (!file.exists()) {
255                 throw new FileNotFoundException(fileName);
256             }
257 
258             URLClassLoader classloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
259             try {
260                 Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
261                 method.setAccessible(true);
262                 method.invoke(classloader, file.toURL());
263             } catch (Throwable t) {
264                 t.printStackTrace();
265                 throw new IOException("Error, could not add URL to system classloader");
266             }
267         }
268     }
269 }