001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 023 package org.jboss.dna.repository.util; 024 025 import java.util.Map; 026 import java.util.concurrent.ConcurrentHashMap; 027 import javax.jcr.Credentials; 028 import javax.jcr.Repository; 029 import javax.jcr.RepositoryException; 030 import javax.jcr.Session; 031 import javax.jcr.SimpleCredentials; 032 import org.jboss.dna.common.SystemFailureException; 033 import org.jboss.dna.common.util.CheckArg; 034 035 /** 036 * A SessionFactory implementation that creates {@link Session} instances using {@link Repository} instances. 037 * <p> 038 * This factory using a naming convention where the name supplied to the {@link #createSession(String)} contains both the name of 039 * the repository and the name of the workspace. Typically, this is <i><code>repositoryName/workspaceName</code></i>, where 040 * <code>repositoryName</code> is the name under which the Repository instance was bound, and <code>workspaceName</code> is 041 * the name of the workspace. Note that this method looks for the last delimiter in the whole name to distinguish between the 042 * repository and workspace names. 043 * </p> 044 * <p> 045 * For example, if "<code>java:comp/env/repository/dataRepository/myWorkspace</code>" is passed to the 046 * {@link #createSession(String)} method, this factory will look for a {@link Repository} instance registered with the name "<code>java:comp/env/repository/dataRepository</code>" 047 * and use it to {@link Repository#login(String) create a session} to the workspace named "<code>myWorkspace</code>". 048 * </p> 049 * <p> 050 * By default, this factory creates an anonymous JCR session. To use sessions with specific {@link Credentials}, simply 051 * {@link #registerCredentials(String, Credentials) register} credentials for the appropriate repository/workspace name. For 052 * security reasons, it is not possible to retrieve the Credentials once registered with this factory. 053 * </p> 054 * @author Randall Hauch 055 */ 056 public abstract class AbstractSessionFactory implements SessionFactory { 057 058 protected static char[] DEFAULT_DELIMITERS = new char[] {'/'}; 059 060 private final char[] workspaceDelims; 061 private final String workspaceDelimsRegexCharacterSet; 062 private final Map<String, Credentials> credentials = new ConcurrentHashMap<String, Credentials>(); 063 064 /** 065 * Create an instance of the factory with the default delimiters. 066 */ 067 public AbstractSessionFactory() { 068 this(null); 069 } 070 071 /** 072 * Create an instance of the factory by supplying naming context and the characters that may be used to delimit the workspace 073 * name from the repository name. 074 * @param workspaceDelimiters the delimiters, or null/empty if the default delimiter of '/' should be used. 075 * @throws IllegalArgumentException if the context parameter is null 076 */ 077 public AbstractSessionFactory( char... workspaceDelimiters ) { 078 this.workspaceDelims = (workspaceDelimiters == null || workspaceDelimiters.length == 0) ? DEFAULT_DELIMITERS : workspaceDelimiters; 079 StringBuilder sb = new StringBuilder(); 080 for (char delim : this.workspaceDelims) { 081 switch (delim) { 082 case '\\': 083 sb.append("\\"); 084 break; 085 // case '[' : sb.append("\\["); break; 086 case ']': 087 sb.append("\\]"); 088 break; 089 case '-': 090 sb.append("\\-"); 091 break; 092 case '^': 093 sb.append("\\^"); 094 break; 095 default: 096 sb.append(delim); 097 } 098 } 099 this.workspaceDelimsRegexCharacterSet = sb.toString(); 100 } 101 102 /** 103 * Convenience method to bind a repository in JNDI. Repository instances can be bound into JNDI using any technique, so this 104 * method need not be used. <i>Note that the name should not contain the workspace part.</i> 105 * @param name the name of the repository, without the workspace name component. 106 * @param repository the repository to be bound, or null if an existing repository should be unbound. 107 */ 108 public void registerRepository( String name, Repository repository ) { 109 assert name != null; 110 // Remove all trailing delimiters ... 111 name = name.replaceAll("[" + this.workspaceDelimsRegexCharacterSet + "]+$", ""); 112 if (repository != null) { 113 this.doRegisterRepository(name, repository); 114 } else { 115 this.doUnregisterRepository(name); 116 } 117 } 118 119 protected abstract void doRegisterRepository( String name, Repository repository ) throws SystemFailureException; 120 121 protected abstract void doUnregisterRepository( String name ) throws SystemFailureException; 122 123 protected abstract Repository findRegisteredRepository( String name ) throws SystemFailureException; 124 125 /** 126 * Register the credentials for the repository and workspace given by the supplied name, username and password. This is 127 * equivalent to calling <code>registerCredentials(name, new SimpleCredentials(username,password))</code>, although if 128 * <code>username</code> is null then this is equivalent to <code>registerCredentials(name,null)</code>. 129 * @param name the name of the repository and workspace 130 * @param username the username to use, or null if the existing credentials for the named workspace should be removed 131 * @param password the password, may be null or empty 132 * @return true if this overwrote existing credentials 133 * @see #registerCredentials(String, Credentials) 134 * @see #removeCredentials(String) 135 */ 136 public boolean registerCredentials( String name, String username, char[] password ) { 137 if (password == null && username != null) password = new char[] {}; 138 Credentials creds = username == null ? null : new SimpleCredentials(username, password); 139 return registerCredentials(name, creds); 140 } 141 142 /** 143 * Register the credentials to be used for the named repository and workspace. Use the same name as used to 144 * {@link #createSession(String) create sessions}. 145 * @param name the name of the repository and workspace 146 * @param credentials the credentials to use, or null if the existing credentials for the named workspace should be removed 147 * @return true if this overwrote existing credentials 148 * @see #registerCredentials(String, String, char[]) 149 * @see #removeCredentials(String) 150 */ 151 public boolean registerCredentials( String name, Credentials credentials ) { 152 boolean foundExisting = false; 153 name = name != null ? name.trim() : null; 154 if (credentials == null) { 155 foundExisting = this.credentials.remove(name) != null; 156 } else { 157 foundExisting = this.credentials.put(name, credentials) != null; 158 } 159 return foundExisting; 160 } 161 162 /** 163 * Remove any credentials associated with the named repository and workspace. This is equivalent to calling 164 * <code>registerCredentials(name,null)</code>. 165 * @param name the name of the repository and workspace 166 * @return true if existing credentials were found and removed, or false if no such credentials existed 167 * @see #registerCredentials(String, Credentials) 168 */ 169 public boolean removeCredentials( String name ) { 170 return registerCredentials(name, null); 171 } 172 173 /** 174 * {@inheritDoc} 175 */ 176 public Session createSession( String name ) throws RepositoryException { 177 CheckArg.isNotNull(name, "session name"); 178 name = name.trim(); 179 // Look up the Repository object in JNDI ... 180 String repositoryName = getRepositoryName(name); 181 Repository repository = findRegisteredRepository(repositoryName); 182 183 // Determine the name of the workspace ... 184 String workspaceName = getWorkspaceName(name); 185 186 // Look up any credentials, which may be null ... 187 Credentials creds = this.credentials.get(name); 188 189 // Create a session to the specified workspace and using the credentials (either or both may be null) ... 190 return repository.login(creds, workspaceName); 191 } 192 193 protected String getWorkspaceName( String name ) { 194 assert name != null; 195 int index = getIndexOfLastWorkspaceDelimiter(name); 196 if (index == -1) return null; 197 if ((index + 1) == name.length()) return null; // delim is the last character 198 return name.substring(index + 1); 199 } 200 201 protected String getRepositoryName( String name ) { 202 assert name != null; 203 int index = getIndexOfLastWorkspaceDelimiter(name); 204 if (index == -1) return name; // no delim 205 if ((index + 1) == name.length()) return name.substring(0, index); // delim as last character 206 return name.substring(0, index); 207 } 208 209 protected int getIndexOfLastWorkspaceDelimiter( String name ) { 210 int index = -1; 211 for (char delim : this.workspaceDelims) { 212 int i = name.lastIndexOf(delim); 213 index = Math.max(index, i); 214 } 215 return index; 216 } 217 218 }