001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.jdbc.delegate; 017 018import java.sql.Connection; 019import java.sql.DriverPropertyInfo; 020import java.sql.SQLException; 021import java.util.ArrayList; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Properties; 025import java.util.Set; 026import javax.jcr.LoginException; 027import javax.jcr.NoSuchWorkspaceException; 028import javax.jcr.Repository; 029import javax.jcr.RepositoryException; 030import javax.jcr.Workspace; 031import javax.jcr.nodetype.NodeType; 032import javax.jcr.nodetype.NodeTypeIterator; 033import javax.jcr.query.Query; 034import javax.jcr.query.QueryResult; 035import javax.naming.Context; 036import javax.naming.InitialContext; 037import javax.naming.NamingException; 038import org.modeshape.jcr.api.Repositories; 039import org.modeshape.jdbc.JdbcLocalI18n; 040import org.modeshape.jdbc.LocalJcrDriver; 041import org.modeshape.jdbc.LocalJcrDriver.JcrContextFactory; 042 043/** 044 * The LocalRepositoryDelegate provides a local Repository implementation to access the Jcr layer via JNDI Lookup. 045 */ 046public class LocalRepositoryDelegate extends AbstractRepositoryDelegate { 047 public static final RepositoryDelegateFactory FACTORY = new RepositoryDelegateFactory() {}; 048 049 private static final String JNDI_EXAMPLE_URL = LocalJcrDriver.JNDI_URL_PREFIX + "{jndiName}"; 050 051 protected static final Set<LocalSession> TRANSACTION_IDS = java.util.Collections.synchronizedSet(new HashSet<LocalSession>()); 052 053 private JcrContextFactory jcrContext = null; 054 private Repository repository = null; 055 056 public LocalRepositoryDelegate( String url, 057 Properties info, 058 JcrContextFactory contextFactory ) { 059 super(url, info); 060 061 if (contextFactory == null) { 062 jcrContext = new JcrContextFactory() { 063 @Override 064 public Context createContext( Properties properties ) throws NamingException { 065 return ((properties == null || properties.isEmpty()) ? new InitialContext() : new InitialContext(properties)); 066 } 067 }; 068 } else { 069 this.jcrContext = contextFactory; 070 } 071 } 072 073 @Override 074 protected ConnectionInfo createConnectionInfo( String url, 075 Properties info ) { 076 return new JNDIConnectionInfo(url, info); 077 } 078 079 protected JcrContextFactory getJcrContext() { 080 return jcrContext; 081 } 082 083 private LocalSession getLocalSession() throws LoginException, NoSuchWorkspaceException, RepositoryException { 084 return LocalSession.getLocalSessionInstance().getLocalSession(repository, getConnectionInfo()); 085 } 086 087 private LocalSession getCurrentLocalSession() { 088 return LocalSession.getLocalSessionInstance().getLocalSession(); 089 } 090 091 @Override 092 public String getDescriptor( String descriptorKey ) { 093 return repository.getDescriptor(descriptorKey); 094 } 095 096 @Override 097 public NodeType nodeType( String name ) throws RepositoryException { 098 LocalSession localSession = getLocalSession(); 099 return localSession.getSession().getWorkspace().getNodeTypeManager().getNodeType(name); 100 } 101 102 @Override 103 public List<NodeType> nodeTypes() throws RepositoryException { 104 105 LocalSession localSession = getLocalSession(); 106 List<NodeType> types = new ArrayList<NodeType>(); 107 NodeTypeIterator its = localSession.getSession().getWorkspace().getNodeTypeManager().getAllNodeTypes(); 108 while (its.hasNext()) { 109 types.add((NodeType)its.next()); 110 } 111 return types; 112 113 } 114 115 /** 116 * This execute method is used for redirection so that the JNDI implementation can control calling execute. 117 * 118 * @see java.sql.Statement#execute(java.lang.String) 119 */ 120 @Override 121 public QueryResult execute( String query, 122 String language ) throws RepositoryException { 123 logger.trace("Executing query: {0}", query); 124 125 // Create the query ... 126 127 final Query jcrQuery = getLocalSession().getSession().getWorkspace().getQueryManager().createQuery(query, language); 128 return jcrQuery.execute(); 129 } 130 131 @Override 132 public String explain( String query, 133 String language ) throws RepositoryException { 134 logger.trace("Explaining query: {0}", query); 135 136 // Create the query ... 137 138 final org.modeshape.jcr.api.query.Query jcrQuery = (org.modeshape.jcr.api.query.Query)getLocalSession().getSession().getWorkspace().getQueryManager().createQuery(query, 139 language); 140 return jcrQuery.explain().getPlan(); 141 } 142 143 @Override 144 protected void initRepository() throws SQLException { 145 if (repository != null) { 146 return; 147 } 148 logger.debug("Creating repository for LocalRepositoryDelegate"); 149 150 Set<String> repositoryNames = null; 151 ConnectionInfo connInfo = this.getConnectionInfo(); 152 assert connInfo != null; 153 154 // Look up the object in JNDI and find the JCR Repository object ... 155 String jndiName = connInfo.getRepositoryPath(); 156 if (jndiName == null) { 157 String msg = JdbcLocalI18n.urlMustContainJndiNameOfRepositoryOrRepositoriesObject.text(); 158 throw new SQLException(msg); 159 } 160 161 Context context = null; 162 try { 163 context = this.jcrContext.createContext(connInfo.getProperties()); 164 } catch (NamingException e) { 165 throw new SQLException(JdbcLocalI18n.unableToGetJndiContext.text(e.getLocalizedMessage())); 166 } 167 if (context == null) { 168 throw new SQLException(JdbcLocalI18n.unableToFindObjectInJndi.text(jndiName)); 169 170 } 171 String repositoryName = "NotAssigned"; 172 try { 173 174 Object target = context.lookup(jndiName); 175 repositoryName = connInfo.getRepositoryName(); 176 177 if (target instanceof Repositories) { 178 logger.trace("JNDI Lookup found Repositories "); 179 Repositories repositories = (Repositories)target; 180 181 if (repositoryName == null) { 182 repositoryNames = repositories.getRepositoryNames(); 183 if (repositoryNames == null || repositoryNames.isEmpty()) { 184 throw new SQLException(JdbcLocalI18n.noRepositoryNamesFound.text()); 185 } 186 if (repositoryNames.size() == 1) { 187 repositoryName = repositoryNames.iterator().next(); 188 connInfo.setRepositoryName(repositoryName); 189 logger.trace("Setting Repository {0} as default", repositoryName); 190 191 } else { 192 throw new SQLException(JdbcLocalI18n.objectInJndiIsRepositories.text(jndiName)); 193 } 194 } 195 try { 196 repository = repositories.getRepository(repositoryName); 197 } catch (RepositoryException e) { 198 throw new SQLException(JdbcLocalI18n.unableToFindNamedRepository.text(jndiName, repositoryName)); 199 } 200 } else if (target instanceof Repository) { 201 logger.trace("JNDI Lookup found a Repository"); 202 repository = (Repository)target; 203 repositoryNames = new HashSet<String>(1); 204 205 if (repositoryName == null) { 206 repositoryName = ("DefaultRepository"); 207 connInfo.setRepositoryName(repositoryName); 208 } 209 210 repositoryNames.add(repositoryName); 211 } else { 212 throw new SQLException(JdbcLocalI18n.objectInJndiMustBeRepositoryOrRepositories.text(jndiName)); 213 } 214 assert repository != null; 215 } catch (NamingException e) { 216 throw new SQLException(JdbcLocalI18n.unableToFindObjectInJndi.text(jndiName), e); 217 } 218 this.setRepositoryName(repositoryName); 219 this.setRepositoryNames(repositoryNames); 220 } 221 222 @Override 223 public boolean isValid( final int timeout ) throws RepositoryException { 224 225 LocalSession ls = getLocalSession(); 226 if (!ls.getSession().isLive()) { 227 ls.remove(); 228 return false; 229 } 230 231 return true; 232 } 233 234 @Override 235 public void closeStatement() { 236 LocalSession session = getCurrentLocalSession(); 237 try { 238 if (session != null) { 239 session.remove(); 240 } 241 } catch (Exception e) { 242 // do nothing 243 } 244 } 245 246 @Override 247 public void close() { 248 for (LocalSession id : TRANSACTION_IDS) { 249 id.remove(); 250 } 251 this.repository = null; 252 } 253 254 @SuppressWarnings( "unused" ) 255 @Override 256 public void rollback() throws RepositoryException { 257 closeStatement(); 258 } 259 260 @Override 261 public <T> T unwrap( Class<T> iface ) throws SQLException { 262 263 try { 264 if (iface.isInstance(this)) { 265 return iface.cast(this); 266 } 267 268 if (iface.isInstance(Workspace.class)) { 269 Workspace workspace = getLocalSession().getSession().getWorkspace(); 270 return iface.cast(workspace); 271 } 272 } catch (RepositoryException re) { 273 throw new SQLException(re.getLocalizedMessage()); 274 } 275 276 throw new SQLException(JdbcLocalI18n.classDoesNotImplementInterface.text(Connection.class.getSimpleName(), 277 iface.getName())); 278 } 279 280 class JNDIConnectionInfo extends ConnectionInfo { 281 282 protected JNDIConnectionInfo( String url, 283 Properties properties ) { 284 super(url, properties); 285 } 286 287 @Override 288 public String getUrlExample() { 289 return JNDI_EXAMPLE_URL; 290 } 291 292 @Override 293 public String getUrlPrefix() { 294 return LocalJcrDriver.JNDI_URL_PREFIX; 295 } 296 297 @Override 298 protected void addRepositoryNamePropertyInfo( List<DriverPropertyInfo> results ) { 299 boolean no_errors = results.size() == 0; 300 boolean nameRequired = false; 301 if (getRepositoryName() == null) { 302 boolean found = false; 303 if (no_errors) { 304 try { 305 Context context = getJcrContext().createContext(getProperties()); 306 Object obj = context.lookup(getRepositoryPath()); 307 if (obj instanceof Repositories) { 308 nameRequired = true; 309 found = true; 310 } else if (obj instanceof Repository) { 311 found = true; 312 } 313 } catch (NamingException e) { 314 // do nothing about it ... 315 } 316 } 317 if (nameRequired || !found) { 318 DriverPropertyInfo info = new DriverPropertyInfo(JdbcLocalI18n.repositoryNamePropertyName.text(), null); 319 info.description = JdbcLocalI18n.repositoryNamePropertyDescription.text(); 320 info.required = nameRequired; 321 info.choices = null; 322 results.add(info); 323 } 324 } 325 } 326 } 327 328}