1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.finalist.jaggenerator;
18
19 import org.w3c.dom.Document;
20 import org.w3c.dom.Element;
21 import org.w3c.dom.NodeList;
22
23 import javax.swing.*;
24 import java.io.ByteArrayOutputStream;
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.util.*;
29 import java.util.jar.JarEntry;
30 import java.util.jar.JarFile;
31
32 /***
33 * The DatabaseManager handles JAG's generic database support.
34 * This enables JAG to connect to any database, provided the database has a JDBC driver.
35 *
36 * @author Michael O'Connor - Finalist IT Group
37 */
38 public class DatabaseManager {
39 private static DatabaseManager ourInstance;
40 private static ArrayList databases = new ArrayList();
41
42 private static final String DATABASE = "database";
43 private static final String SUPPORTED_DATABASES = "supported-databases";
44 public static final String NAME = "name";
45 private static final String DRIVER_CLASS = "driver-class";
46 private static final String APPSERVER_TYPEMAPPING = "appserver-typemapping";
47 private static final int READ_BUFFER_SIZE = 2048;
48 private static final String DOT_CLASS = ".class";
49 public static final String APPSERVER_TYPEMAPPINGS = "appserver-typemappings";
50
51 private static String[] typeMappings;
52 private static final Database[] DATABASE_ARRAY = new Database[0];
53 private static final String FILE = "file";
54
55 /***
56 * The DatabaseManager is a singleton - this method obtains the one and only instance.
57 * @return
58 */
59 public synchronized static DatabaseManager getInstance() {
60 if (ourInstance == null) {
61 ourInstance = new DatabaseManager();
62 }
63 return ourInstance;
64 }
65
66 private DatabaseManager() {
67 load();
68 }
69
70
71 /***
72 * Adds JDBC driver(s) from the specified File.
73 *
74 * @param driverFile
75 * @return a List of Database objects, one for each driver found in the file.
76 */
77 public List addDrivers(final File driverFile) {
78 boolean driverFound = false;
79 List newDatabases = new ArrayList();
80
81 try {
82 final JarFile jar = new JarFile(driverFile);
83 Enumeration e = jar.entries();
84
85 while (e.hasMoreElements()) {
86 JarEntry entry = (JarEntry) e.nextElement();
87 if (entry.getName().endsWith(DOT_CLASS)) {
88 String className = entry.getName().replace('/', '.').
89 substring(0, entry.getName().indexOf(DOT_CLASS));
90 try {
91 Class clazz = new JarClassLoader(jar).loadClass(className);
92 if (Arrays.asList(clazz.getInterfaces()).contains(java.sql.Driver.class)) {
93 boolean alreadyKnown = false;
94 driverFound = true;
95 Iterator i = databases.iterator();
96 while (i.hasNext()) {
97 Database database = (Database) i.next();
98 if (database.getDriverClass().equals(className)) {
99 JOptionPane.showMessageDialog(null,
100 "The driver '" + className + "' will not be added, as it is already known!",
101 "Duplicate Driver", javax.swing.JOptionPane.INFORMATION_MESSAGE);
102 alreadyKnown = true;
103 break;
104 }
105 }
106
107 if (!alreadyKnown) {
108 Database newDb = new Database();
109 newDb.setDriverClass(className);
110 newDb.setFilename(driverFile.getAbsolutePath());
111 newDatabases.add(newDb);
112 }
113 }
114
115 } catch (Throwable cnf) {
116 System.out.println(cnf);
117 }
118 }
119 }
120
121 } catch (IOException e) {
122 e.printStackTrace();
123 }
124
125 if (!driverFound) {
126 JOptionPane.showMessageDialog(null,
127 "JAG could not find any JDBC drivers in file: " + driverFile,
128 "No Drivers Found!", javax.swing.JOptionPane.INFORMATION_MESSAGE);
129 } else if (newDatabases.size() > 0) {
130 JOptionPane.showMessageDialog(null,
131 "Found " + newDatabases.size() + " JDBC driver" + (newDatabases.size() == 1 ? "" : "s") +
132 " in file: " + driverFile,
133 "JDBC Driver" + (newDatabases.size() == 1 ? "" : "s") + " Found!",
134 javax.swing.JOptionPane.INFORMATION_MESSAGE);
135 }
136
137 return newDatabases;
138 }
139
140 /***
141 * Gets the list of supported databases.
142 *
143 * @return an array of all currently supported databases.
144 */
145 public Database[] getSupportedDatabases() {
146 return (Database[]) databases.toArray(DATABASE_ARRAY);
147 }
148
149 /***
150 * Resets the list of supported databases (and also resets the list in the Datasource panel).
151 * @param editedValues
152 */
153 public void setDatabases(ArrayList editedValues) {
154 databases = editedValues;
155 JagGenerator.jagGenerator.root.datasource.setSupportedDatabases(getSupportedDatabases());
156 }
157
158 /***
159 * Gets the list of possible appserver typemappings.
160 * @return
161 */
162 public String[] getTypeMappings() {
163 return typeMappings;
164 }
165
166 /***
167 * Persists the supported database information by appending an XML element to the config Document.
168 *
169 * @param root The XML Element under which we append the XML.
170 */
171 public Element appendXML(Element root) {
172 Document doc = root.getOwnerDocument();
173 Element dbRoot = doc.createElement(SUPPORTED_DATABASES);
174 Iterator i = databases.iterator();
175 while (i.hasNext()) {
176 Database dbInfo = (Database) i.next();
177 Element database = doc.createElement(DATABASE);
178 Element child = doc.createElement(NAME);
179 if (dbInfo.getDbName() != null) {
180 child.appendChild(doc.createTextNode(dbInfo.getDbName()));
181 }
182 database.appendChild(child);
183 child = doc.createElement(DRIVER_CLASS);
184 if (dbInfo.getDriverClass() != null) {
185 child.appendChild(doc.createTextNode(dbInfo.getDriverClass()));
186 }
187 database.appendChild(child);
188 child = doc.createElement(APPSERVER_TYPEMAPPING);
189 if (dbInfo.getTypeMapping() != null) {
190 child.appendChild(doc.createTextNode(dbInfo.getTypeMapping()));
191 }
192 database.appendChild(child);
193 child = doc.createElement(FILE);
194 if (dbInfo.getFilename() != null) {
195 child.appendChild(doc.createTextNode(dbInfo.getFilename()));
196 }
197 database.appendChild(child);
198 dbRoot.appendChild(database);
199 }
200 return dbRoot;
201 }
202
203
204 /***
205 * Reads in the supported database info from the config doc.
206 */
207 private void load() {
208 databases.clear();
209 Document doc = ConfigManager.getInstance().getDocument();
210 NodeList databaseNodes = doc.getElementsByTagName(DATABASE);
211 for (int i = 0; i < databaseNodes.getLength(); i++) {
212 Database dbInfo = new Database();
213 Element database = (Element) databaseNodes.item(i);
214 NodeList children = database.getChildNodes();
215 for (int j = 0; j < children.getLength(); j++) {
216 if (children.item(j) instanceof Element) {
217 Element child = (Element) children.item(j);
218 if (NAME.equals(child.getNodeName())) {
219 dbInfo.setDbName(child.getFirstChild().getNodeValue());
220 } else if (DRIVER_CLASS.equals(child.getNodeName())) {
221 dbInfo.setDriverClass(child.getFirstChild().getNodeValue());
222 } else if (APPSERVER_TYPEMAPPING.equals(child.getNodeName())) {
223 dbInfo.setTypeMapping(child.getFirstChild().getNodeValue());
224 } else if (FILE.equals(child.getNodeName())) {
225 dbInfo.setFilename(child.getFirstChild().getNodeValue());
226 }
227 }
228 }
229
230 File driver = new File(dbInfo.getFilename());
231 if (!driver.exists()) {
232 JagGenerator.logToConsole("Removing missing driver reference: " + dbInfo.getFilename());
233 JOptionPane.showMessageDialog(JagGenerator.jagGenerator,
234 "The previously specified JDBC driver for '" + dbInfo.getDbName() + "' databases can not be located.\n" +
235 "(last known location was: " + dbInfo.getFilename() + ")\n" +
236 "The entry for this driver will be deleted.",
237 "Missing JDBC Driver!", javax.swing.JOptionPane.ERROR_MESSAGE);
238 } else {
239
240 databases.add(dbInfo);
241 }
242 }
243
244 Map typeMappingsMap = ConfigManager.getInstance().retrievePropertiesFromXML(APPSERVER_TYPEMAPPINGS);
245 String[] temp = (String[]) typeMappingsMap.get(NAME);
246 if (temp != null) {
247 typeMappings = temp;
248 }
249 }
250
251 /***
252 * This ClassLoader is necessary for grabbing Class objects from a jar file.
253 */
254 private class JarClassLoader extends ClassLoader {
255 private HashMap alreadyLoaded = new HashMap();
256 private JarFile jar;
257
258 /***
259 * Initialises the JarClassLoader with the jar file.
260 * @param jar
261 */
262 public JarClassLoader(JarFile jar) {
263 this.jar = jar;
264 }
265
266 public synchronized Class loadClass(String name, boolean resolve)
267 throws ClassNotFoundException {
268 Class clazz = (Class) alreadyLoaded.get(name);
269 if (clazz == null) {
270 try {
271
272 return findSystemClass(name);
273 } catch (Exception e) {
274 }
275 try {
276
277 return Class.forName(name);
278 } catch (Exception e) {
279 }
280
281 byte[] bytes = loadClassBytes(name);
282 try {
283 clazz = defineClass(name, bytes, 0, bytes.length);
284 } catch (Throwable t) {
285
286 }
287 alreadyLoaded.put(name, clazz);
288
289 if (clazz == null) {
290 throw new ClassNotFoundException(name);
291 }
292 }
293
294 if (resolve) {
295 resolveClass(clazz);
296 }
297
298 return clazz;
299 }
300
301 private byte[] loadClassBytes(String name) {
302 ByteArrayOutputStream temp = new ByteArrayOutputStream();
303 byte[] buffer = new byte[READ_BUFFER_SIZE];
304 String entryName = name.replace('.', '/') + DOT_CLASS;
305 InputStream in = null;
306 try {
307
308
309
310
311 JarEntry entry = jar.getJarEntry(entryName);
312 if (entry != null) {
313 in = jar.getInputStream(entry);
314 int bytesRead = -1;
315 do {
316 bytesRead = in.read(buffer, 0, READ_BUFFER_SIZE);
317 if (bytesRead != -1) {
318 temp.write(buffer, 0, bytesRead);
319 }
320 } while (bytesRead != -1);
321 }
322
323 } catch (IOException e1) {
324 e1.printStackTrace();
325 }
326
327 return temp.toByteArray();
328 }
329 }
330
331 }
332