001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (c) 2004-2007 Paul Ferraro 004 * 005 * This library is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Lesser General Public License as published by the 007 * Free Software Foundation; either version 2.1 of the License, or (at your 008 * option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT 011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this library; if not, write to the Free Software Foundation, 017 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 * 019 * Contact: ferraro@users.sourceforge.net 020 */ 021package net.sf.hajdbc; 022 023import java.lang.management.ManagementFactory; 024import java.lang.reflect.InvocationTargetException; 025import java.net.MalformedURLException; 026import java.net.URL; 027import java.sql.SQLException; 028import java.text.MessageFormat; 029import java.util.Hashtable; 030import java.util.ResourceBundle; 031 032import javax.management.JMException; 033import javax.management.MBeanServer; 034import javax.management.MBeanServerInvocationHandler; 035import javax.management.MalformedObjectNameException; 036import javax.management.ObjectName; 037 038import net.sf.hajdbc.util.SQLExceptionFactory; 039 040/** 041 * @author Paul Ferraro 042 */ 043public class DatabaseClusterFactory 044{ 045 private static final String CONFIG_FORMAT_PROPERTY = "ha-jdbc.{0}.configuration"; //$NON-NLS-1$ 046 private static final String CONFIG_PROPERTY = "ha-jdbc.configuration"; //$NON-NLS-1$ 047 private static final String DEFAULT_RESOURCE = "ha-jdbc-{0}.xml"; //$NON-NLS-1$ 048 private static final String MBEAN_CLUSTER_KEY = "cluster"; //$NON-NLS-1$ 049 private static final String MBEAN_DATABASE_KEY = "database"; //$NON-NLS-1$ 050 private static final String VERSION = "version"; //$NON-NLS-1$ 051 052 private static ResourceBundle resource = ResourceBundle.getBundle(DatabaseClusterFactory.class.getName()); 053 054 /** 055 * Convenience method for constructing a standardized mbean ObjectName for this cluster. 056 * @param clusterId a cluster identifier 057 * @return an ObjectName for this cluster 058 * @throws MalformedObjectNameException if the ObjectName could not be constructed 059 */ 060 public static ObjectName getObjectName(String clusterId) throws MalformedObjectNameException 061 { 062 return ObjectName.getInstance(getDomain(), createProperties(clusterId)); 063 } 064 065 /** 066 * Convenience method for constructing a standardized mbean ObjectName for this database. 067 * @param clusterId a cluster identifier 068 * @param databaseId a database identifier 069 * @return an ObjectName for this cluster 070 * @throws MalformedObjectNameException if the ObjectName could not be constructed 071 */ 072 public static ObjectName getObjectName(String clusterId, String databaseId) throws MalformedObjectNameException 073 { 074 Hashtable<String, String> properties = createProperties(clusterId); 075 076 properties.put(MBEAN_DATABASE_KEY, databaseId); 077 078 return ObjectName.getInstance(getDomain(), properties); 079 } 080 081 private static Hashtable<String, String> createProperties(String clusterId) 082 { 083 Hashtable<String, String> properties = new Hashtable<String, String>(); 084 085 properties.put(MBEAN_CLUSTER_KEY, clusterId); 086 087 return properties; 088 } 089 090 private static String getDomain() 091 { 092 return DatabaseClusterFactory.class.getPackage().getName(); 093 } 094 095 /** 096 * Returns the current HA-JDBC version. 097 * @return a version label 098 */ 099 public static String getVersion() 100 { 101 return resource.getString(VERSION); 102 } 103 104 public static synchronized <C extends DatabaseCluster<?>> C getDatabaseCluster(String id, Class<? extends C> targetClass, Class<C> mbeanInterface, String resource) throws SQLException 105 { 106 try 107 { 108 ObjectName name = getObjectName(id); 109 110 MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 111 112 if (!server.isRegistered(name)) 113 { 114 URL url = findResource((resource == null) ? identifyResource(id) : resource); 115 116 C cluster = targetClass.getConstructor(String.class, URL.class).newInstance(id, url); 117 118 server.registerMBean(cluster, name); 119 } 120 121 return MBeanServerInvocationHandler.newProxyInstance(server, name, mbeanInterface, false); 122 } 123 catch (JMException e) 124 { 125 throw SQLExceptionFactory.createSQLException(e); 126 } 127 catch (InstantiationException e) 128 { 129 throw SQLExceptionFactory.createSQLException(e); 130 } 131 catch (IllegalAccessException e) 132 { 133 throw SQLExceptionFactory.createSQLException(e); 134 } 135 catch (NoSuchMethodException e) 136 { 137 throw SQLExceptionFactory.createSQLException(e); 138 } 139 catch (InvocationTargetException e) 140 { 141 throw SQLExceptionFactory.createSQLException(e); 142 } 143 } 144 145 private static String identifyResource(String id) 146 { 147 String resource = System.getProperty(MessageFormat.format(CONFIG_FORMAT_PROPERTY, id)); 148 149 return (resource != null) ? resource : MessageFormat.format(System.getProperty(CONFIG_PROPERTY, DEFAULT_RESOURCE), id); 150 } 151 152 /** 153 * Algorithm for searching class loaders for HA-JDBC url. 154 * @param resource a resource name 155 * @return a URL for the HA-JDBC configuration resource 156 */ 157 private static URL findResource(String resource) throws SQLException 158 { 159 try 160 { 161 return new URL(resource); 162 } 163 catch (MalformedURLException e) 164 { 165 URL url = Thread.currentThread().getContextClassLoader().getResource(resource); 166 167 if (url == null) 168 { 169 url = DatabaseClusterFactory.class.getClassLoader().getResource(resource); 170 } 171 172 if (url == null) 173 { 174 url = ClassLoader.getSystemResource(resource); 175 } 176 177 if (url == null) 178 { 179 throw new SQLException(Messages.getMessage(Messages.CONFIG_NOT_FOUND, resource)); 180 } 181 182 return url; 183 } 184 } 185}