001: /* Copyright (C) 2003 Finalist IT Group
002: *
003: * This file is part of JAG - the Java J2EE Application Generator
004: *
005: * JAG is free software; you can redistribute it and/or modify
006: * it under the terms of the GNU General Public License as published by
007: * the Free Software Foundation; either version 2 of the License, or
008: * (at your option) any later version.
009: * JAG is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: * You should have received a copy of the GNU General Public License
014: * along with JAG; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
016: */
017: package com.finalist.jaggenerator;
018:
019: import org.w3c.dom.Document;
020: import org.w3c.dom.Element;
021: import org.w3c.dom.NodeList;
022:
023: import javax.swing.*;
024: import java.io.ByteArrayOutputStream;
025: import java.io.File;
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.util.*;
029: import java.util.jar.JarEntry;
030: import java.util.jar.JarFile;
031:
032: /**
033: * The DatabaseManager handles JAG's generic database support.
034: * This enables JAG to connect to any database, provided the database has a JDBC driver.
035: *
036: * @author Michael O'Connor - Finalist IT Group
037: */
038: public class DatabaseManager {
039: private static DatabaseManager ourInstance;
040: private static ArrayList databases = new ArrayList();
041:
042: private static final String DATABASE = "database";
043: private static final String SUPPORTED_DATABASES = "supported-databases";
044: public static final String NAME = "name";
045: private static final String DRIVER_CLASS = "driver-class";
046: private static final String APPSERVER_TYPEMAPPING = "appserver-typemapping";
047: private static final int READ_BUFFER_SIZE = 2048;
048: private static final String DOT_CLASS = ".class";
049: public static final String APPSERVER_TYPEMAPPINGS = "appserver-typemappings";
050:
051: private static String[] typeMappings;
052: private static final Database[] DATABASE_ARRAY = new Database[0];
053: private static final String FILE = "file";
054:
055: /**
056: * The DatabaseManager is a singleton - this method obtains the one and only instance.
057: * @return
058: */
059: public synchronized static DatabaseManager getInstance() {
060: if (ourInstance == null) {
061: ourInstance = new DatabaseManager();
062: }
063: return ourInstance;
064: }
065:
066: private DatabaseManager() {
067: load();
068: }
069:
070: /**
071: * Adds JDBC driver(s) from the specified File.
072: *
073: * @param driverFile
074: * @return a List of Database objects, one for each driver found in the file.
075: */
076: public List addDrivers(final File driverFile) {
077: boolean driverFound = false;
078: List newDatabases = new ArrayList();
079:
080: try {
081: final JarFile jar = new JarFile(driverFile);
082: Enumeration e = jar.entries();
083:
084: while (e.hasMoreElements()) {
085: JarEntry entry = (JarEntry) e.nextElement();
086: if (entry.getName().endsWith(DOT_CLASS)) {
087: String className = entry.getName()
088: .replace('/', '.').substring(0,
089: entry.getName().indexOf(DOT_CLASS));
090: try {
091: Class clazz = new JarClassLoader(jar)
092: .loadClass(className);
093: if (Arrays.asList(clazz.getInterfaces())
094: .contains(java.sql.Driver.class)) {
095: boolean alreadyKnown = false;
096: driverFound = true;
097: Iterator i = databases.iterator();
098: while (i.hasNext()) {
099: Database database = (Database) i.next();
100: if (database.getDriverClass().equals(
101: className)) {
102: JOptionPane
103: .showMessageDialog(
104: null,
105: "The driver '"
106: + className
107: + "' will not be added, as it is already known!",
108: "Duplicate Driver",
109: javax.swing.JOptionPane.INFORMATION_MESSAGE);
110: alreadyKnown = true;
111: break;
112: }
113: }
114:
115: if (!alreadyKnown) {
116: Database newDb = new Database();
117: newDb.setDriverClass(className);
118: newDb.setFilename(driverFile
119: .getAbsolutePath());
120: newDatabases.add(newDb);
121: }
122: }
123:
124: } catch (Throwable cnf) {
125: System.out.println(cnf);
126: }
127: }
128: }
129:
130: } catch (IOException e) {
131: e.printStackTrace(); //To change body of catch statement use Options | File Templates.
132: }
133:
134: if (!driverFound) {
135: JOptionPane.showMessageDialog(null,
136: "JAG could not find any JDBC drivers in file: "
137: + driverFile, "No Drivers Found!",
138: javax.swing.JOptionPane.INFORMATION_MESSAGE);
139: } else if (newDatabases.size() > 0) {
140: JOptionPane.showMessageDialog(null, "Found "
141: + newDatabases.size() + " JDBC driver"
142: + (newDatabases.size() == 1 ? "" : "s")
143: + " in file: " + driverFile,
144: "JDBC Driver"
145: + (newDatabases.size() == 1 ? "" : "s")
146: + " Found!",
147: javax.swing.JOptionPane.INFORMATION_MESSAGE);
148: }
149:
150: return newDatabases;
151: }
152:
153: /**
154: * Gets the list of supported databases.
155: *
156: * @return an array of all currently supported databases.
157: */
158: public Database[] getSupportedDatabases() {
159: return (Database[]) databases.toArray(DATABASE_ARRAY);
160: }
161:
162: /**
163: * Resets the list of supported databases (and also resets the list in the Datasource panel).
164: * @param editedValues
165: */
166: public void setDatabases(ArrayList editedValues) {
167: databases = editedValues;
168: JagGenerator.jagGenerator.root.datasource
169: .setSupportedDatabases(getSupportedDatabases());
170: }
171:
172: /**
173: * Gets the list of possible appserver typemappings.
174: * @return
175: */
176: public String[] getTypeMappings() {
177: return typeMappings;
178: }
179:
180: /**
181: * Persists the supported database information by appending an XML element to the config Document.
182: *
183: * @param root The XML Element under which we append the XML.
184: */
185: public Element appendXML(Element root) {
186: Document doc = root.getOwnerDocument();
187: Element dbRoot = doc.createElement(SUPPORTED_DATABASES);
188: Iterator i = databases.iterator();
189: while (i.hasNext()) {
190: Database dbInfo = (Database) i.next();
191: Element database = doc.createElement(DATABASE);
192: Element child = doc.createElement(NAME);
193: if (dbInfo.getDbName() != null) {
194: child.appendChild(doc
195: .createTextNode(dbInfo.getDbName()));
196: }
197: database.appendChild(child);
198: child = doc.createElement(DRIVER_CLASS);
199: if (dbInfo.getDriverClass() != null) {
200: child.appendChild(doc.createTextNode(dbInfo
201: .getDriverClass()));
202: }
203: database.appendChild(child);
204: child = doc.createElement(APPSERVER_TYPEMAPPING);
205: if (dbInfo.getTypeMapping() != null) {
206: child.appendChild(doc.createTextNode(dbInfo
207: .getTypeMapping()));
208: }
209: database.appendChild(child);
210: child = doc.createElement(FILE);
211: if (dbInfo.getFilename() != null) {
212: child.appendChild(doc.createTextNode(dbInfo
213: .getFilename()));
214: }
215: database.appendChild(child);
216: dbRoot.appendChild(database);
217: }
218: return dbRoot;
219: }
220:
221: /**
222: * Reads in the supported database info from the config doc.
223: */
224: private void load() {
225: databases.clear();
226: Document doc = ConfigManager.getInstance().getDocument();
227: NodeList databaseNodes = doc.getElementsByTagName(DATABASE);
228: for (int i = 0; i < databaseNodes.getLength(); i++) {
229: Database dbInfo = new Database();
230: Element database = (Element) databaseNodes.item(i);
231: NodeList children = database.getChildNodes();
232: for (int j = 0; j < children.getLength(); j++) {
233: if (children.item(j) instanceof Element) {
234: Element child = (Element) children.item(j);
235: if (NAME.equals(child.getNodeName())) {
236: dbInfo.setDbName(child.getFirstChild()
237: .getNodeValue());
238: } else if (DRIVER_CLASS.equals(child.getNodeName())) {
239: dbInfo.setDriverClass(child.getFirstChild()
240: .getNodeValue());
241: } else if (APPSERVER_TYPEMAPPING.equals(child
242: .getNodeName())) {
243: dbInfo.setTypeMapping(child.getFirstChild()
244: .getNodeValue());
245: } else if (FILE.equals(child.getNodeName())) {
246: dbInfo.setFilename(child.getFirstChild()
247: .getNodeValue());
248: }
249: }
250: }
251: //check validity of driver file
252: File driver = new File(dbInfo.getFilename());
253: if (!driver.exists()) {
254: JagGenerator
255: .logToConsole("Removing missing driver reference: "
256: + dbInfo.getFilename());
257: JOptionPane
258: .showMessageDialog(
259: JagGenerator.jagGenerator,
260: "The previously specified JDBC driver for '"
261: + dbInfo.getDbName()
262: + "' databases can not be located.\n"
263: + "(last known location was: "
264: + dbInfo.getFilename()
265: + ")\n"
266: + "The entry for this driver will be deleted.",
267: "Missing JDBC Driver!",
268: javax.swing.JOptionPane.ERROR_MESSAGE);
269: } else {
270:
271: databases.add(dbInfo);
272: }
273: }
274:
275: Map typeMappingsMap = ConfigManager.getInstance()
276: .retrievePropertiesFromXML(APPSERVER_TYPEMAPPINGS);
277: String[] temp = (String[]) typeMappingsMap.get(NAME);
278: if (temp != null) {
279: typeMappings = temp;
280: }
281: }
282:
283: /**
284: * This ClassLoader is necessary for grabbing Class objects from a jar file.
285: */
286: private class JarClassLoader extends ClassLoader {
287: private HashMap alreadyLoaded = new HashMap();
288: private JarFile jar;
289:
290: /**
291: * Initialises the JarClassLoader with the jar file.
292: * @param jar
293: */
294: public JarClassLoader(JarFile jar) {
295: this .jar = jar;
296: }
297:
298: public synchronized Class loadClass(String name, boolean resolve)
299: throws ClassNotFoundException {
300: Class clazz = (Class) alreadyLoaded.get(name);
301: if (clazz == null) {
302: try {
303: //is it a system class?
304: return findSystemClass(name);
305: } catch (Exception e) {
306: }
307: try {
308: //is it already loaded?
309: return Class.forName(name);
310: } catch (Exception e) {
311: }
312:
313: byte[] bytes = loadClassBytes(name);
314: try {
315: clazz = defineClass(name, bytes, 0, bytes.length);
316: } catch (Throwable t) {
317: //System.out.println("!!defineClass threw " + t);
318: }
319: alreadyLoaded.put(name, clazz);
320:
321: if (clazz == null) {
322: throw new ClassNotFoundException(name);
323: }
324: }
325:
326: if (resolve) {
327: resolveClass(clazz);
328: }
329:
330: return clazz;
331: }
332:
333: private byte[] loadClassBytes(String name) {
334: ByteArrayOutputStream temp = new ByteArrayOutputStream();
335: byte[] buffer = new byte[READ_BUFFER_SIZE];
336: String entryName = name.replace('.', '/') + DOT_CLASS;
337: InputStream in = null;
338: try {
339:
340: //todo : the jars so far have not needed inflating.. is this always the case?
341: // InflaterInputStream in = new InflaterInputStream(jar.getInputStream(entry));
342:
343: JarEntry entry = jar.getJarEntry(entryName);
344: if (entry != null) {
345: in = jar.getInputStream(entry);
346: int bytesRead = -1;
347: do {
348: bytesRead = in
349: .read(buffer, 0, READ_BUFFER_SIZE);
350: if (bytesRead != -1) {
351: temp.write(buffer, 0, bytesRead);
352: }
353: } while (bytesRead != -1);
354: }
355:
356: } catch (IOException e1) {
357: e1.printStackTrace();
358: }
359:
360: return temp.toByteArray();
361: }
362: }
363:
364: }
|