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.federation; 023 024 import java.util.Enumeration; 025 import java.util.HashMap; 026 import java.util.Hashtable; 027 import java.util.LinkedList; 028 import java.util.List; 029 import java.util.Map; 030 import java.util.concurrent.TimeUnit; 031 import java.util.concurrent.atomic.AtomicInteger; 032 import javax.naming.Context; 033 import javax.naming.RefAddr; 034 import javax.naming.Reference; 035 import javax.naming.StringRefAddr; 036 import javax.naming.spi.ObjectFactory; 037 import javax.security.auth.callback.Callback; 038 import javax.security.auth.callback.CallbackHandler; 039 import javax.security.auth.callback.NameCallback; 040 import javax.security.auth.callback.PasswordCallback; 041 import javax.security.auth.login.LoginException; 042 import net.jcip.annotations.ThreadSafe; 043 import org.jboss.dna.common.collection.Problems; 044 import org.jboss.dna.common.collection.SimpleProblems; 045 import org.jboss.dna.common.i18n.I18n; 046 import org.jboss.dna.common.util.CheckArg; 047 import org.jboss.dna.graph.ExecutionContext; 048 import org.jboss.dna.graph.ExecutionContextFactory; 049 import org.jboss.dna.graph.Graph; 050 import org.jboss.dna.graph.Location; 051 import org.jboss.dna.graph.Node; 052 import org.jboss.dna.graph.Subgraph; 053 import org.jboss.dna.graph.cache.BasicCachePolicy; 054 import org.jboss.dna.graph.cache.CachePolicy; 055 import org.jboss.dna.graph.connectors.RepositoryConnection; 056 import org.jboss.dna.graph.connectors.RepositoryConnectionFactory; 057 import org.jboss.dna.graph.connectors.RepositoryContext; 058 import org.jboss.dna.graph.connectors.RepositorySource; 059 import org.jboss.dna.graph.connectors.RepositorySourceCapabilities; 060 import org.jboss.dna.graph.connectors.RepositorySourceException; 061 import org.jboss.dna.graph.properties.NameFactory; 062 import org.jboss.dna.graph.properties.Path; 063 import org.jboss.dna.graph.properties.Property; 064 import org.jboss.dna.graph.properties.ValueFactories; 065 import org.jboss.dna.graph.properties.ValueFactory; 066 067 /** 068 * @author Randall Hauch 069 */ 070 @ThreadSafe 071 public class FederatedRepositorySource implements RepositorySource, ObjectFactory { 072 073 /** 074 */ 075 private static final long serialVersionUID = 7587346948013486977L; 076 077 /** 078 * The default limit is {@value} for retrying {@link RepositoryConnection connection} calls to the underlying source. 079 */ 080 public static final int DEFAULT_RETRY_LIMIT = 0; 081 082 public static final String DEFAULT_CONFIGURATION_SOURCE_PATH = "/"; 083 084 protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(true, true); 085 086 protected static final String REPOSITORY_NAME = "repositoryName"; 087 protected static final String SOURCE_NAME = "sourceName"; 088 protected static final String USERNAME = "username"; 089 protected static final String PASSWORD = "password"; 090 protected static final String CONFIGURATION_SOURCE_NAME = "configurationSourceName"; 091 protected static final String CONFIGURATION_SOURCE_PATH = "configurationSourcePath"; 092 protected static final String SECURITY_DOMAIN = "securityDomain"; 093 protected static final String RETRY_LIMIT = "retryLimit"; 094 095 public static final String DNA_FEDERATION_SEGMENT = "dna:federation"; 096 public static final String DNA_CACHE_SEGMENT = "dna:cache"; 097 public static final String DNA_PROJECTIONS_SEGMENT = "dna:projections"; 098 public static final String PROJECTION_RULES_CONFIG_PROPERTY_NAME = "dna:projectionRules"; 099 public static final String CACHE_POLICY_TIME_TO_LIVE_CONFIG_PROPERTY_NAME = "dna:timeToCache"; 100 101 private String repositoryName; 102 private String sourceName; 103 private String username; 104 private String password; 105 private String configurationSourceName; 106 private String configurationSourcePath = DEFAULT_CONFIGURATION_SOURCE_PATH; 107 private String securityDomain; 108 private final AtomicInteger retryLimit = new AtomicInteger(DEFAULT_RETRY_LIMIT); 109 private transient FederatedRepository repository; 110 private transient RepositoryContext repositoryContext; 111 112 /** 113 * Create a new instance of the source, which must still be properly initialized with a {@link #setRepositoryName(String) 114 * repository name}. 115 */ 116 public FederatedRepositorySource() { 117 super(); 118 } 119 120 /** 121 * Create a new instance of the source with the required repository name and federation service. 122 * 123 * @param repositoryName the repository name 124 * @throws IllegalArgumentException if the federation service is null or the repository name is null or blank 125 */ 126 public FederatedRepositorySource( String repositoryName ) { 127 super(); 128 CheckArg.isNotNull(repositoryName, "repositoryName"); 129 this.repositoryName = repositoryName; 130 } 131 132 /** 133 * {@inheritDoc} 134 * 135 * @see org.jboss.dna.graph.connectors.RepositorySource#initialize(org.jboss.dna.graph.connectors.RepositoryContext) 136 */ 137 public void initialize( RepositoryContext context ) throws RepositorySourceException { 138 this.repositoryContext = context; 139 } 140 141 /** 142 * @return repositoryContext 143 */ 144 public RepositoryContext getRepositoryContext() { 145 return repositoryContext; 146 } 147 148 /** 149 * {@inheritDoc} 150 */ 151 public synchronized String getName() { 152 return sourceName; 153 } 154 155 /** 156 * Set the name of this source. 157 * <p> 158 * This is a required property. 159 * </p> 160 * 161 * @param sourceName the name of this repository source 162 * @see #setConfigurationSourceName(String) 163 * @see #setConfigurationSourcePath(String) 164 * @see #setPassword(String) 165 * @see #setUsername(String) 166 * @see #setRepositoryName(String) 167 * @see #setPassword(String) 168 * @see #setUsername(String) 169 * @see #setName(String) 170 */ 171 public synchronized void setName( String sourceName ) { 172 if (this.sourceName == sourceName || this.sourceName != null && this.sourceName.equals(sourceName)) return; // unchanged 173 this.sourceName = sourceName; 174 changeRepositoryConfig(); 175 } 176 177 /** 178 * {@inheritDoc} 179 * 180 * @see org.jboss.dna.graph.connectors.RepositorySource#getRetryLimit() 181 */ 182 public int getRetryLimit() { 183 return retryLimit.get(); 184 } 185 186 /** 187 * {@inheritDoc} 188 * 189 * @see org.jboss.dna.graph.connectors.RepositorySource#setRetryLimit(int) 190 */ 191 public void setRetryLimit( int limit ) { 192 retryLimit.set(limit < 0 ? 0 : limit); 193 } 194 195 /** 196 * Get the name in JNDI of a {@link RepositorySource} instance that should be used by the {@link FederatedRepository federated 197 * repository} as the configuration repository. 198 * <p> 199 * This is a required property. 200 * </p> 201 * 202 * @return the JNDI name of the {@link RepositorySource} instance that should be used for the configuration, or null if the 203 * federated repository instance is to be found in JNDI 204 * @see #setConfigurationSourceName(String) 205 */ 206 public String getConfigurationSourceName() { 207 return configurationSourceName; 208 } 209 210 /** 211 * Get the name of a {@link RepositorySource} instance that should be used by the {@link FederatedRepository federated 212 * repository} as the configuration repository. The instance will be retrieved from the {@link RepositoryConnectionFactory} 213 * instance from the {@link RepositoryContext#getRepositoryConnectionFactory() repository context} supplied during 214 * {@link RepositorySource#initialize(RepositoryContext) initialization}. 215 * <p> 216 * This is a required property. 217 * </p> 218 * 219 * @param sourceName the name of the {@link RepositorySource} instance that should be used for the configuration, or null if 220 * the federated repository instance is to be found in JNDI 221 * @see #getConfigurationSourceName() 222 * @see #setConfigurationSourcePath(String) 223 * @see #setPassword(String) 224 * @see #setUsername(String) 225 * @see #setRepositoryName(String) 226 * @see #setName(String) 227 */ 228 public void setConfigurationSourceName( String sourceName ) { 229 if (this.configurationSourceName == sourceName || this.configurationSourceName != null 230 && this.configurationSourceName.equals(sourceName)) return; // unchanged 231 this.configurationSourceName = sourceName; 232 changeRepositoryConfig(); 233 } 234 235 /** 236 * Get the path in the source that will be subgraph below the <code>/dna:system</code> branch of the repository. 237 * <p> 238 * This is a required property. 239 * </p> 240 * 241 * @return the string array of projection rules, or null if the projection rules haven't yet been set or if the federated 242 * repository instance is to be found in JNDI 243 * @see #setConfigurationSourcePath(String) 244 */ 245 public String getConfigurationSourcePath() { 246 return configurationSourcePath; 247 } 248 249 /** 250 * Set the path in the source that will be subgraph below the <code>/dna:system</code> branch of the repository. 251 * <p> 252 * This is a required property. 253 * </p> 254 * 255 * @param pathInSourceToConfigurationRoot the path within the configuration source to the node that should be the root of the 256 * configuration information, or null if the path hasn't yet been set or if the federated repository instance is to be 257 * found in JNDI 258 * @see #setConfigurationSourcePath(String) 259 * @see #setConfigurationSourceName(String) 260 * @see #setPassword(String) 261 * @see #setUsername(String) 262 * @see #setRepositoryName(String) 263 * @see #setName(String) 264 */ 265 public void setConfigurationSourcePath( String pathInSourceToConfigurationRoot ) { 266 if (this.configurationSourcePath == pathInSourceToConfigurationRoot || this.configurationSourcePath != null 267 && this.configurationSourcePath.equals(pathInSourceToConfigurationRoot)) return; 268 String path = pathInSourceToConfigurationRoot != null ? pathInSourceToConfigurationRoot : DEFAULT_CONFIGURATION_SOURCE_PATH; 269 // Ensure one leading slash and one trailing slashes ... 270 this.configurationSourcePath = path = ("/" + path).replaceAll("^/+", "/").replaceAll("/+$", "") + "/"; 271 changeRepositoryConfig(); 272 } 273 274 /** 275 * Get the name of the security domain that should be used by JAAS to identify the application or security context. This 276 * should correspond to the JAAS login configuration located within the JAAS login configuration file. 277 * 278 * @return securityDomain 279 */ 280 public String getSecurityDomain() { 281 return securityDomain; 282 } 283 284 /** 285 * Set the name of the security domain that should be used by JAAS to identify the application or security context. This 286 * should correspond to the JAAS login configuration located within the JAAS login configuration file. 287 * 288 * @param securityDomain Sets securityDomain to the specified value. 289 */ 290 public void setSecurityDomain( String securityDomain ) { 291 if (this.securityDomain != null && this.securityDomain.equals(securityDomain)) return; // unchanged 292 this.securityDomain = securityDomain; 293 changeRepositoryConfig(); 294 } 295 296 /** 297 * Get the name of the federated repository. 298 * <p> 299 * This is a required property. 300 * </p> 301 * 302 * @return the name of the repository 303 * @see #setRepositoryName(String) 304 */ 305 public synchronized String getRepositoryName() { 306 return this.repositoryName; 307 } 308 309 /** 310 * Get the name of the federated repository. 311 * <p> 312 * This is a required property. 313 * </p> 314 * 315 * @param repositoryName the new name of the repository 316 * @throws IllegalArgumentException if the repository name is null, empty or blank 317 * @see #getRepositoryName() 318 * @see #setConfigurationSourceName(String) 319 * @see #setConfigurationSourcePath(String) 320 * @see #setPassword(String) 321 * @see #setUsername(String) 322 * @see #setName(String) 323 */ 324 public synchronized void setRepositoryName( String repositoryName ) { 325 CheckArg.isNotEmpty(repositoryName, "repositoryName"); 326 if (this.repositoryName != null && this.repositoryName.equals(repositoryName)) return; // unchanged 327 this.repositoryName = repositoryName; 328 changeRepositoryConfig(); 329 } 330 331 /** 332 * Get the username that should be used when authenticating and {@link #getConnection() creating connections}. 333 * <p> 334 * This is an optional property, required only when authentication is to be used. 335 * </p> 336 * 337 * @return the username, or null if no username has been set or are not to be used 338 * @see #setUsername(String) 339 */ 340 public String getUsername() { 341 return this.username; 342 } 343 344 /** 345 * Set the username that should be used when authenticating and {@link #getConnection() creating connections}. 346 * <p> 347 * This is an optional property, required only when authentication is to be used. 348 * </p> 349 * 350 * @param username the username, or null if no username has been set or are not to be used 351 * @see #getUsername() 352 * @see #setPassword(String) 353 * @see #setConfigurationSourceName(String) 354 * @see #setConfigurationSourcePath(String) 355 * @see #setPassword(String) 356 * @see #setRepositoryName(String) 357 * @see #setName(String) 358 */ 359 public void setUsername( String username ) { 360 if (this.username != null && this.username.equals(username)) return; // unchanged 361 this.username = username; 362 changeRepositoryConfig(); 363 } 364 365 /** 366 * Get the password that should be used when authenticating and {@link #getConnection() creating connections}. 367 * <p> 368 * This is an optional property, required only when authentication is to be used. 369 * </p> 370 * 371 * @return the password, or null if no password have been set or are not to be used 372 * @see #setPassword(String) 373 */ 374 public String getPassword() { 375 return this.password; 376 } 377 378 /** 379 * Get the password that should be used when authenticating and {@link #getConnection() creating connections}. 380 * <p> 381 * This is an optional property, required only when authentication is to be used. 382 * </p> 383 * 384 * @param password the password, or null if no password have been set or are not to be used 385 * @see #getPassword() 386 * @see #setConfigurationSourceName(String) 387 * @see #setConfigurationSourcePath(String) 388 * @see #setUsername(String) 389 * @see #setRepositoryName(String) 390 * @see #setName(String) 391 */ 392 public void setPassword( String password ) { 393 if (this.password != null && this.password.equals(password)) return; // unchanged 394 this.password = password; 395 changeRepositoryConfig(); 396 } 397 398 /** 399 * This method is called to signal that some aspect of the configuration has changed. If a {@link #getRepository() repository} 400 * instance has been created, it's configuration is 401 * {@link #getRepositoryConfiguration(ExecutionContext, RepositoryConnectionFactory) rebuilt} and updated. Nothing is done, 402 * however, if there is currently no {@link #getRepository() repository}. 403 */ 404 protected synchronized void changeRepositoryConfig() { 405 if (this.repository != null) { 406 RepositoryContext repositoryContext = getRepositoryContext(); 407 if (repositoryContext != null) { 408 // Find in JNDI the repository source registry and the environment ... 409 ExecutionContext context = getExecutionContext(); 410 RepositoryConnectionFactory factory = getRepositoryContext().getRepositoryConnectionFactory(); 411 // Compute a new repository config and set it on the repository ... 412 FederatedRepositoryConfig newConfig = getRepositoryConfiguration(context, factory); 413 this.repository.setConfiguration(newConfig); 414 } 415 } 416 } 417 418 /** 419 * {@inheritDoc} 420 * 421 * @see org.jboss.dna.graph.connectors.RepositorySource#getConnection() 422 */ 423 public RepositoryConnection getConnection() throws RepositorySourceException { 424 if (getName() == null) { 425 I18n msg = FederationI18n.propertyIsRequired; 426 throw new RepositorySourceException(getName(), msg.text("name")); 427 } 428 if (getRepositoryContext() == null) { 429 I18n msg = FederationI18n.propertyIsRequired; 430 throw new RepositorySourceException(getName(), msg.text("repository context")); 431 } 432 if (getUsername() != null && getSecurityDomain() == null) { 433 I18n msg = FederationI18n.propertyIsRequired; 434 throw new RepositorySourceException(getName(), msg.text("security domain")); 435 } 436 // Find the repository ... 437 FederatedRepository repository = getRepository(); 438 // Authenticate the user ... 439 String username = this.username; 440 Object credentials = this.password; 441 RepositoryConnection connection = repository.createConnection(this, username, credentials); 442 if (connection == null) { 443 I18n msg = FederationI18n.unableToAuthenticateConnectionToFederatedRepository; 444 throw new RepositorySourceException(msg.text(this.repositoryName, username)); 445 } 446 // Return the new connection ... 447 return connection; 448 } 449 450 /** 451 * Get the {@link FederatedRepository} instance that this source is using. This method uses the following logic: 452 * <ol> 453 * <li>If a {@link FederatedRepository} already was obtained from a prior call, the same instance is returned.</li> 454 * <li>A {@link FederatedRepository} is created using a {@link FederatedRepositoryConfig} is created from this instance's 455 * properties and {@link ExecutionContext} and {@link RepositoryConnectionFactory} instances obtained from JNDI.</li> 456 * <li></li> 457 * <li></li> 458 * </ol> 459 * 460 * @return the federated repository instance 461 * @throws RepositorySourceException 462 */ 463 protected synchronized FederatedRepository getRepository() throws RepositorySourceException { 464 if (repository == null) { 465 ExecutionContext context = getExecutionContext(); 466 RepositoryConnectionFactory connectionFactory = getRepositoryContext().getRepositoryConnectionFactory(); 467 // And create the configuration and the repository ... 468 FederatedRepositoryConfig config = getRepositoryConfiguration(context, connectionFactory); 469 repository = new FederatedRepository(context, connectionFactory, config); 470 } 471 return repository; 472 } 473 474 protected ExecutionContext getExecutionContext() { 475 ExecutionContextFactory factory = getRepositoryContext().getExecutionContextFactory(); 476 CallbackHandler handler = createCallbackHandler(); 477 try { 478 String securityDomain = getSecurityDomain(); 479 if (securityDomain != null || getUsername() != null) { 480 return factory.create(securityDomain, handler); 481 } 482 return factory.create(); 483 } catch (LoginException e) { 484 I18n msg = FederationI18n.unableToCreateExecutionContext; 485 throw new RepositorySourceException(getName(), msg.text(this.sourceName, securityDomain), e); 486 } 487 } 488 489 protected CallbackHandler createCallbackHandler() { 490 return new CallbackHandler() { 491 public void handle( Callback[] callbacks ) { 492 for (Callback callback : callbacks) { 493 if (callback instanceof NameCallback) { 494 NameCallback nameCallback = (NameCallback)callback; 495 nameCallback.setName(FederatedRepositorySource.this.getUsername()); 496 } 497 if (callback instanceof PasswordCallback) { 498 PasswordCallback passwordCallback = (PasswordCallback)callback; 499 passwordCallback.setPassword(FederatedRepositorySource.this.getPassword().toCharArray()); 500 } 501 } 502 } 503 }; 504 } 505 506 /** 507 * Create a {@link FederatedRepositoryConfig} instance from the current properties of this instance. This method does 508 * <i>not</i> modify the state of this instance. 509 * 510 * @param context the execution context that should be used to read the configuration; may not be null 511 * @param connectionFactory the factory for {@link RepositoryConnection}s can be obtained; may not be null 512 * @return a configuration reflecting the current state of this instance 513 */ 514 protected synchronized FederatedRepositoryConfig getRepositoryConfiguration( ExecutionContext context, 515 RepositoryConnectionFactory connectionFactory ) { 516 Problems problems = new SimpleProblems(); 517 ValueFactories valueFactories = context.getValueFactories(); 518 NameFactory nameFactory = valueFactories.getNameFactory(); 519 ValueFactory<Long> longFactory = valueFactories.getLongFactory(); 520 ProjectionParser projectionParser = ProjectionParser.getInstance(); 521 522 // Create a graph to access the configuration ... 523 Graph config = Graph.create(configurationSourceName, connectionFactory, context); 524 525 // Read the federated repositories subgraph (of max depth 4)... 526 Subgraph repositories = config.getSubgraphOfDepth(4).at(getConfigurationSourcePath()); 527 528 // Set up the default cache policy by reading the "dna:federation" node ... 529 CachePolicy defaultCachePolicy = null; 530 Node federation = repositories.getNode(DNA_FEDERATION_SEGMENT); 531 if (federation == null) { 532 I18n msg = FederationI18n.requiredNodeDoesNotExistRelativeToNode; 533 throw new FederationException(msg.text(DNA_FEDERATION_SEGMENT, repositories.getLocation().getPath())); 534 } 535 Property timeToLiveProperty = federation.getProperty(nameFactory.create(CACHE_POLICY_TIME_TO_LIVE_CONFIG_PROPERTY_NAME)); 536 if (timeToLiveProperty != null && !timeToLiveProperty.isEmpty()) { 537 long timeToCacheInMillis = longFactory.create(timeToLiveProperty.getValues().next()); 538 BasicCachePolicy policy = new BasicCachePolicy(timeToCacheInMillis, TimeUnit.MILLISECONDS); 539 defaultCachePolicy = policy.getUnmodifiable(); 540 } 541 542 // Read the "dna:cache" and its projection ... 543 String cacheNodePath = DNA_FEDERATION_SEGMENT + "/" + DNA_CACHE_SEGMENT; 544 Node cacheNode = repositories.getNode(cacheNodePath); 545 if (cacheNode == null) { 546 I18n msg = FederationI18n.requiredNodeDoesNotExistRelativeToNode; 547 throw new FederationException(msg.text(cacheNodePath, repositories.getLocation().getPath())); 548 } 549 Projection cacheProjection = null; 550 for (Location cacheProjectionLocation : cacheNode) { 551 Node projection = repositories.getNode(cacheProjectionLocation); 552 cacheProjection = createProjection(context, projectionParser, projection, problems); 553 } 554 555 // Read the "dna:projections" and create a projection for each ... 556 String projectionsPath = DNA_FEDERATION_SEGMENT + "/" + DNA_PROJECTIONS_SEGMENT; 557 Node projectionsNode = repositories.getNode(projectionsPath); 558 if (projectionsNode == null) { 559 I18n msg = FederationI18n.requiredNodeDoesNotExistRelativeToNode; 560 throw new FederationException(msg.text(projectionsNode, repositories.getLocation().getPath())); 561 } 562 List<Projection> sourceProjections = new LinkedList<Projection>(); 563 for (Location location : projectionsNode) { 564 Node projection = repositories.getNode(location); 565 sourceProjections.add(createProjection(context, projectionParser, projection, problems)); 566 } 567 568 return new FederatedRepositoryConfig(repositoryName, cacheProjection, sourceProjections, defaultCachePolicy); 569 } 570 571 /** 572 * Instantiate the {@link Projection} described by the supplied properties. 573 * 574 * @param context the execution context that should be used to read the configuration; may not be null 575 * @param projectionParser the projection rule parser that should be used; may not be null 576 * @param node the node where these properties were found; never null 577 * @param problems the problems container in which any problems should be reported; never null 578 * @return the region instance, or null if it could not be created 579 */ 580 protected Projection createProjection( ExecutionContext context, 581 ProjectionParser projectionParser, 582 Node node, 583 Problems problems ) { 584 ValueFactories valueFactories = context.getValueFactories(); 585 NameFactory nameFactory = valueFactories.getNameFactory(); 586 ValueFactory<String> stringFactory = valueFactories.getStringFactory(); 587 588 Path path = node.getLocation().getPath(); 589 String sourceName = path.getLastSegment().getName().getLocalName(); 590 591 // Get the rules ... 592 Projection.Rule[] projectionRules = null; 593 Property projectionRulesProperty = node.getProperty(nameFactory.create(PROJECTION_RULES_CONFIG_PROPERTY_NAME)); 594 if (projectionRulesProperty != null && !projectionRulesProperty.isEmpty()) { 595 String[] projectionRuleStrs = stringFactory.create(projectionRulesProperty.getValuesAsArray()); 596 if (projectionRuleStrs != null && projectionRuleStrs.length != 0) { 597 projectionRules = projectionParser.rulesFromStrings(context, projectionRuleStrs); 598 } 599 } 600 if (problems.hasErrors()) return null; 601 602 Projection region = new Projection(sourceName, projectionRules); 603 return region; 604 } 605 606 /** 607 * {@inheritDoc} 608 */ 609 public synchronized Reference getReference() { 610 String className = getClass().getName(); 611 String factoryClassName = this.getClass().getName(); 612 Reference ref = new Reference(className, factoryClassName, null); 613 614 if (getRepositoryName() != null) { 615 ref.add(new StringRefAddr(REPOSITORY_NAME, getRepositoryName())); 616 } 617 if (getName() != null) { 618 ref.add(new StringRefAddr(SOURCE_NAME, getName())); 619 } 620 if (getUsername() != null) { 621 ref.add(new StringRefAddr(USERNAME, getUsername())); 622 } 623 if (getPassword() != null) { 624 ref.add(new StringRefAddr(PASSWORD, getPassword())); 625 } 626 if (getConfigurationSourceName() != null) { 627 ref.add(new StringRefAddr(CONFIGURATION_SOURCE_NAME, getConfigurationSourceName())); 628 } 629 if (getConfigurationSourcePath() != null) { 630 ref.add(new StringRefAddr(CONFIGURATION_SOURCE_PATH, getConfigurationSourcePath())); 631 } 632 if (getSecurityDomain() != null) { 633 ref.add(new StringRefAddr(SECURITY_DOMAIN, getSecurityDomain())); 634 } 635 ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit()))); 636 return ref; 637 } 638 639 /** 640 * {@inheritDoc} 641 */ 642 public Object getObjectInstance( Object obj, 643 javax.naming.Name name, 644 Context nameCtx, 645 Hashtable<?, ?> environment ) throws Exception { 646 if (obj instanceof Reference) { 647 Map<String, String> values = new HashMap<String, String>(); 648 Reference ref = (Reference)obj; 649 Enumeration<?> en = ref.getAll(); 650 while (en.hasMoreElements()) { 651 RefAddr subref = (RefAddr)en.nextElement(); 652 if (subref instanceof StringRefAddr) { 653 String key = subref.getType(); 654 Object value = subref.getContent(); 655 if (value != null) values.put(key, value.toString()); 656 } 657 } 658 String repositoryName = values.get(FederatedRepositorySource.REPOSITORY_NAME); 659 String sourceName = values.get(FederatedRepositorySource.SOURCE_NAME); 660 String username = values.get(FederatedRepositorySource.USERNAME); 661 String password = values.get(FederatedRepositorySource.PASSWORD); 662 String configurationSourceName = values.get(FederatedRepositorySource.CONFIGURATION_SOURCE_NAME); 663 String configurationSourcePath = values.get(FederatedRepositorySource.CONFIGURATION_SOURCE_PATH); 664 String securityDomain = values.get(FederatedRepositorySource.SECURITY_DOMAIN); 665 String retryLimit = values.get(FederatedRepositorySource.RETRY_LIMIT); 666 667 // Create the source instance ... 668 FederatedRepositorySource source = new FederatedRepositorySource(); 669 if (repositoryName != null) source.setRepositoryName(repositoryName); 670 if (sourceName != null) source.setName(sourceName); 671 if (username != null) source.setUsername(username); 672 if (password != null) source.setPassword(password); 673 if (configurationSourceName != null) source.setConfigurationSourceName(configurationSourceName); 674 if (configurationSourcePath != null) source.setConfigurationSourcePath(configurationSourcePath); 675 if (securityDomain != null) source.setSecurityDomain(securityDomain); 676 if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit)); 677 return source; 678 } 679 return null; 680 } 681 682 /** 683 * {@inheritDoc} 684 */ 685 @Override 686 public int hashCode() { 687 return repositoryName.hashCode(); 688 } 689 690 /** 691 * {@inheritDoc} 692 */ 693 @Override 694 public boolean equals( Object obj ) { 695 if (obj == this) return true; 696 if (obj instanceof FederatedRepositorySource) { 697 FederatedRepositorySource that = (FederatedRepositorySource)obj; 698 // The repository name, source name, and federation service must all match 699 if (!this.getRepositoryName().equals(that.getRepositoryName())) return false; 700 if (this.getName() == null) { 701 if (that.getName() != null) return false; 702 } else { 703 if (!this.getName().equals(that.getName())) return false; 704 } 705 return true; 706 } 707 return false; 708 } 709 710 /** 711 * {@inheritDoc} 712 * 713 * @see org.jboss.dna.graph.connectors.RepositorySource#getCapabilities() 714 */ 715 public RepositorySourceCapabilities getCapabilities() { 716 return CAPABILITIES; 717 } 718 }