001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.resource.adapter.jdbc.xa;
023:
024: import org.jboss.resource.JBossResourceException;
025: import org.jboss.util.JBossStringBuilder;
026:
027: import javax.resource.ResourceException;
028: import javax.resource.spi.ManagedConnection;
029: import javax.resource.spi.ConnectionRequestInfo;
030: import javax.sql.XADataSource;
031: import javax.security.auth.Subject;
032: import java.util.List;
033: import java.util.ArrayList;
034: import java.util.Properties;
035: import java.util.Iterator;
036: import java.lang.reflect.Method;
037: import java.lang.reflect.InvocationTargetException;
038: import java.beans.PropertyEditorManager;
039: import java.beans.PropertyEditor;
040:
041: /**
042: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
043: * @version <tt>$Revision: 59774 $</tt>
044: */
045: public class HAXAManagedConnectionFactory extends
046: XAManagedConnectionFactory {
047: private static final long serialVersionUID = 1898242235188801452L;
048:
049: private String urlProperty;
050: private String urlDelimiter;
051: private XADataSelector xadsSelector;
052:
053: public String getURLProperty() {
054: return urlProperty;
055: }
056:
057: public void setURLProperty(String urlProperty)
058: throws ResourceException {
059: this .urlProperty = urlProperty;
060: initSelector();
061: }
062:
063: public String getURLDelimiter() {
064: return urlDelimiter;
065: }
066:
067: public void setURLDelimiter(String urlDelimetir)
068: throws ResourceException {
069: this .urlDelimiter = urlDelimetir;
070: initSelector();
071: }
072:
073: public void setXADataSourceProperties(String xaDataSourceProperties)
074: throws ResourceException {
075: super .setXADataSourceProperties(xaDataSourceProperties);
076: initSelector();
077: }
078:
079: // Protected
080:
081: public ManagedConnection createManagedConnection(Subject subject,
082: ConnectionRequestInfo cri)
083: throws javax.resource.ResourceException {
084: if (xadsSelector == null) {
085: JBossStringBuilder buffer = new JBossStringBuilder();
086: buffer
087: .append("Missing configuration for HA XA datasource.");
088: if (urlProperty == null)
089: buffer.append(" No url property.");
090: else if (xaProps.containsKey(urlProperty) == false)
091: buffer.append(" ").append(urlProperty).append(
092: " not found in datasource properties.");
093: if (urlDelimiter == null)
094: buffer.append(" No url-delimiter.");
095: throw new JBossResourceException(buffer.toString());
096: }
097:
098: // try to get a connection as many times as many urls we have in the list
099: for (int i = 0; i < xadsSelector.getXADataSourceList().size(); ++i) {
100: XAData xaData = xadsSelector.getXAData();
101:
102: if (log.isTraceEnabled()) {
103: log.trace("Trying to create an XA connection to "
104: + xaData.url);
105: }
106:
107: try {
108: return super .createManagedConnection(subject, cri);
109: } catch (ResourceException e) {
110: log.warn("Failed to create an XA connection to "
111: + xaData.url + ": " + e.getMessage());
112: xadsSelector.failedXAData(xaData);
113: }
114: }
115:
116: // we have supposedly tried all the urls
117: throw new JBossResourceException(
118: "Could not create connection using any of the URLs: "
119: + xadsSelector.getXADataSourceList());
120: }
121:
122: protected synchronized XADataSource getXADataSource()
123: throws ResourceException {
124: return xadsSelector.getXAData().xads;
125: }
126:
127: // Private
128:
129: private XADataSource createXaDataSource(Properties xaProps)
130: throws JBossResourceException {
131: if (getXADataSourceClass() == null) {
132: throw new JBossResourceException(
133: "No XADataSourceClass supplied!");
134: }
135:
136: XADataSource xads;
137: try {
138: Class clazz = Thread.currentThread()
139: .getContextClassLoader().loadClass(
140: getXADataSourceClass());
141: xads = (XADataSource) clazz.newInstance();
142: Class[] NOCLASSES = new Class[] {};
143: for (Iterator i = xaProps.keySet().iterator(); i.hasNext();) {
144: String name = (String) i.next();
145: String value = xaProps.getProperty(name);
146: //This is a bad solution. On the other hand the only known example
147: // of a setter with no getter is for Oracle with password.
148: //Anyway, each xadatasource implementation should get its
149: //own subclass of this that explicitly sets the
150: //properties individually.
151: Class type = null;
152: try {
153: Method getter = clazz.getMethod("get" + name,
154: NOCLASSES);
155: type = getter.getReturnType();
156: } catch (NoSuchMethodException e) {
157: type = String.class;
158: } // end of try-catch
159:
160: Method setter = clazz.getMethod("set" + name,
161: new Class[] { type });
162: PropertyEditor editor = PropertyEditorManager
163: .findEditor(type);
164: if (editor == null) {
165: throw new JBossResourceException(
166: "No property editor found for type: "
167: + type);
168: } // end of if ()
169: editor.setAsText(value);
170: setter.invoke(xads, new Object[] { editor.getValue() });
171:
172: } // end of for ()
173: } catch (ClassNotFoundException cnfe) {
174: throw new JBossResourceException(
175: "Class not found for XADataSource "
176: + getXADataSourceClass(), cnfe);
177: } // end of try-catch
178: catch (InstantiationException ie) {
179: throw new JBossResourceException(
180: "Could not create an XADataSource: ", ie);
181: } // end of catch
182: catch (IllegalAccessException iae) {
183: throw new JBossResourceException(
184: "Could not set a property: ", iae);
185: } // end of catch
186:
187: catch (IllegalArgumentException iae) {
188: throw new JBossResourceException(
189: "Could not set a property: ", iae);
190: } // end of catch
191:
192: catch (InvocationTargetException ite) {
193: throw new JBossResourceException(
194: "Could not invoke setter on XADataSource: ", ite);
195: } // end of catch
196: catch (NoSuchMethodException nsme) {
197: throw new JBossResourceException(
198: "Could not find accessor on XADataSource: ", nsme);
199: } // end of catch
200:
201: return xads;
202: }
203:
204: private void initSelector() throws JBossResourceException {
205: if (urlProperty != null && urlProperty.length() > 0) {
206: String urlsStr = xaProps.getProperty(urlProperty);
207: if (urlsStr != null && urlsStr.trim().length() > 0
208: && urlDelimiter != null
209: && urlDelimiter.trim().length() > 0) {
210: List xaDataList = new ArrayList();
211:
212: // copy xaProps
213: // ctor doesn't work because iteration won't include defaults
214: // Properties xaPropsCopy = new Properties(xaProps);
215: Properties xaPropsCopy = new Properties();
216: for (Iterator i = xaProps.keySet().iterator(); i
217: .hasNext();) {
218: Object key = i.next();
219: xaPropsCopy.put(key, xaProps.get(key));
220: }
221:
222: int urlStart = 0;
223: int urlEnd = urlsStr.indexOf(urlDelimiter);
224: while (urlEnd > 0) {
225: String url = urlsStr.substring(urlStart, urlEnd);
226: xaPropsCopy.setProperty(urlProperty, url);
227: XADataSource xads = createXaDataSource(xaPropsCopy);
228: xaDataList.add(new XAData(xads, url));
229: urlStart = ++urlEnd;
230: urlEnd = urlsStr.indexOf(urlDelimiter, urlEnd);
231: log.debug("added XA HA connection url: " + url);
232: }
233:
234: if (urlStart != urlsStr.length()) {
235: String url = urlsStr.substring(urlStart, urlsStr
236: .length());
237: xaPropsCopy.setProperty(urlProperty, url);
238: XADataSource xads = createXaDataSource(xaPropsCopy);
239: xaDataList.add(new XAData(xads, url));
240: log.debug("added XA HA connection url: " + url);
241: }
242:
243: xadsSelector = new XADataSelector(xaDataList);
244: }
245: }
246: }
247:
248: // Inner
249:
250: public static class XADataSelector {
251: private final List xaDataList;
252: private int xaDataIndex;
253: private XAData xaData;
254:
255: public XADataSelector(List xaDataList) {
256: if (xaDataList == null || xaDataList.size() == 0) {
257: throw new IllegalStateException(
258: "Expected non-empty list of XADataSource/URL pairs but got: "
259: + xaDataList);
260: }
261:
262: this .xaDataList = xaDataList;
263: }
264:
265: public synchronized XAData getXAData() {
266: if (xaData == null) {
267: if (xaDataIndex == xaDataList.size()) {
268: xaDataIndex = 0;
269: }
270: xaData = (XAData) xaDataList.get(xaDataIndex++);
271: }
272: return xaData;
273: }
274:
275: public synchronized void failedXAData(XAData xads) {
276: if (xads.equals(this .xaData)) {
277: this .xaData = null;
278: }
279: }
280:
281: public List getXADataSourceList() {
282: return xaDataList;
283: }
284: }
285:
286: private static class XAData {
287: public final XADataSource xads;
288: public final String url;
289:
290: public XAData(XADataSource xads, String url) {
291: this .xads = xads;
292: this .url = url;
293: }
294:
295: public boolean equals(Object o) {
296: if (this == o) {
297: return true;
298: }
299: if (!(o instanceof XAData)) {
300: return false;
301: }
302:
303: final XAData xaData = (XAData) o;
304:
305: if (!url.equals(xaData.url)) {
306: return false;
307: }
308:
309: return true;
310: }
311:
312: public int hashCode() {
313: return url.hashCode();
314: }
315:
316: public String toString() {
317: return "[XA URL=" + url + "]";
318: }
319: }
320: }
|