001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: *
013: * @created Dec 30, 2005
014: * @author James Dixon
015: */
016: package org.pentaho.core.admin.datasources.jboss;
017:
018: import java.io.File;
019: import java.io.FileOutputStream;
020: import java.io.IOException;
021: import java.util.HashMap;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.dom4j.Document;
028: import org.dom4j.DocumentFactory;
029: import org.dom4j.Element;
030: import org.dom4j.Node;
031: import org.dom4j.io.OutputFormat;
032: import org.dom4j.io.XMLWriter;
033: import org.pentaho.core.admin.datasources.DataSourceInfo;
034: import org.pentaho.core.admin.datasources.ServerDatasourceAdmin;
035: import org.pentaho.core.system.PentahoSystem;
036: import org.pentaho.core.util.XmlHelper;
037: import org.pentaho.messages.Messages;
038: import org.pentaho.util.logging.Logger;
039:
040: public class JBossDatasourceAdmin extends ServerDatasourceAdmin {
041: private static final String JBOSS_JNDI_NAME = "jndi-name"; //$NON-NLS-1$
042:
043: private static final Log logger = LogFactory
044: .getLog(JBossDatasourceAdmin.class);
045: private Map dsFileMap = new HashMap();
046:
047: public Map listContainerDataSources() {
048: Map jBossDSList = new HashMap();
049: // first get all the datasources listed in the jboss-web.xml
050: File jbossWeb = getJBossWebXmlFile();
051: if (jbossWeb == null) {
052: return jBossDSList;
053: }
054: Document doc = getWebXmlDoc(jbossWeb);
055: List resourceList = doc.selectNodes("jboss-web/resource-ref"); //$NON-NLS-1$
056: if (dsFileMap.size() == 0) {
057: // read all of the -ds.xml files from the deploy directory
058: File deployDir = jbossWeb.getParentFile().getParentFile()
059: .getParentFile();
060: getDSMap(deployDir);
061: }
062: for (int n = 0; n < resourceList.size(); n++) {
063: // process each of the resource nodes in jboss-web.xml
064: Element dataSourceNode = (Element) resourceList.get(n);
065: String description = ""; //$NON-NLS-1$
066: String name = ""; //$NON-NLS-1$
067: String type = null;
068: // get the type of the datasource
069: Node node = dataSourceNode
070: .selectSingleNode(WEB_XML_RESOURCE_REF_TYPE);
071: if (node != null) {
072: type = node.getText();
073: }
074: // we can only handle JDBC datasources at the moment
075: if (JAVAX_SQL_DATASOURCE.equals(type)) {
076: node = dataSourceNode
077: .selectSingleNode(WEB_XML_RESOURCE_REF_NAME);
078: if (node != null) {
079: name = node.getText();
080: if (name.startsWith(JDBC_PREFIX)) {
081: name = name.substring(5);
082: } else {
083: // we cannot understand this node
084: continue;
085: }
086: }
087: node = dataSourceNode.selectSingleNode(JBOSS_JNDI_NAME);
088: if (node != null) {
089: // create a DataSourceInfo object
090: JBossDataSourceInfo info = new JBossDataSourceInfo(
091: name, description, type);
092: info.setJndiName(node.getText());
093: // make sure there is a matching -ds.xml file
094: JBossDataSourceInfo dsInfo = (JBossDataSourceInfo) dsFileMap
095: .get(name);
096: if (dsInfo == null) {
097: // this datasource reference is not defined in the
098: // deploy directory
099: // TODO log this properly
100: // TODO surface this message to the calller
101: info
102: .setStatus(Messages
103: .getErrorString(
104: "JBossDSAdmin.ERROR_0001_DS_FILE_MISSING", name)); //$NON-NLS-1$
105: jBossDSList.put(name, info);
106: } else {
107: // add the DataSourceInfo object into the list to return
108: jBossDSList.put(name, dsInfo);
109: }
110: }
111: }
112: }
113: return jBossDSList;
114: }
115:
116: public DataSourceInfo getContainerDataSourceInfo(String id,
117: DataSourceInfo info) {
118: Map dsList = listContainerDataSources();
119: JBossDataSourceInfo jbossInfo = (JBossDataSourceInfo) dsList
120: .get(id);
121: if (jbossInfo == null) {
122: return null;
123: }
124: return jbossInfo;
125: }
126:
127: private void getDSMap(File deployDir) {
128: // clear out the list of existing DS objects
129: dsFileMap.clear();
130: // now see if we can find a data source file (-ds.xml) in the deploy
131: // directory
132: JBossDataSourceFileFilter filter = new JBossDataSourceFileFilter();
133: File dsFiles[] = deployDir.listFiles(filter);
134: for (int f = 0; f < dsFiles.length; f++) {
135: Document dsDoc = this .getDSXmlDoc(dsFiles[f]);
136: String dsName = null;
137: Node dsNode = dsDoc
138: .selectSingleNode("datasources/local-tx-datasource/jndi-name"); //$NON-NLS-1$
139: if (dsNode != null) {
140: dsName = dsNode.getText();
141: JBossDataSourceInfo dsInfo = new JBossDataSourceInfo(
142: dsName, "", JAVAX_SQL_DATASOURCE); //$NON-NLS-1$
143: dsInfo.setDSFileName(dsFiles[f].getName());
144: dsNode = dsDoc
145: .selectSingleNode("datasources/local-tx-datasource/connection-url"); //$NON-NLS-1$
146: if (dsNode != null) {
147: dsInfo.setUrl(dsNode.getText());
148: }
149: dsNode = dsDoc
150: .selectSingleNode("datasources/local-tx-datasource/driver-class"); //$NON-NLS-1$
151: if (dsNode != null) {
152: dsInfo.setDriver(dsNode.getText());
153: }
154: dsNode = dsDoc
155: .selectSingleNode("datasources/local-tx-datasource/user-name"); //$NON-NLS-1$
156: if (dsNode != null) {
157: dsInfo.setUserId(dsNode.getText());
158: }
159: dsNode = dsDoc
160: .selectSingleNode("datasources/local-tx-datasource/password"); //$NON-NLS-1$
161: if (dsNode != null) {
162: dsInfo.setPassword(dsNode.getText());
163: }
164: dsFileMap.put(dsName, dsInfo);
165: }
166: }
167: }
168:
169: public int deleteContainerDataSource(String id) {
170: try {
171: File jbossWeb = getJBossWebXmlFile();
172: if (jbossWeb == null) {
173: return DS_FILE_OPERATION_FAILED;
174: }
175: File deployDir = jbossWeb.getParentFile().getParentFile()
176: .getParentFile();
177: if (dsFileMap.size() == 0) {
178: // read all of the -ds.xml files from the deploy directory
179: getDSMap(deployDir);
180: }
181: // first try to delete the datasource file from the deploy directory
182: JBossDataSourceInfo dsInfo = (JBossDataSourceInfo) dsFileMap
183: .get(id);
184: File dsFile = new File(deployDir, dsInfo.getDSFileName());
185: try {
186: if (!dsFile.delete()) {
187: logger
188: .error(Messages
189: .getString(
190: "JBossDSAdmin.ERROR_0002_COULD_NOT_DELETE", dsInfo.getDSFileName())); //$NON-NLS-1$
191: return DS_FILE_OPERATION_FAILED;
192: }
193: } catch (SecurityException se) {
194: logger
195: .error(Messages
196: .getString(
197: "JBossDSAdmin.ERROR_0002_COULD_NOT_DELETE", dsInfo.getDSFileName())); //$NON-NLS-1$
198: return DS_FILE_OPERATION_FAILED;
199: }
200: // now update the jboss-web.xml
201: Document doc = getWebXmlDoc(jbossWeb);
202: int status = removeJBossWebXmlDataSourceNode(doc, id);
203: if (status == DS_DELETED) {
204: // now try to save the file
205: try {
206: FileOutputStream outStream = new FileOutputStream(
207: jbossWeb);
208: OutputFormat format = OutputFormat
209: .createPrettyPrint();
210: XMLWriter writer = new XMLWriter(outStream, format);
211: writer.write(doc);
212: writer.flush();
213: writer.close();
214: } catch (IOException ioe) {
215: logger.error(null, ioe);
216: return DS_FILE_OPERATION_FAILED;
217: }
218: return DS_DELETED;
219: } else {
220: return status;
221: }
222: } catch (Exception e2) {
223: logger.error(null, e2);
224: }
225: return DS_OPERATION_FAILED;
226: }
227:
228: private int removeJBossWebXmlDataSourceNode(Document doc, String id) {
229: // create the target res-ref-name
230: String targetName = JDBC_PREFIX + id;
231: // get a list of the 'resource-ref' nodes
232: Element jbossWebNode = (Element) doc
233: .selectSingleNode("jboss-web"); //$NON-NLS-1$
234: List dataSourceNodes = jbossWebNode.selectNodes("resource-ref"); //$NON-NLS-1$
235: for (int n = 0; n < dataSourceNodes.size(); n++) {
236: // process each nose that was found
237: Element dataSourceNode = (Element) dataSourceNodes.get(n);
238: String type = null;
239: Node node = dataSourceNode.selectSingleNode("res-type"); //$NON-NLS-1$
240: if (node != null) {
241: type = node.getText();
242: }
243: if (JAVAX_SQL_DATASOURCE.equals(type)) {
244: // see if the res-ref-name of this datasource reference matches
245: // our target name
246: node = dataSourceNode
247: .selectSingleNode(WEB_XML_RESOURCE_REF_NAME);
248: if (node != null) {
249: String dataSourceName = node.getText();
250: if (dataSourceName != null
251: && dataSourceName.equals(targetName)) {
252: // we have found the datasource we are trying to delete
253: // remove the node from the DOM object
254: jbossWebNode.remove(dataSourceNode);
255: return DS_DELETED;
256: }
257: }
258: }
259: }
260: return DS_NOT_FOUND;
261: }
262:
263: public int renameContainerDataSource(String id, String newId) {
264: return DS_OPERATION_FAILED;
265: }
266:
267: public int saveContainerDataSource(DataSourceInfo info,
268: boolean isEdit) {
269: if (isEdit) {
270: // this is an edit of an existing datasource
271: return saveJBossDSEdit(info);
272: } else {
273: // we need to create new files and entries for this
274: return savenewJBossDS(info);
275: }
276: }
277:
278: private int saveJBossDSEdit(DataSourceInfo info) {
279: File jbossWeb = getJBossWebXmlFile();
280: if (jbossWeb == null) {
281: return DS_FILE_OPERATION_FAILED;
282: }
283: File deployDir = jbossWeb.getParentFile().getParentFile()
284: .getParentFile();
285: File dsFile = new File(deployDir, info.getName() + "-ds.xml"); //$NON-NLS-1$
286: if (dsFile == null || !dsFile.exists() || !dsFile.isFile()) {
287: return DS_FILE_OPERATION_FAILED;
288: }
289: Document dsDoc = XmlHelper.getDocFromFile(dsFile);
290: if (dsDoc == null) {
291: // the file is corrupt, try to create a good one
292: // TODO
293: }
294: try {
295: if (dsDoc != null) {
296: Node node = dsDoc
297: .selectSingleNode("//*[jndi-name='" + info.getName() + "']"); //$NON-NLS-1$ //$NON-NLS-2$
298: if (node == null) {
299: return DS_OPERATION_FAILED;
300: }
301: Element dsNode = (Element) node;
302: if (info.getUrl() == null) {
303: dsNode
304: .selectSingleNode("connection-url").setText(""); //$NON-NLS-1$ //$NON-NLS-2$
305: } else {
306: dsNode
307: .selectSingleNode("connection-url").setText(info.getUrl()); //$NON-NLS-1$
308: }
309: if (info.getDriver() == null) {
310: dsNode.selectSingleNode("driver-class").setText(""); //$NON-NLS-1$//$NON-NLS-2$
311: } else {
312: dsNode
313: .selectSingleNode("driver-class").setText(info.getDriver()); //$NON-NLS-1$
314: }
315: if (info.getUserId() == null) {
316: dsNode.selectSingleNode("user-name").setText(""); //$NON-NLS-1$ //$NON-NLS-2$
317: } else {
318: dsNode
319: .selectSingleNode("user-name").setText(info.getUserId()); //$NON-NLS-1$
320: }
321: if (info.getPassword() == null) {
322: dsNode.selectSingleNode("password").setText(""); //$NON-NLS-1$ //$NON-NLS-2$
323: } else {
324: dsNode
325: .selectSingleNode("password").setText(info.getPassword()); //$NON-NLS-1$
326: }
327:
328: try {
329: FileOutputStream outStream = new FileOutputStream(
330: jbossWeb);
331: OutputFormat format = OutputFormat
332: .createPrettyPrint();
333: XMLWriter writer = new XMLWriter(outStream, format);
334: writer.write(dsDoc);
335: writer.flush();
336: writer.close();
337: } catch (IOException ioe) {
338: logger.error(null, ioe);
339: return DS_OPERATION_FAILED;
340: }
341:
342: return DS_SAVED;
343: }
344: } catch (Throwable t) {
345: Logger
346: .error(
347: getClass().getName(),
348: Messages
349: .getString("JBossDSAdmin.ERROR_0006_ERRORSAVINGEDIT"), t); //$NON-NLS-1$
350: }
351: return DS_OPERATION_FAILED;
352: }
353:
354: private int savenewJBossDS(DataSourceInfo info) {
355: File jbossWeb = getJBossWebXmlFile();
356: if (jbossWeb == null) {
357: return DS_FILE_OPERATION_FAILED;
358: }
359: File deployDir = jbossWeb.getParentFile().getParentFile()
360: .getParentFile();
361: File dsFile = new File(deployDir, info.getName() + "-ds.xml"); //$NON-NLS-1$
362: // if (dsFile.exists()) {
363: // return DS_FILE_OPERATION_FAILED;
364: // }
365: try {
366:
367: Document jbossWebXMLDoc = XmlHelper
368: .getDocFromFile(jbossWeb);
369:
370: int status = saveJBossWebXmlDataSource(jbossWebXMLDoc, info);
371: if (status == DS_SAVED) {
372: // save doc
373: FileOutputStream outStream = new FileOutputStream(
374: jbossWeb);
375: OutputFormat format = OutputFormat.createPrettyPrint();
376: XMLWriter writer = new XMLWriter(outStream, format);
377: writer.write(jbossWebXMLDoc);
378: writer.flush();
379: writer.close();
380: }
381:
382: Document dsDoc = DocumentFactory.getInstance()
383: .createDocument();
384: Element datasources = dsDoc.addElement("datasources"); //$NON-NLS-1$
385: Element datasouce = datasources
386: .addElement("local-tx-datasource"); //$NON-NLS-1$
387: Element jndiName = datasouce.addElement("jndi-name"); //$NON-NLS-1$
388: jndiName.setText(info.getName());
389:
390: Element connectionUrl = datasouce
391: .addElement("connection-url"); //$NON-NLS-1$
392: connectionUrl.setText(info.getUrl());
393:
394: Element driver = datasouce.addElement("driver-class"); //$NON-NLS-1$
395: driver.setText(info.getDriver());
396:
397: Element user = datasouce.addElement("user-name"); //$NON-NLS-1$
398: user.setText(info.getUserId());
399:
400: Element password = datasouce.addElement("password"); //$NON-NLS-1$
401: password.setText(info.getPassword());
402:
403: FileOutputStream outStream = new FileOutputStream(dsFile);
404: OutputFormat format = OutputFormat.createPrettyPrint();
405: XMLWriter writer = new XMLWriter(outStream, format);
406: writer.write(dsDoc);
407: writer.flush();
408: writer.close();
409:
410: return DS_SAVED;
411: } catch (Throwable t) {
412: Logger
413: .error(
414: getClass().getName(),
415: Messages
416: .getString("JBossDSAdmin.ERROR_0007_ERRORCREATINGDS"), t); //$NON-NLS-1$
417: }
418: return DS_OPERATION_FAILED;
419: }
420:
421: private int saveJBossWebXmlDataSource(Document doc,
422: DataSourceInfo info) {
423: // create the target res-ref-name
424: // remove the existing datasource reference from the document
425: removeJBossWebXmlDataSourceNode(doc, info.getName());
426: // we always add the resource references at the end of the document
427: // otherwise it will not validate
428: // TODO check the web.xml schema/DTD to make sure there are no valid
429: // node types after the resources
430: Element webApp = (Element) doc.selectSingleNode("jboss-web"); //$NON-NLS-1$
431: Element resourceNode = webApp.addElement(WEB_XML_RESOURCE_REF);
432: resourceNode.addElement(WEB_XML_RESOURCE_REF_NAME).setText(
433: JDBC_PREFIX + info.getName());
434: resourceNode.addElement(WEB_XML_RESOURCE_REF_TYPE).setText(
435: JAVAX_SQL_DATASOURCE);
436: resourceNode.addElement(JBOSS_JNDI_NAME).setText(
437: JNDI_PREFIX + info.getName());
438:
439: return DS_SAVED;
440: }
441:
442: private File getJBossWebXmlFile() {
443: // find the webXml file in WEB-INF
444: File webXml = new File(PentahoSystem.getApplicationContext()
445: .getApplicationPath(
446: "WEB-INF" + File.separator + "jboss-web.xml")); //$NON-NLS-1$ //$NON-NLS-2$
447: if (!webXml.exists() || !webXml.isFile()) {
448: // TODO log this properly
449: File standaloneWebXml = new File(applicationPath
450: + File.separator + webAppName + File.separator
451: + "WEB-INF" + File.separator + "jboss-web.xml"); //$NON-NLS-1$ //$NON-NLS-2$
452: if (standaloneWebXml.exists()) {
453: return standaloneWebXml;
454: }
455: System.err
456: .println(Messages
457: .getString(
458: "JBossDSAdmin.ERROR_0003_JBOSS_WEB_XML_NOT_FOUND", webXml.getAbsolutePath())); //$NON-NLS-1$
459: return null;
460: }
461: return webXml;
462: }
463:
464: private Document getWebXmlDoc(File webXmlFile) {
465: // parse the web.xml file into a DOM object
466: Document doc = XmlHelper.getDocFromFile(webXmlFile);
467: if (doc == null) {
468: // TODO log this properly
469: System.err
470: .println(Messages
471: .getString(
472: "JBossDSAdmin.ERROR_0004_JBOSS_WEB_XML_INVALID", webXmlFile.getAbsolutePath())); //$NON-NLS-1$
473: }
474: return doc;
475: }
476:
477: private Document getDSXmlDoc(File dsXmlFile) {
478: // parse the web.xml file into a DOM object
479: Document doc = XmlHelper.getDocFromFile(dsXmlFile);
480: if (doc == null) {
481: // TODO log this properly
482: System.err
483: .println(Messages
484: .getString(
485: "JBossDSAdmin.ERROR_0005_DS_FILE_INVALID", dsXmlFile.getAbsolutePath())); //$NON-NLS-1$
486: }
487: return doc;
488: }
489: }
|