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 package org.jboss.dna.connector.svn; 023 024 import java.io.ByteArrayInputStream; 025 import java.io.ByteArrayOutputStream; 026 import java.io.IOException; 027 import java.io.ObjectInputStream; 028 import java.io.ObjectOutputStream; 029 import java.util.Enumeration; 030 import java.util.HashMap; 031 import java.util.Hashtable; 032 import java.util.Map; 033 import java.util.UUID; 034 import java.util.concurrent.atomic.AtomicInteger; 035 import javax.naming.BinaryRefAddr; 036 import javax.naming.Context; 037 import javax.naming.Name; 038 import javax.naming.RefAddr; 039 import javax.naming.Reference; 040 import javax.naming.Referenceable; 041 import javax.naming.StringRefAddr; 042 import javax.naming.spi.ObjectFactory; 043 import org.jboss.dna.common.i18n.I18n; 044 import org.jboss.dna.common.util.CheckArg; 045 import org.jboss.dna.graph.DnaLexicon; 046 import org.jboss.dna.graph.cache.CachePolicy; 047 import org.jboss.dna.graph.connectors.RepositoryConnection; 048 import org.jboss.dna.graph.connectors.RepositoryContext; 049 import org.jboss.dna.graph.connectors.RepositorySource; 050 import org.jboss.dna.graph.connectors.RepositorySourceCapabilities; 051 import org.jboss.dna.graph.connectors.RepositorySourceException; 052 import org.jboss.dna.graph.properties.Property; 053 import org.tmatesoft.svn.core.SVNException; 054 import org.tmatesoft.svn.core.SVNURL; 055 import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; 056 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; 057 import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; 058 import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; 059 import org.tmatesoft.svn.core.io.SVNRepository; 060 import org.tmatesoft.svn.core.io.SVNRepositoryFactory; 061 import org.tmatesoft.svn.core.wc.SVNWCUtil; 062 063 /** 064 * A repository source that uses a SVN repository instance to manage the content. This source is capable of using an existing 065 * {@link SVNRepository} instance or creating a new instance. This process is controlled entirely by the JavaBean properties of 066 * the SVNRepositorySource instance. Like other {@link RepositorySource} classes, instances of SVNRepositorySource can be placed 067 * into JNDI and do support the creation of {@link Referenceable JNDI referenceable} objects and resolution of references into 068 * SVNRepositorySource. </p> 069 * 070 * @author Serge Pagop 071 */ 072 public class SVNRepositorySource implements RepositorySource, ObjectFactory { 073 074 private static final long serialVersionUID = 1L; 075 /** 076 * The default limit is {@value} for retrying {@link RepositoryConnection connection} calls to the underlying source. 077 */ 078 public static final int DEFAULT_RETRY_LIMIT = 0; 079 080 protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(false, true); 081 082 public static final String DEFAULT_UUID_PROPERTY_NAME = DnaLexicon.UUID.getString(); 083 084 protected static final String SOURCE_NAME = "sourceName"; 085 protected static final String ROOT_NODE_UUID = "rootNodeUuid"; 086 protected static final String DEFAULT_CACHE_POLICY = "defaultCachePolicy"; 087 protected static final String UUID_PROPERTY_NAME = "uuidPropertyName"; 088 protected static final String SVN_REPOS_JNDI_NAME = "svnReposJndiName"; 089 protected static final String SVN_REPOS_FACTORY_JNDI_NAME = "svnReposFactoryJndiName"; 090 protected static final String SVN_URL = "svnURL"; 091 protected static final String SVN_USERNAME = "svnUsername"; 092 protected static final String SVN_PASSWORD = "svnPassword"; 093 protected static final String RETRY_LIMIT = "retryLimit"; 094 095 private final AtomicInteger retryLimit = new AtomicInteger(DEFAULT_RETRY_LIMIT); 096 private String name; 097 private UUID rootNodeUuid = UUID.randomUUID(); 098 private String uuidPropertyName = DEFAULT_UUID_PROPERTY_NAME; 099 private String svnURL; 100 private String svnUsername; 101 private String svnPassword; 102 private CachePolicy defaultCachePolicy; 103 104 private transient Context jndiContext; 105 private transient RepositoryContext repositoryContext; 106 private transient SVNRepository svnRepository; 107 108 /** 109 * Create a repository source instance. 110 */ 111 public SVNRepositorySource() { 112 } 113 114 /** 115 * {@inheritDoc} 116 * 117 * @see org.jboss.dna.graph.connectors.RepositorySource#initialize(org.jboss.dna.graph.connectors.RepositoryContext) 118 */ 119 public void initialize( RepositoryContext context ) throws RepositorySourceException { 120 this.repositoryContext = context; 121 } 122 123 /** 124 * @return repositoryContext 125 */ 126 public RepositoryContext getRepositoryContext() { 127 return repositoryContext; 128 } 129 130 /** 131 * {@inheritDoc} 132 */ 133 public String getName() { 134 return this.name; 135 } 136 137 /** 138 * {@inheritDoc} 139 * 140 * @see org.jboss.dna.graph.connectors.RepositorySource#getRetryLimit() 141 */ 142 public int getRetryLimit() { 143 return retryLimit.get(); 144 } 145 146 /** 147 * {@inheritDoc} 148 * 149 * @see org.jboss.dna.graph.connectors.RepositorySource#setRetryLimit(int) 150 */ 151 public void setRetryLimit( int limit ) { 152 retryLimit.set(limit < 0 ? 0 : limit); 153 } 154 155 /** 156 * Set the name of this source 157 * 158 * @param name the name for this source 159 */ 160 public synchronized void setName( String name ) { 161 if (this.name == name || this.name != null && this.name.equals(name)) return; // unchanged 162 this.name = name; 163 } 164 165 /** 166 * Get the default cache policy for this source, or null if the global default cache policy should be used 167 * 168 * @return the default cache policy, or null if this source has no explicit default cache policy 169 */ 170 public CachePolicy getDefaultCachePolicy() { 171 return defaultCachePolicy; 172 } 173 174 /** 175 * @param defaultCachePolicy Sets defaultCachePolicy to the specified value. 176 */ 177 public synchronized void setDefaultCachePolicy( CachePolicy defaultCachePolicy ) { 178 if (this.defaultCachePolicy == defaultCachePolicy || this.defaultCachePolicy != null 179 && this.defaultCachePolicy.equals(defaultCachePolicy)) return; // unchanged 180 this.defaultCachePolicy = defaultCachePolicy; 181 } 182 183 /** 184 * Get the UUID of the root node for the cache. If the cache exists, this UUID is not used but is instead set to the UUID of 185 * the existing root node. 186 * 187 * @return the UUID of the root node for the cache. 188 */ 189 public String getRootNodeUuid() { 190 return this.rootNodeUuid.toString(); 191 } 192 193 /** 194 * Get the UUID of the root node for the cache. If the cache exists, this UUID is not used but is instead set to the UUID of 195 * the existing root node. 196 * 197 * @return the UUID of the root node for the cache. 198 */ 199 public UUID getRootNodeUuidObject() { 200 return this.rootNodeUuid; 201 } 202 203 /** 204 * Set the UUID of the root node in this repository. If the cache exists, this UUID is not used but is instead set to the UUID 205 * of the existing root node. 206 * 207 * @param rootNodeUuid the UUID of the root node for the cache, or null if the UUID should be randomly generated 208 */ 209 public synchronized void setRootNodeUuid( String rootNodeUuid ) { 210 UUID uuid = null; 211 if (rootNodeUuid == null) uuid = UUID.randomUUID(); 212 else uuid = UUID.fromString(rootNodeUuid); 213 if (this.rootNodeUuid.equals(uuid)) return; // unchanged 214 this.rootNodeUuid = uuid; 215 } 216 217 /** 218 * Get the {@link Property#getName() property name} where the UUID is stored for each node. 219 * 220 * @return the name of the UUID property; never null 221 */ 222 public String getUuidPropertyName() { 223 return this.uuidPropertyName; 224 } 225 226 /** 227 * Set the {@link Property#getName() property name} where the UUID is stored for each node. 228 * 229 * @param uuidPropertyName the name of the UUID property, or null if the {@link #DEFAULT_UUID_PROPERTY_NAME default name} 230 * should be used 231 */ 232 public synchronized void setUuidPropertyName( String uuidPropertyName ) { 233 if (uuidPropertyName == null || uuidPropertyName.trim().length() == 0) uuidPropertyName = DEFAULT_UUID_PROPERTY_NAME; 234 if (this.uuidPropertyName.equals(uuidPropertyName)) return; // unchanged 235 this.uuidPropertyName = uuidPropertyName; 236 } 237 238 public String getSVNURL() { 239 return this.svnURL; 240 } 241 242 /** 243 * Set the url for the subversion repository. 244 * 245 * @param url - the url location. 246 * @throws IllegalArgumentException If svn url is null or empty 247 */ 248 public void setSVNURL( String url ) { 249 CheckArg.isNotEmpty(url, "SVNURL"); 250 this.svnURL = url; 251 } 252 253 public String getSVNUsername() { 254 return this.svnUsername; 255 } 256 257 /** 258 * @param username 259 */ 260 public void setSVNUsername( String username ) { 261 this.svnUsername = username; 262 } 263 264 public String getSVNPassword() { 265 return this.svnPassword; 266 } 267 268 /** 269 * @param password 270 */ 271 public void setSVNPassword( String password ) { 272 this.svnPassword = password; 273 } 274 275 /** 276 * {@inheritDoc} 277 * 278 * @see org.jboss.dna.graph.connectors.RepositorySource#getCapabilities() 279 */ 280 public RepositorySourceCapabilities getCapabilities() { 281 return CAPABILITIES; 282 } 283 284 /** 285 * {@inheritDoc} 286 * 287 * @see org.jboss.dna.graph.connectors.RepositorySource#getConnection() 288 */ 289 public RepositoryConnection getConnection() throws RepositorySourceException { 290 if (getName() == null) { 291 I18n msg = SVNRepositoryConnectorI18n.propertyIsRequired; 292 throw new RepositorySourceException(getName(), msg.text("name")); 293 } 294 if (getUuidPropertyName() == null) { 295 I18n msg = SVNRepositoryConnectorI18n.propertyIsRequired; 296 throw new RepositorySourceException(getUuidPropertyName(), msg.text("uuidPropertyName")); 297 } 298 SVNURL svnURL = null; 299 if (this.svnRepository == null) { 300 try { 301 svnURL = SVNURL.parseURIDecoded(getSVNURL()); 302 String usedProtocol = this.getSVNURL().substring(0, this.getSVNURL().lastIndexOf(":")); 303 if (usedProtocol.equals(SVNProtocol.SVN.value()) || usedProtocol.equals(SVNProtocol.SVN_SSH.value())) { 304 SVNRepositoryFactoryImpl.setup(); 305 this.svnRepository = SVNRepositoryFactory.create(svnURL); 306 ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(this.getSVNUsername(), 307 this.getSVNPassword()); 308 this.svnRepository.setAuthenticationManager(authManager); 309 } 310 if (usedProtocol.equals(SVNProtocol.HTTP.value()) || usedProtocol.equals(SVNProtocol.HTTPS.value())) { 311 DAVRepositoryFactory.setup(); 312 this.svnRepository = DAVRepositoryFactory.create(svnURL); 313 ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(this.getSVNUsername(), 314 this.getSVNPassword()); 315 this.svnRepository.setAuthenticationManager(authManager); 316 } 317 if (usedProtocol.equals(SVNProtocol.FILE.value())) { 318 FSRepositoryFactory.setup(); 319 this.svnRepository = FSRepositoryFactory.create(svnURL); 320 ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(this.getSVNUsername(), 321 this.getSVNPassword()); 322 this.svnRepository.setAuthenticationManager(authManager); 323 } 324 325 } catch (SVNException ex) { 326 I18n msg = SVNRepositoryConnectorI18n.propertyIsRequired; 327 throw new RepositorySourceException(getSVNURL(), msg.text(this.getSVNURL()), ex); 328 } 329 } 330 return new SVNRepositoryConnection(this.getName(), this.getDefaultCachePolicy(), this.getUuidPropertyName(), 331 this.svnRepository); 332 } 333 334 protected Context getContext() { 335 return this.jndiContext; 336 } 337 338 protected synchronized void setContext( Context context ) { 339 this.jndiContext = context; 340 } 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override 346 public boolean equals( Object obj ) { 347 if (obj == this) return true; 348 if (obj instanceof SVNRepositorySource) { 349 SVNRepositorySource that = (SVNRepositorySource)obj; 350 if (this.getName() == null) { 351 if (that.getName() != null) return false; 352 } else { 353 if (!this.getName().equals(that.getName())) return false; 354 } 355 return true; 356 } 357 return false; 358 } 359 360 /** 361 * {@inheritDoc} 362 * 363 * @see javax.naming.Referenceable#getReference() 364 */ 365 public synchronized Reference getReference() { 366 String className = getClass().getName(); 367 String factoryClassName = this.getClass().getName(); 368 Reference ref = new Reference(className, factoryClassName, null); 369 370 if (getName() != null) { 371 ref.add(new StringRefAddr(SOURCE_NAME, getName())); 372 } 373 if (getRootNodeUuid() != null) { 374 ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid().toString())); 375 } 376 if (getUuidPropertyName() != null) { 377 ref.add(new StringRefAddr(UUID_PROPERTY_NAME, getUuidPropertyName())); 378 } 379 if (getSVNURL() != null) { 380 ref.add(new StringRefAddr(SVN_URL, getSVNURL())); 381 } 382 if (getSVNUsername() != null) { 383 ref.add(new StringRefAddr(SVN_USERNAME, getSVNUsername())); 384 } 385 if (getSVNPassword() != null) { 386 ref.add(new StringRefAddr(SVN_PASSWORD, getSVNPassword())); 387 } 388 if (getDefaultCachePolicy() != null) { 389 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 390 CachePolicy policy = getDefaultCachePolicy(); 391 try { 392 ObjectOutputStream oos = new ObjectOutputStream(baos); 393 oos.writeObject(policy); 394 ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY, baos.toByteArray())); 395 } catch (IOException e) { 396 I18n msg = SVNRepositoryConnectorI18n.errorSerializingCachePolicyInSource; 397 throw new RepositorySourceException(getName(), msg.text(policy.getClass().getName(), getName()), e); 398 } 399 } 400 ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit()))); 401 return ref; 402 } 403 404 /** 405 * {@inheritDoc} 406 * 407 * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, 408 * java.util.Hashtable) 409 */ 410 public Object getObjectInstance( Object obj, 411 Name name, 412 Context nameCtx, 413 Hashtable<?, ?> environment ) throws Exception { 414 if (obj instanceof Reference) { 415 Map<String, Object> values = new HashMap<String, Object>(); 416 Reference ref = (Reference)obj; 417 Enumeration<?> en = ref.getAll(); 418 while (en.hasMoreElements()) { 419 RefAddr subref = (RefAddr)en.nextElement(); 420 if (subref instanceof StringRefAddr) { 421 String key = subref.getType(); 422 Object value = subref.getContent(); 423 if (value != null) values.put(key, value.toString()); 424 } else if (subref instanceof BinaryRefAddr) { 425 String key = subref.getType(); 426 Object value = subref.getContent(); 427 if (value instanceof byte[]) { 428 // Deserialize ... 429 ByteArrayInputStream bais = new ByteArrayInputStream((byte[])value); 430 ObjectInputStream ois = new ObjectInputStream(bais); 431 value = ois.readObject(); 432 values.put(key, value); 433 } 434 } 435 } 436 String sourceName = (String)values.get(SOURCE_NAME); 437 String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID); 438 String uuidPropertyName = (String)values.get(UUID_PROPERTY_NAME); 439 String svnURL = (String)values.get(SVN_URL); 440 String svnUsername = (String)values.get(SVN_USERNAME); 441 String svnPassword = (String)values.get(SVN_PASSWORD); 442 Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY); 443 String retryLimit = (String)values.get(RETRY_LIMIT); 444 445 // Create the source instance ... 446 SVNRepositorySource source = new SVNRepositorySource(); 447 if (sourceName != null) source.setName(sourceName); 448 if (rootNodeUuidString != null) source.setRootNodeUuid(rootNodeUuidString); 449 if (uuidPropertyName != null) source.setUuidPropertyName(uuidPropertyName); 450 if (svnURL != null) source.setSVNURL(svnURL); 451 if (svnUsername != null) source.setSVNUsername(svnUsername); 452 if (svnPassword != null) source.setSVNPassword(svnPassword); 453 if (defaultCachePolicy instanceof CachePolicy) { 454 source.setDefaultCachePolicy((CachePolicy)defaultCachePolicy); 455 } 456 if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit)); 457 return source; 458 } 459 return null; 460 } 461 462 }