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.sql;
022
023import java.sql.Connection;
024import java.sql.DriverManager;
025import java.sql.DriverPropertyInfo;
026import java.sql.SQLException;
027import java.sql.SQLFeatureNotSupportedException;
028import java.util.Properties;
029import java.util.regex.Matcher;
030import java.util.regex.Pattern;
031
032import net.sf.hajdbc.Database;
033import net.sf.hajdbc.DatabaseCluster;
034import net.sf.hajdbc.DatabaseClusterFactory;
035import net.sf.hajdbc.Messages;
036import net.sf.hajdbc.util.SQLExceptionFactory;
037import net.sf.hajdbc.util.Strings;
038import net.sf.hajdbc.util.reflect.ProxyFactory;
039
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043/**
044 * @author  Paul Ferraro
045 * @version $Revision: 2015 $
046 */
047public final class Driver implements java.sql.Driver
048{
049        private static final Pattern URL_PATTERN = Pattern.compile("jdbc:ha-jdbc:(.+)"); //$NON-NLS-1$
050        private static final String CONFIG = "config"; //$NON-NLS-1$
051        
052        private static Logger logger = LoggerFactory.getLogger(Driver.class);
053        
054        static
055        {
056                try
057                {
058                        DriverManager.registerDriver(new Driver());
059                }
060                catch (SQLException e)
061                {
062                        logger.error(Messages.getMessage(Messages.DRIVER_REGISTER_FAILED, Driver.class.getName()), e);
063                }
064        }
065        
066        /**
067         * @see java.sql.Driver#acceptsURL(java.lang.String)
068         */
069        @Override
070        public boolean acceptsURL(String url)
071        {
072                return (this.parse(url) != null);
073        }
074
075        /**
076         * @see java.sql.Driver#connect(java.lang.String, java.util.Properties)
077         */
078        @Override
079        public Connection connect(String url, final Properties properties) throws SQLException
080        {
081                String id = this.parse(url);
082                
083                if (id == null) return null;
084                
085                DatabaseCluster<java.sql.Driver> cluster = this.getDatabaseCluster(id, properties);
086                
087                DriverInvocationHandler handler = new DriverInvocationHandler(cluster);
088                
089                java.sql.Driver driver = ProxyFactory.createProxy(java.sql.Driver.class, handler);
090                
091                Invoker<java.sql.Driver, java.sql.Driver, Connection> invoker = new Invoker<java.sql.Driver, java.sql.Driver, Connection>()
092                {
093                        public Connection invoke(Database<java.sql.Driver> database, java.sql.Driver driver) throws SQLException
094                        {
095                                String url = ((DriverDatabase) database).getUrl();
096                                
097                                return driver.connect(url, properties);
098                        }
099                };
100                
101                TransactionContext<java.sql.Driver> context = new LocalTransactionContext<java.sql.Driver>(cluster);
102                
103                try
104                {
105                        return new ConnectionInvocationStrategy<java.sql.Driver, java.sql.Driver>(cluster, driver, context).invoke(handler, invoker);
106                }
107                catch (Exception e)
108                {
109                        throw SQLExceptionFactory.createSQLException(e);
110                }
111        }
112
113        /**
114         * @see java.sql.Driver#getMajorVersion()
115         */
116        @Override
117        public int getMajorVersion()
118        {
119                return Integer.parseInt(version()[0]);
120        }
121        
122        /**
123         * @see java.sql.Driver#getMinorVersion()
124         */
125        @Override
126        public int getMinorVersion()
127        {
128                return Integer.parseInt(version()[1]);
129        }
130
131        private String[] version()
132        {
133                return DatabaseClusterFactory.getVersion().split(Strings.DASH)[0].split(Pattern.quote(Strings.DOT));
134        }
135        
136        /**
137         * @see java.sql.Driver#getPropertyInfo(java.lang.String, java.util.Properties)
138         */
139        @Override
140        public DriverPropertyInfo[] getPropertyInfo(String url, final Properties properties) throws SQLException
141        {
142                String id = this.parse(url);
143                
144                if (id == null) return null;
145                
146                DatabaseCluster<java.sql.Driver> cluster = this.getDatabaseCluster(id, properties);
147                
148                DriverInvocationHandler handler = new DriverInvocationHandler(cluster);
149                
150                Invoker<java.sql.Driver, java.sql.Driver, DriverPropertyInfo[]> invoker = new Invoker<java.sql.Driver, java.sql.Driver, DriverPropertyInfo[]>()
151                {
152                        public DriverPropertyInfo[] invoke(Database<java.sql.Driver> database, java.sql.Driver driver) throws SQLException
153                        {
154                                String url = ((DriverDatabase) database).getUrl();
155                                
156                                return driver.getPropertyInfo(url, properties);
157                        }                       
158                };
159                
160                try
161                {
162                        return new DriverReadInvocationStrategy<java.sql.Driver, java.sql.Driver, DriverPropertyInfo[]>().invoke(handler, invoker);
163                }
164                catch (Exception e)
165                {
166                        throw SQLExceptionFactory.createSQLException(e);
167                }
168        }
169
170        /**
171         * @see java.sql.Driver#jdbcCompliant()
172         */
173        @Override
174        public boolean jdbcCompliant()
175        {
176                return true;
177        }
178
179        private DatabaseCluster<java.sql.Driver> getDatabaseCluster(String id, Properties properties) throws SQLException
180        {
181                DatabaseCluster<java.sql.Driver> cluster = DatabaseClusterFactory.getDatabaseCluster(id, DriverDatabaseCluster.class, DriverDatabaseClusterMBean.class, properties.getProperty(CONFIG));
182                
183                if (cluster == null)
184                {
185                        throw new SQLException(Messages.getMessage(Messages.INVALID_DATABASE_CLUSTER, id));
186                }
187                
188                return cluster;
189        }
190        
191        private String parse(String url)
192        {
193                Matcher matcher = URL_PATTERN.matcher(url);
194                
195                if (!matcher.matches())
196                {
197                        return null;
198                }
199                
200                return matcher.group(1);
201        }
202
203        public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
204            throw new SQLFeatureNotSupportedException();
205        }
206
207}