You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RepositoryGroupHandler.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. package org.apache.archiva.repository.base;
  2. /*
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import org.apache.archiva.components.registry.RegistryException;
  20. import org.apache.archiva.configuration.Configuration;
  21. import org.apache.archiva.configuration.IndeterminateConfigurationException;
  22. import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
  23. import org.apache.archiva.configuration.RepositoryGroupConfiguration;
  24. import org.apache.archiva.indexer.merger.MergedRemoteIndexesScheduler;
  25. import org.apache.archiva.repository.CheckedResult;
  26. import org.apache.archiva.repository.EditableRepository;
  27. import org.apache.archiva.repository.EditableRepositoryGroup;
  28. import org.apache.archiva.repository.ManagedRepository;
  29. import org.apache.archiva.repository.RepositoryException;
  30. import org.apache.archiva.repository.RepositoryGroup;
  31. import org.apache.archiva.repository.RepositoryHandler;
  32. import org.apache.archiva.repository.RepositoryProvider;
  33. import org.apache.archiva.repository.RepositoryType;
  34. import org.apache.archiva.repository.event.RepositoryEvent;
  35. import org.apache.archiva.repository.features.IndexCreationFeature;
  36. import org.apache.archiva.repository.storage.StorageAsset;
  37. import org.apache.archiva.repository.validation.RepositoryChecker;
  38. import org.apache.archiva.repository.validation.RepositoryValidator;
  39. import org.apache.commons.lang3.StringUtils;
  40. import org.slf4j.Logger;
  41. import org.slf4j.LoggerFactory;
  42. import org.springframework.stereotype.Service;
  43. import javax.annotation.PostConstruct;
  44. import javax.annotation.PreDestroy;
  45. import javax.inject.Named;
  46. import java.io.IOException;
  47. import java.nio.file.Files;
  48. import java.nio.file.Path;
  49. import java.util.Collection;
  50. import java.util.Collections;
  51. import java.util.HashMap;
  52. import java.util.LinkedHashMap;
  53. import java.util.List;
  54. import java.util.Map;
  55. import java.util.concurrent.locks.ReentrantReadWriteLock;
  56. import java.util.stream.Collectors;
  57. import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
  58. /**
  59. * This class manages repository groups for the RepositoryRegistry.
  60. * It is tightly coupled with the {@link ArchivaRepositoryRegistry}.
  61. *
  62. * @author Martin Stockhammer <martin_s@apache.org>
  63. */
  64. @Service( "repositoryGroupHandler#default" )
  65. public class RepositoryGroupHandler implements RepositoryHandler<RepositoryGroup, RepositoryGroupConfiguration>
  66. {
  67. private static final Logger log = LoggerFactory.getLogger( RepositoryGroupHandler.class );
  68. private final ArchivaRepositoryRegistry repositoryRegistry;
  69. private final ConfigurationHandler configurationHandler;
  70. private final MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler;
  71. private final Map<String, RepositoryGroup> repositoryGroups = new HashMap<>( );
  72. private final RepositoryValidator<RepositoryGroup> validator;
  73. private Path groupsDirectory;
  74. /**
  75. * Creates a new instance. All dependencies are injected on the constructor.
  76. *
  77. * @param repositoryRegistry the registry. To avoid circular dependencies via DI, this class registers itself on the registry.
  78. * @param configurationHandler the configuration handler is used to retrieve and save configuration.
  79. * @param mergedRemoteIndexesScheduler the index scheduler is used for merging the indexes from all group members
  80. */
  81. public RepositoryGroupHandler( ArchivaRepositoryRegistry repositoryRegistry,
  82. ConfigurationHandler configurationHandler,
  83. @Named( "mergedRemoteIndexesScheduler#default" ) MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler,
  84. @Named( "repositoryValidator#common#group") RepositoryValidator<RepositoryGroup> repositoryGroupValidator
  85. )
  86. {
  87. this.configurationHandler = configurationHandler;
  88. this.mergedRemoteIndexesScheduler = mergedRemoteIndexesScheduler;
  89. this.repositoryRegistry = repositoryRegistry;
  90. this.validator = repositoryGroupValidator;
  91. }
  92. @Override
  93. @PostConstruct
  94. public void init( )
  95. {
  96. log.debug( "Initializing repository group handler " + repositoryRegistry.toString( ) );
  97. initializeStorage( );
  98. // We are registering this class on the registry. This is necessary to avoid circular dependencies via injection.
  99. this.repositoryRegistry.registerGroupHandler( this );
  100. }
  101. public void initializeFromConfig( )
  102. {
  103. this.repositoryGroups.clear( );
  104. this.repositoryGroups.putAll( newInstancesFromConfig( ) );
  105. for ( RepositoryGroup group : this.repositoryGroups.values( ) )
  106. {
  107. initializeGroup( group );
  108. }
  109. }
  110. private void initializeStorage( )
  111. {
  112. Path baseDir = this.configurationHandler.getArchivaConfiguration( ).getRepositoryGroupBaseDir( );
  113. if ( !Files.exists( baseDir ) )
  114. {
  115. try
  116. {
  117. Files.createDirectories( baseDir );
  118. }
  119. catch ( IOException e )
  120. {
  121. log.error( "Could not create group base directory: {}", e.getMessage( ), e );
  122. }
  123. }
  124. this.groupsDirectory = baseDir;
  125. }
  126. private void initializeGroup( RepositoryGroup repositoryGroup )
  127. {
  128. StorageAsset indexDirectory = getMergedIndexDirectory( repositoryGroup );
  129. if ( !indexDirectory.exists( ) )
  130. {
  131. try
  132. {
  133. indexDirectory.create( );
  134. }
  135. catch ( IOException e )
  136. {
  137. log.error( "Could not create index directory {} for group {}: {}", indexDirectory, repositoryGroup.getId( ), e.getMessage( ) );
  138. }
  139. }
  140. Path groupPath = groupsDirectory.resolve( repositoryGroup.getId( ) );
  141. if ( !Files.exists( groupPath ) )
  142. {
  143. try
  144. {
  145. Files.createDirectories( groupPath );
  146. }
  147. catch ( IOException e )
  148. {
  149. log.error( "Could not create repository group directory {}", groupPath );
  150. }
  151. }
  152. mergedRemoteIndexesScheduler.schedule( repositoryGroup,
  153. indexDirectory );
  154. }
  155. public StorageAsset getMergedIndexDirectory( RepositoryGroup group )
  156. {
  157. if ( group != null )
  158. {
  159. return group.getFeature( IndexCreationFeature.class ).get( ).getLocalIndexPath( );
  160. }
  161. else
  162. {
  163. return null;
  164. }
  165. }
  166. @Override
  167. public Map<String, RepositoryGroup> newInstancesFromConfig( )
  168. {
  169. try
  170. {
  171. List<RepositoryGroupConfiguration> repositoryGroupConfigurations =
  172. this.configurationHandler.getBaseConfiguration( ).getRepositoryGroups( );
  173. if ( repositoryGroupConfigurations == null )
  174. {
  175. return Collections.emptyMap( );
  176. }
  177. Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>( repositoryGroupConfigurations.size( ) );
  178. Map<RepositoryType, RepositoryProvider> providerMap = repositoryRegistry.getRepositoryProviderMap( );
  179. for ( RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations )
  180. {
  181. RepositoryType repositoryType = RepositoryType.valueOf( repoConfig.getType( ) );
  182. if ( providerMap.containsKey( repositoryType ) )
  183. {
  184. try
  185. {
  186. RepositoryGroup repo = createNewRepositoryGroup( providerMap.get( repositoryType ), repoConfig );
  187. repositoryGroupMap.put( repo.getId( ), repo );
  188. }
  189. catch ( Exception e )
  190. {
  191. log.error( "Could not create repository group {}: {}", repoConfig.getId( ), e.getMessage( ), e );
  192. }
  193. }
  194. }
  195. return repositoryGroupMap;
  196. }
  197. catch ( Throwable e )
  198. {
  199. log.error( "Could not initialize repositories from config: {}", e.getMessage( ), e );
  200. return Collections.emptyMap( );
  201. }
  202. }
  203. @Override
  204. public RepositoryGroup newInstance( final RepositoryType type, String id ) throws RepositoryException
  205. {
  206. RepositoryProvider provider = repositoryRegistry.getProvider( type );
  207. RepositoryGroupConfiguration config = new RepositoryGroupConfiguration( );
  208. config.setId( id );
  209. return createNewRepositoryGroup( provider, config );
  210. }
  211. @Override
  212. public RepositoryGroup newInstance( final RepositoryGroupConfiguration repositoryConfiguration ) throws RepositoryException
  213. {
  214. RepositoryType type = RepositoryType.valueOf( repositoryConfiguration.getType( ) );
  215. RepositoryProvider provider = repositoryRegistry.getProvider( type );
  216. return createNewRepositoryGroup( provider, repositoryConfiguration );
  217. }
  218. private RepositoryGroup createNewRepositoryGroup( RepositoryProvider provider, RepositoryGroupConfiguration config ) throws RepositoryException
  219. {
  220. RepositoryGroup repositoryGroup = provider.createRepositoryGroup( config );
  221. updateReferences( repositoryGroup, config );
  222. return repositoryGroup;
  223. }
  224. /**
  225. * Adds a new repository group to the current list, or replaces the repository group definition with
  226. * the same id, if it exists already.
  227. * The change is saved to the configuration immediately.
  228. *
  229. * @param repositoryGroup the new repository group.
  230. * @throws RepositoryException if the new repository group could not be saved to the configuration.
  231. */
  232. @Override
  233. public RepositoryGroup put( final RepositoryGroup repositoryGroup ) throws RepositoryException
  234. {
  235. final String id = repositoryGroup.getId( );
  236. RepositoryGroup originRepoGroup = repositoryGroups.remove( id );
  237. try
  238. {
  239. if ( originRepoGroup != null && originRepoGroup != repositoryGroup )
  240. {
  241. this.mergedRemoteIndexesScheduler.unschedule( originRepoGroup );
  242. originRepoGroup.close( );
  243. }
  244. RepositoryProvider provider = repositoryRegistry.getProvider( repositoryGroup.getType( ) );
  245. RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration( repositoryGroup );
  246. ReentrantReadWriteLock.WriteLock configLock = this.configurationHandler.getLock( ).writeLock( );
  247. configLock.lock( );
  248. try
  249. {
  250. Configuration configuration = this.configurationHandler.getBaseConfiguration( );
  251. updateReferences( repositoryGroup, newCfg );
  252. RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( id );
  253. if ( oldCfg != null )
  254. {
  255. configuration.removeRepositoryGroup( oldCfg );
  256. }
  257. configuration.addRepositoryGroup( newCfg );
  258. configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
  259. initializeGroup( repositoryGroup );
  260. }
  261. finally
  262. {
  263. configLock.unlock( );
  264. }
  265. repositoryGroups.put( id, repositoryGroup );
  266. return repositoryGroup;
  267. }
  268. catch ( Exception e )
  269. {
  270. // Rollback
  271. if ( originRepoGroup != null )
  272. {
  273. repositoryGroups.put( id, originRepoGroup );
  274. }
  275. else
  276. {
  277. repositoryGroups.remove( id );
  278. }
  279. log.error( "Exception during configuration update {}", e.getMessage( ), e );
  280. throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ), e);
  281. }
  282. }
  283. /**
  284. * Adds a new repository group or updates the repository with the same id, if it exists already.
  285. * The configuration is saved immediately.
  286. *
  287. * @param repositoryGroupConfiguration the repository configuration
  288. * @return the updated or created repository
  289. * @throws RepositoryException if an error occurs, or the configuration is not valid.
  290. */
  291. @Override
  292. public RepositoryGroup put( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException
  293. {
  294. final String id = repositoryGroupConfiguration.getId( );
  295. final RepositoryType repositoryType = RepositoryType.valueOf( repositoryGroupConfiguration.getType( ) );
  296. final RepositoryProvider provider = repositoryRegistry.getProvider( repositoryType );
  297. RepositoryGroup currentRepository;
  298. ReentrantReadWriteLock.WriteLock configLock = this.configurationHandler.getLock( ).writeLock( );
  299. configLock.lock( );
  300. try
  301. {
  302. Configuration configuration = this.configurationHandler.getBaseConfiguration( );
  303. currentRepository = repositoryRegistry.getRepositoryGroup( id );
  304. RepositoryGroup oldRepository = currentRepository == null ? null : clone( currentRepository );
  305. try
  306. {
  307. if (currentRepository==null) {
  308. currentRepository = put( repositoryGroupConfiguration, configuration );
  309. } else
  310. {
  311. setRepositoryGroupDefaults( repositoryGroupConfiguration );
  312. provider.updateRepositoryGroupInstance( (EditableRepositoryGroup) currentRepository, repositoryGroupConfiguration );
  313. }
  314. configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
  315. updateReferences( currentRepository, repositoryGroupConfiguration );
  316. initializeGroup( currentRepository );
  317. this.repositoryGroups.put( id, currentRepository );
  318. }
  319. catch ( IndeterminateConfigurationException | RegistryException | RepositoryException e )
  320. {
  321. // Trying a rollback
  322. if ( oldRepository != null )
  323. {
  324. RepositoryGroupConfiguration oldCfg = provider.getRepositoryGroupConfiguration( oldRepository );
  325. provider.updateRepositoryGroupInstance( (EditableRepositoryGroup) currentRepository, oldCfg);
  326. replaceOrAddRepositoryConfig( oldCfg, configuration );
  327. try
  328. {
  329. configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
  330. }
  331. catch ( IndeterminateConfigurationException | RegistryException indeterminateConfigurationException )
  332. {
  333. log.error( "Fatal error, config save during rollback failed: {}", e.getMessage( ), e );
  334. }
  335. updateReferences( oldRepository, oldCfg );
  336. initializeGroup( oldRepository );
  337. }
  338. log.error( "Could not save the configuration for repository group {}: {}", id, e.getMessage( ), e );
  339. if (e instanceof RepositoryException) {
  340. throw (RepositoryException) e;
  341. } else
  342. {
  343. throw new RepositoryException( "Could not save the configuration for repository group " + id + ": " + e.getMessage( ) );
  344. }
  345. }
  346. }
  347. finally
  348. {
  349. configLock.unlock( );
  350. }
  351. return currentRepository;
  352. }
  353. @Override
  354. public RepositoryGroup put( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException
  355. {
  356. final String id = repositoryGroupConfiguration.getId( );
  357. final RepositoryType repoType = RepositoryType.valueOf( repositoryGroupConfiguration.getType( ) );
  358. RepositoryGroup repo;
  359. setRepositoryGroupDefaults( repositoryGroupConfiguration );
  360. if ( repositoryGroups.containsKey( id ) )
  361. {
  362. repo = clone( repositoryGroups.get( id ) );
  363. if ( repo instanceof EditableRepositoryGroup )
  364. {
  365. repositoryRegistry.getProvider( repoType ).updateRepositoryGroupInstance( (EditableRepositoryGroup) repo, repositoryGroupConfiguration );
  366. }
  367. else
  368. {
  369. throw new RepositoryException( "The repository is not editable " + id );
  370. }
  371. }
  372. else
  373. {
  374. repo = repositoryRegistry.getProvider( repoType ).createRepositoryGroup( repositoryGroupConfiguration );
  375. }
  376. replaceOrAddRepositoryConfig( repositoryGroupConfiguration, configuration );
  377. updateReferences( repo, repositoryGroupConfiguration );
  378. return repo;
  379. }
  380. @Override
  381. public <D> CheckedResult<RepositoryGroup, D> putWithCheck( RepositoryGroupConfiguration repositoryConfiguration, RepositoryChecker<RepositoryGroup, D> checker ) throws RepositoryException
  382. {
  383. final String id = repositoryConfiguration.getId( );
  384. RepositoryGroup currentGroup = repositoryGroups.get( id );
  385. Configuration configuration = configurationHandler.getBaseConfiguration( );
  386. RepositoryGroup repositoryGroup = put( repositoryConfiguration, configuration );
  387. CheckedResult<RepositoryGroup, D> result;
  388. if ( currentGroup == null )
  389. {
  390. result = checker.apply( repositoryGroup );
  391. }
  392. else
  393. {
  394. result = checker.applyForUpdate( repositoryGroup );
  395. }
  396. if ( result.isValid( ) )
  397. {
  398. put( result.getRepository() );
  399. }
  400. return result;
  401. }
  402. private void setRepositoryGroupDefaults( RepositoryGroupConfiguration repositoryGroupConfiguration )
  403. {
  404. if ( StringUtils.isEmpty( repositoryGroupConfiguration.getMergedIndexPath( ) ) )
  405. {
  406. repositoryGroupConfiguration.setMergedIndexPath( DEFAULT_INDEX_PATH );
  407. }
  408. if ( repositoryGroupConfiguration.getMergedIndexTtl( ) <= 0 )
  409. {
  410. repositoryGroupConfiguration.setMergedIndexTtl( 300 );
  411. }
  412. if ( StringUtils.isEmpty( repositoryGroupConfiguration.getCronExpression( ) ) )
  413. {
  414. repositoryGroupConfiguration.setCronExpression( "0 0 03 ? * MON" );
  415. }
  416. }
  417. private void replaceOrAddRepositoryConfig( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration )
  418. {
  419. RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( repositoryGroupConfiguration.getId( ) );
  420. if ( oldCfg != null )
  421. {
  422. configuration.removeRepositoryGroup( oldCfg );
  423. }
  424. configuration.addRepositoryGroup( repositoryGroupConfiguration );
  425. }
  426. public void removeRepositoryFromGroups( ManagedRepository repo )
  427. {
  428. if ( repo != null )
  429. {
  430. repositoryGroups.values( ).stream( ).filter( repoGroup -> repoGroup instanceof EditableRepository ).
  431. map( repoGroup -> (EditableRepositoryGroup) repoGroup ).forEach( repoGroup -> repoGroup.removeRepository( repo ) );
  432. }
  433. }
  434. /**
  435. * Removes a repository group from the registry and configuration, if it exists.
  436. * The change is saved to the configuration immediately.
  437. *
  438. * @param id the id of the repository group to remove
  439. * @throws RepositoryException if a error occurs during configuration save
  440. */
  441. @Override
  442. public void remove( final String id ) throws RepositoryException
  443. {
  444. RepositoryGroup repo = get( id );
  445. if ( repo != null )
  446. {
  447. try
  448. {
  449. repo = repositoryGroups.remove( id );
  450. if ( repo != null )
  451. {
  452. this.mergedRemoteIndexesScheduler.unschedule( repo );
  453. repo.close( );
  454. Configuration configuration = this.configurationHandler.getBaseConfiguration( );
  455. RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById( id );
  456. if ( cfg != null )
  457. {
  458. configuration.removeRepositoryGroup( cfg );
  459. }
  460. this.configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
  461. }
  462. }
  463. catch ( RegistryException | IndeterminateConfigurationException e )
  464. {
  465. // Rollback
  466. log.error( "Could not save config after repository removal: {}", e.getMessage( ), e );
  467. repositoryGroups.put( repo.getId( ), repo );
  468. throw new RepositoryException( "Could not save configuration after repository removal: " + e.getMessage( ) );
  469. }
  470. }
  471. }
  472. @Override
  473. public void remove( String id, Configuration configuration ) throws RepositoryException
  474. {
  475. RepositoryGroup repo = repositoryGroups.get( id );
  476. if ( repo != null )
  477. {
  478. repo = repositoryGroups.remove( id );
  479. if ( repo != null )
  480. {
  481. this.mergedRemoteIndexesScheduler.unschedule( repo );
  482. repo.close( );
  483. RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById( id );
  484. if ( cfg != null )
  485. {
  486. configuration.removeRepositoryGroup( cfg );
  487. }
  488. }
  489. }
  490. }
  491. @Override
  492. public RepositoryGroup get( String groupId )
  493. {
  494. return repositoryGroups.get( groupId );
  495. }
  496. @Override
  497. public RepositoryGroup clone( RepositoryGroup repo ) throws RepositoryException
  498. {
  499. RepositoryProvider provider = repositoryRegistry.getProvider( repo.getType( ) );
  500. RepositoryGroupConfiguration cfg = provider.getRepositoryGroupConfiguration( repo );
  501. RepositoryGroup cloned = provider.createRepositoryGroup( cfg );
  502. cloned.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry );
  503. return cloned;
  504. }
  505. @Override
  506. public void updateReferences( RepositoryGroup repo, RepositoryGroupConfiguration repositoryConfiguration ) throws RepositoryException
  507. {
  508. if ( repo instanceof EditableRepositoryGroup )
  509. {
  510. EditableRepositoryGroup eGroup = (EditableRepositoryGroup) repo;
  511. eGroup.setRepositories( repositoryConfiguration.getRepositories( ).stream( )
  512. .map( repositoryRegistry::getManagedRepository ).collect( Collectors.toList( ) ) );
  513. }
  514. }
  515. @Override
  516. public Collection<RepositoryGroup> getAll( )
  517. {
  518. return repositoryGroups.values( );
  519. }
  520. @Override
  521. public RepositoryValidator<RepositoryGroup> getValidator( )
  522. {
  523. return this.validator;
  524. }
  525. @Override
  526. public boolean has( String id )
  527. {
  528. return repositoryGroups.containsKey( id );
  529. }
  530. @PreDestroy
  531. private void destroy( )
  532. {
  533. this.close( );
  534. }
  535. @Override
  536. public void close( )
  537. {
  538. for ( RepositoryGroup group : repositoryGroups.values( ) )
  539. {
  540. try
  541. {
  542. mergedRemoteIndexesScheduler.unschedule( group );
  543. group.close( );
  544. }
  545. catch ( Throwable e )
  546. {
  547. log.error( "Could not close repository group {}: {}", group.getId( ), e.getMessage( ) );
  548. }
  549. }
  550. this.repositoryGroups.clear( );
  551. }
  552. }