View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jdbc;
25  
26  import java.sql.Connection;
27  import java.sql.DriverManager;
28  import java.sql.DriverPropertyInfo;
29  import java.sql.SQLException;
30  import java.util.Properties;
31  import java.util.Set;
32  import java.util.logging.Level;
33  import java.util.logging.Logger;
34  
35  import javax.jcr.Repository;
36  import javax.naming.Context;
37  import javax.naming.NamingException;
38  
39  import org.modeshape.jdbc.delegate.ConnectionInfo;
40  import org.modeshape.jdbc.delegate.RepositoryDelegateFactory;
41  import org.modeshape.jdbc.util.Collections;
42  
43  /**
44   * A JDBC driver implementation that is able to access a JCR repository to query its contents using JCR-SQL2. <h3>Connection URLs</h3>
45   * <p>
46   * The driver accepts several URL formats based on how the repository is configured:
47   * 
48   * <ol>
49   * <li>configured for <i>local</i> access using JNDI 
50   * <pre>
51   *     jdbc:jcr:jndi:{jndiName}
52   * </pre>
53   * 
54   * or
55   * 
56   * <pre>
57   *     jdbc:jcr:jndi:{jndiName}?{firstProperty}&amp;{secondProperty}&amp;...
58   * </pre>
59   * 
60   * where
61   * <ul>
62   * <li><strong>{jndiName}</strong> is the JNDI name where the {@link Repository} or  {@literal org.modeshape.jcr.api.Repositories} instance can be found;</li>
63   * <li><strong>{firstProperty}</strong> consists of the first property name followed by '=' followed by the property's value;</li>
64   * <li><strong>{secondProperty}</strong> consists of the second property name followed by '=' followed by the property's value;</li>
65   * </ul>
66   * Note that any use of URL encoding ('%' followed by a two-digit hexadecimal value) will be decoded before being used.
67   * </p>
68   * <p>
69   * Here's an example of a URL that defines a {@link Repository} instance located at "<code>jcr/local</code>" with a 
70   * repository name of "repository" and a user, password of "secret", and workspace name of "My Workspace":
71   * 
72   * <pre>
73   *     jdbc:jcr:jndi:jcr/local?repositoryName=repository&user=jsmith&amp;password=secret&amp;workspace=My%20Workspace
74   * </pre>
75   * 
76   * The "repository" property is required only if the object in JNDI is a {@literal org.modeshape.jcr.api.Repositories} object.
77   * <br /><br />
78   * <li>configured for <i>remote</i> access using REST interface.
79   * <pre>
80   *     jdbc:jcr:http://{hostname}:{port}?{firstProperty}&amp;{secondProperty}&amp;...
81   * </pre>
82   * where
83   * <ul>
84   * <li><strong>{hostname}</strong> is the host name where the {@link Repository} or {@literal org.modeshape.jcr.api.Repositories} instance can be found;</li>
85   * <li><strong>{port}</strong> is the port to access the {@link Repository} or {@literal org.modeshape.jcr.api.Repositories} on the specified <i>hostname</i>;</li>
86   * <li><strong>{firstProperty}</strong> consists of the first property name followed by '=' followed by the property's value;</li>
87   * <li><strong>{secondProperty}</strong> consists of the second property name followed by '=' followed by the property's value;</li>
88   * </ul>
89   * Note that any use of URL encoding ('%' followed by a two-digit hexadecimal value) will be decoded before being used.
90   * </p>
91   * Note that any use of URL encoding ('%' followed by a two-digit hexadecimal value) will be decoded before being used.
92   *
93   */
94  
95  public class JcrDriver implements java.sql.Driver {
96  	protected static Logger logger = Logger.getLogger("org.modeshape.jdbc"); //$NON-NLS-1$
97  
98      public static final String WORKSPACE_PROPERTY_NAME = "workspace";
99      public static final String REPOSITORY_PROPERTY_NAME = "repositoryName";
100     public static final String USERNAME_PROPERTY_NAME = "user";
101     public static final String PASSWORD_PROPERTY_NAME = "password";
102     public static final String TEIID_SUPPORT_PROPERTY_NAME = "teiidsupport";
103 
104     protected static final Set<String> ALL_PROPERTY_NAMES = Collections.unmodifiableSet(WORKSPACE_PROPERTY_NAME,
105                                                                                         REPOSITORY_PROPERTY_NAME,
106                                                                                         USERNAME_PROPERTY_NAME,
107                                                                                         PASSWORD_PROPERTY_NAME,
108                                                                                         TEIID_SUPPORT_PROPERTY_NAME);
109 
110     /* URL Prefix used for JNDI access */
111     public static final String JNDI_URL_PREFIX = "jdbc:jcr:jndi:";
112     /* URL Prefix used for remote access */ 
113     public static final String HTTP_URL_PREFIX = "jdbc:jcr:http://";
114 
115  
116     private static DriverMetadata driverMetadata;
117 
118     public JcrContextFactory contextFactory = null;
119     
120     private static JcrDriver INSTANCE = new JcrDriver();
121     
122     static {
123         try {
124             DriverManager.registerDriver(INSTANCE);
125         } catch(SQLException e) {
126             // Logging
127             String logMsg = JdbcI18n.driverErrorRegistering.text(e.getMessage()); //$NON-NLS-1$
128             logger.log(Level.SEVERE, logMsg);
129         }
130     }
131     
132     public static JcrDriver getInstance() {
133         return INSTANCE;
134     }
135 	
136 
137     /**
138      * No-arg constructor, required by the {@link DriverManager}.
139      */
140     public JcrDriver() {
141     }
142 
143     /**
144      * {@inheritDoc}
145      * 
146      * @see java.sql.Driver#acceptsURL(java.lang.String)
147      */
148     @Override
149     public boolean acceptsURL( String url ) {
150 	return RepositoryDelegateFactory.acceptUrl(url);
151     }
152 
153     /**
154      * {@inheritDoc}
155      * 
156      * @see java.sql.Driver#getPropertyInfo(java.lang.String, java.util.Properties)
157      */
158     @Override
159     public DriverPropertyInfo[] getPropertyInfo( String url,
160                                                  Properties info ) throws SQLException{
161         // Get the connection information ...
162 	return RepositoryDelegateFactory.createRepositoryDelegate(url, info, this.contextFactory).getConnectionInfo().getPropertyInfos();
163     }
164 
165     /**
166      * Get the information describing the connection. This method can be overridden to return a subclass of {@link ConnectionInfo}
167      * that implements {@link ConnectionInfo#getCredentials()} for a specific JCR implementation.
168      * 
169      * @param url the JDBC URL
170      * @param info the JDBC connection properties
171      * @return the connection information, or null if the URL is null or not of the proper format
172      * @throws SQLException 
173      */
174     protected ConnectionInfo createConnectionInfo( String url,
175                                                    Properties info ) throws SQLException {
176         return RepositoryDelegateFactory.createRepositoryDelegate(url, info, this.contextFactory).getConnectionInfo();
177     }
178 
179     /**
180      * {@inheritDoc}
181      * <p>
182      * Note that if the supplied properties and URL contain properties with the same name, the value from the supplied Properties
183      * object will take precedence.
184      * </p>
185      * 
186      * @see java.sql.Driver#connect(java.lang.String, java.util.Properties)
187      */
188     @Override
189     public Connection connect( String url,
190                                Properties info ) throws SQLException {
191 	
192 	return RepositoryDelegateFactory.createRepositoryDelegate(url, info, this.contextFactory).createConnection();
193     }
194 
195     /**
196      * {@inheritDoc}
197      * 
198      * @see java.sql.Driver#getMajorVersion()
199      */
200     @Override
201     public int getMajorVersion() {
202         return getDriverMetadata().getMajorVersion();
203     }
204 
205     /**
206      * {@inheritDoc}
207      * 
208      * @see java.sql.Driver#getMinorVersion()
209      */
210     @Override
211     public int getMinorVersion() {
212         return getDriverMetadata().getMinorVersion();
213     }
214 
215     public String getVendorName() {
216         return getDriverMetadata().getVendorName();
217     }
218 
219     public String getVendorUrl() {
220         return getDriverMetadata().getVendorUrl();
221     }
222 
223     public String getVersion() {
224         return getDriverMetadata().getVersion();
225     }
226 
227     /**
228      * {@inheritDoc}
229      * 
230      * @see java.sql.Driver#jdbcCompliant()
231      */
232     @Override
233     public boolean jdbcCompliant() {
234         return false;
235     }
236     
237 
238     static DriverMetadata getDriverMetadata() {
239         if (driverMetadata == null) {
240             driverMetadata = new DriverMetadata();
241         }
242         return driverMetadata;
243     }
244 
245     static class DriverMetadata {
246         private final int major;
247         private final int minor;
248 
249         protected DriverMetadata() {
250             String[] coords = getVersion().split("[.-]");
251             this.major = Integer.parseInt(coords[0]);
252             this.minor = Integer.parseInt(coords[1]);
253         }
254 
255         public String getVendorName() {
256             return JdbcI18n.driverVendor.text();
257         }
258 
259         public String getVendorUrl() {
260             return JdbcI18n.driverVendorUrl.text();
261         }
262 
263         public String getVersion() {
264             return JdbcI18n.driverVersion.text();
265         }
266 
267         public int getMajorVersion() {
268             return major;
269         }
270 
271         public int getMinorVersion() {
272             return minor;
273         }
274 
275         public String getName() {
276             return JdbcI18n.driverName.text();
277         }
278     }
279 
280     public void setContextFactory( JcrContextFactory factory ) {
281         assert factory != null;
282         this.contextFactory = factory;
283     }
284     
285     public interface JcrContextFactory {
286         Context createContext( Properties properties ) throws NamingException;
287     }
288     
289  
290 
291 }