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.DriverPropertyInfo; 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Properties; 022import javax.jcr.Credentials; 023import javax.jcr.SimpleCredentials; 024import org.modeshape.common.text.TextDecoder; 025import org.modeshape.common.text.UrlEncoder; 026import org.modeshape.common.util.StringUtil; 027import org.modeshape.jcr.api.Repositories; 028import org.modeshape.jdbc.JdbcLocalI18n; 029import org.modeshape.jdbc.LocalJcrDriver; 030 031/** 032 * The ConnectionInfo contains the information used to connect to the Jcr Repository. 033 */ 034public abstract class ConnectionInfo { 035 public static final TextDecoder URL_DECODER = new UrlEncoder(); 036 037 protected String url; 038 protected String repositoryPath; 039 protected Properties properties; 040 private char propertyDelimiter = '?'; 041 042 protected ConnectionInfo( String url, 043 Properties properties ) { 044 this.url = url; 045 this.properties = properties; 046 } 047 048 protected void init() { 049 Properties props = getProperties() != null ? (Properties)getProperties().clone() : new Properties(); 050 repositoryPath = getUrl().substring(this.getUrlPrefix().length()); 051 052 // Find any URL parameters ... 053 int questionMarkIndex = repositoryPath.indexOf('?'); 054 if (questionMarkIndex != -1) { 055 if (repositoryPath.length() > questionMarkIndex + 1) { 056 String paramStr = repositoryPath.substring(questionMarkIndex + 1); 057 for (String param : paramStr.split("&")) { 058 String[] pair = param.split("="); 059 if (pair.length > 1) { 060 String key = URL_DECODER.decode(pair[0] != null ? pair[0].trim() : null); 061 String value = URL_DECODER.decode(pair[1] != null ? pair[1].trim() : null); 062 if (!props.containsKey(key)) { 063 props.put(key, value); 064 } 065 } 066 } 067 } 068 repositoryPath = repositoryPath.substring(0, questionMarkIndex).trim(); 069 } 070 071 Properties newprops = new Properties(); 072 newprops.putAll(props); 073 this.setProperties(newprops); 074 String url = getUrl(); 075 this.setUrl(url != null ? url.trim() : null); 076 } 077 078 /** 079 * Get the original URL of the connection. 080 * 081 * @return the URL; never null 082 */ 083 public String getUrl() { 084 return url; 085 } 086 087 /** 088 * Get the part of the {@link #getUrl()} that indicates the path to connect to the Repository. This value should be prefixed 089 * by the {@link #getUrlPrefix()} in the {@link #getUrl()}. 090 * 091 * @return String 092 */ 093 public String getRepositoryPath() { 094 return this.repositoryPath; 095 } 096 097 /** 098 * Get the immutable properties for the connection. 099 * 100 * @return the properties; never null 101 */ 102 public Properties getProperties() { 103 return properties; 104 } 105 106 /** 107 * Get the name of the repository. This is required only if the {@link Repositories} instance is being used to obtain the 108 * Repository. 109 * 110 * @return the name of the repository, or null if no repository name was specified 111 */ 112 public String getRepositoryName() { 113 return properties.getProperty(LocalJcrDriver.REPOSITORY_PROPERTY_NAME); 114 } 115 116 /** 117 * Call to set the repository name. This is called when no repository name is set on the URL, but there is only one repository 118 * in the list. 119 * 120 * @param repositoryName 121 */ 122 void setRepositoryName( String repositoryName ) { 123 this.properties.setProperty(LocalJcrDriver.REPOSITORY_PROPERTY_NAME, repositoryName); 124 } 125 126 /** 127 * Get the name of the workspace. This is not required, and if abscent implies obtaining the JCR Repository's default 128 * workspace. 129 * 130 * @return the name of the workspace, or null if no workspace name was specified 131 */ 132 public String getWorkspaceName() { 133 return properties.getProperty(LocalJcrDriver.WORKSPACE_PROPERTY_NAME); 134 } 135 136 /** 137 * Call to set the workspace name. This is not required, and if abscent implies obtaining the JCR Repository's default 138 * workspace. 139 * 140 * @param workSpaceName 141 */ 142 public void setWorkspaceName( String workSpaceName ) { 143 properties.setProperty(LocalJcrDriver.WORKSPACE_PROPERTY_NAME, workSpaceName); 144 } 145 146 /** 147 * Get the JCR user name. This is not required, and if abscent implies that no credentials should be used when obtaining a JCR 148 * Session. 149 * 150 * @return the JCR user name, or null if no user name was specified 151 */ 152 public String getUsername() { 153 return properties.getProperty(LocalJcrDriver.USERNAME_PROPERTY_NAME); 154 } 155 156 /** 157 * Get the JCR password. This is not required. 158 * 159 * @return the JCR password, or null if no password was specified 160 */ 161 public char[] getPassword() { 162 String result = properties.getProperty(LocalJcrDriver.PASSWORD_PROPERTY_NAME); 163 return result != null ? result.toCharArray() : null; 164 } 165 166 /** 167 * Return true of Teiid support is required for this connection. 168 * 169 * @return true if Teiid support is required. 170 */ 171 public boolean isTeiidSupport() { 172 String result = properties.getProperty(LocalJcrDriver.TEIID_SUPPORT_PROPERTY_NAME); 173 if (result == null) { 174 return false; 175 } 176 return result.equalsIgnoreCase(Boolean.TRUE.toString()); 177 } 178 179 void setUrl( String url ) { 180 this.url = url; 181 } 182 183 void setProperties( Properties properties ) { 184 this.properties = properties; 185 } 186 187 /** 188 * Get the effective URL of this connection, which places all properties on the URL (with a '*' for each character in the 189 * password property) 190 * 191 * @return the effective URL; never null 192 */ 193 public String getEffectiveUrl() { 194 StringBuilder url = new StringBuilder(this.getUrlPrefix()); 195 url.append(this.getRepositoryPath()); 196 char propertyDelim = getPropertyDelimiter(); 197 for (String propertyName : getProperties().stringPropertyNames()) { 198 String value = getProperties().getProperty(propertyName); 199 if (value == null) { 200 continue; 201 } 202 if (LocalJcrDriver.PASSWORD_PROPERTY_NAME.equals(propertyName)) { 203 value = StringUtil.createString('*', value.length()); 204 } 205 url.append(propertyDelim).append(propertyName).append('=').append(value); 206 propertyDelim = '&'; 207 } 208 return url.toString(); 209 } 210 211 /** 212 * Return the starting property delimiter 213 * 214 * @return char property delimiter 215 */ 216 protected char getPropertyDelimiter() { 217 return propertyDelimiter; 218 } 219 220 protected void setPropertyDelimiter( char delimiter ) { 221 this.propertyDelimiter = delimiter; 222 } 223 224 /** 225 * Obtain the array of {@link DriverPropertyInfo} objects that describe the missing properties. 226 * 227 * @return DriverPropertyInfo the property infos; never null but possibly empty 228 */ 229 public DriverPropertyInfo[] getPropertyInfos() { 230 List<DriverPropertyInfo> results = new ArrayList<DriverPropertyInfo>(); 231 232 addUrlPropertyInfo(results); 233 addUserNamePropertyInfo(results); 234 addPasswordPropertyInfo(results); 235 addWorkspacePropertyInfo(results); 236 addRepositoryNamePropertyInfo(results); 237 238 return results.toArray(new DriverPropertyInfo[results.size()]); 239 } 240 241 protected void addUrlPropertyInfo( List<DriverPropertyInfo> results ) { 242 if (getUrl() == null) { 243 DriverPropertyInfo info = new DriverPropertyInfo(JdbcLocalI18n.urlPropertyName.text(), null); 244 info.description = JdbcLocalI18n.urlPropertyDescription.text(this.getEffectiveUrl(), getUrlExample()); 245 info.required = true; 246 info.choices = new String[] {getUrlExample()}; 247 results.add(info); 248 } 249 } 250 251 protected void addUserNamePropertyInfo( List<DriverPropertyInfo> results ) { 252 if (getUsername() == null) { 253 DriverPropertyInfo info = new DriverPropertyInfo(JdbcLocalI18n.usernamePropertyName.text(), null); 254 info.description = JdbcLocalI18n.usernamePropertyDescription.text(); 255 info.required = false; 256 info.choices = null; 257 results.add(info); 258 } 259 } 260 261 protected void addPasswordPropertyInfo( List<DriverPropertyInfo> results ) { 262 if (getPassword() == null) { 263 DriverPropertyInfo info = new DriverPropertyInfo(JdbcLocalI18n.passwordPropertyName.text(), null); 264 info.description = JdbcLocalI18n.passwordPropertyDescription.text(); 265 info.required = false; 266 info.choices = null; 267 results.add(info); 268 } 269 } 270 271 protected void addWorkspacePropertyInfo( List<DriverPropertyInfo> results ) { 272 if (getWorkspaceName() == null) { 273 DriverPropertyInfo info = new DriverPropertyInfo(JdbcLocalI18n.workspaceNamePropertyName.text(), null); 274 info.description = JdbcLocalI18n.workspaceNamePropertyDescription.text(); 275 info.required = false; 276 info.choices = null; 277 results.add(info); 278 } 279 } 280 281 protected void addRepositoryNamePropertyInfo( List<DriverPropertyInfo> results ) { 282 if (getRepositoryName() == null) { 283 DriverPropertyInfo info = new DriverPropertyInfo(JdbcLocalI18n.repositoryNamePropertyName.text(), null); 284 info.description = JdbcLocalI18n.repositoryNamePropertyDescription.text(); 285 info.required = true; 286 info.choices = null; 287 results.add(info); 288 } 289 } 290 291 /** 292 * The delegate should provide an example of the URL to be used 293 * 294 * @return String url example 295 */ 296 public abstract String getUrlExample(); 297 298 /** 299 * The delegate should provide the prefix defined by the {@link LocalJcrDriver} 300 * 301 * @return String url prefix 302 */ 303 public abstract String getUrlPrefix(); 304 305 /** 306 * Return the credentials based on the user name and password. 307 * 308 * @return Credentials 309 */ 310 public Credentials getCredentials() { 311 String username = getUsername(); 312 char[] password = getPassword(); 313 if (username != null) { 314 return new SimpleCredentials(username, password); 315 } 316 return null; 317 } 318 319}