001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.sql; 019 020import java.beans.IntrospectionException; 021import java.beans.Introspector; 022import java.beans.PropertyDescriptor; 023import java.beans.PropertyEditor; 024import java.beans.PropertyEditorManager; 025import java.lang.reflect.InvocationTargetException; 026import java.util.Map; 027import java.util.Properties; 028import java.util.concurrent.atomic.AtomicReference; 029 030import javax.naming.Context; 031import javax.naming.InitialContext; 032import javax.naming.NamingException; 033 034import net.sf.hajdbc.Messages; 035import net.sf.hajdbc.management.Description; 036import net.sf.hajdbc.management.ManagedAttribute; 037 038/** 039 * A database described by a data source. 040 * @author Paul Ferraro 041 * @param <Z> <code>javax.sql</code> data source interface 042 */ 043public abstract class CommonDataSourceDatabase<Z extends javax.sql.CommonDataSource> extends AbstractDatabase<Z> 044{ 045 private final Class<Z> targetClass; 046 private final AtomicReference<Z> dataSourceRef = new AtomicReference<Z>(); 047 048 protected CommonDataSourceDatabase(Class<Z> targetClass) 049 { 050 this.targetClass = targetClass; 051 } 052 053 /** 054 * {@inheritDoc} 055 * @see net.sf.hajdbc.sql.AbstractDatabase#getLocation() 056 */ 057 @ManagedAttribute 058 @Description("The JNDI name to which this DataSource is bound, or the class name of the DataSource implementation") 059 @Override 060 public void setLocation(String name) 061 { 062 super.setLocation(name); 063 064 this.dataSourceRef.set(null); 065 } 066 067 /** 068 * @see net.sf.hajdbc.Database#getConnectionSource() 069 */ 070 @Override 071 public Z getConnectionSource() 072 { 073 Z dataSource = this.dataSourceRef.get(); 074 075 if (dataSource == null) 076 { 077 try 078 { 079 Class<?> dataSourceClass = this.getClass().getClassLoader().loadClass(this.getLocation()); 080 081 dataSource = this.createDataSource(dataSourceClass.asSubclass(this.targetClass)); 082 } 083 catch (ClassNotFoundException e) 084 { 085 dataSource = this.createDataSource(); 086 } 087 088 if (!this.dataSourceRef.compareAndSet(null, dataSource)) 089 { 090 dataSource = this.dataSourceRef.get(); 091 } 092 } 093 094 return dataSource; 095 } 096 097 private Z createDataSource() 098 { 099 Properties properties = new Properties(); 100 101 for (Map.Entry<String, String> entry: this.getProperties().entrySet()) 102 { 103 properties.setProperty(entry.getKey(), entry.getValue()); 104 } 105 106 String name = this.getLocation(); 107 108 try 109 { 110 Context context = new InitialContext(properties); 111 112 return this.targetClass.cast(context.lookup(name)); 113 } 114 catch (NamingException e) 115 { 116 throw new IllegalArgumentException(Messages.JNDI_LOOKUP_FAILED.getMessage(name), e); 117 } 118 } 119 120 private Z createDataSource(Class<? extends Z> dataSourceClass) 121 { 122 Map<String, String> properties = this.getProperties(); 123 124 try 125 { 126 Z dataSource = dataSourceClass.newInstance(); 127 128 for (PropertyDescriptor descriptor: Introspector.getBeanInfo(dataSourceClass).getPropertyDescriptors()) 129 { 130 String value = properties.get(descriptor.getName()); 131 132 if (value != null) 133 { 134 PropertyEditor editor = PropertyEditorManager.findEditor(descriptor.getPropertyType()); 135 136 editor.setAsText(value); 137 138 descriptor.getWriteMethod().invoke(dataSource, editor.getValue()); 139 } 140 } 141 142 return dataSource; 143 } 144 catch (InstantiationException e) 145 { 146 throw new IllegalArgumentException(e.getMessage(), e); 147 } 148 catch (IllegalAccessException e) 149 { 150 throw new IllegalArgumentException(e.getMessage(), e); 151 } 152 catch (IntrospectionException e) 153 { 154 throw new IllegalArgumentException(e.getMessage(), e); 155 } 156 catch (InvocationTargetException e) 157 { 158 throw new IllegalArgumentException(e.getTargetException().getMessage(), e); 159 } 160 } 161}