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