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}