]> source.dussan.org Git - archiva.git/blob
d30304860cc8c1bfdb971148b662a740d0959c6f
[archiva.git] /
1 package org.apache.archiva.metadata.repository.jcr;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableSet;
24 import org.apache.commons.lang.time.StopWatch;
25 import org.apache.jackrabbit.oak.api.Type;
26 import org.apache.jackrabbit.oak.jcr.Jcr;
27 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
28 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoServiceImpl;
29 import org.apache.jackrabbit.oak.plugins.index.IndexInfoProvider;
30 import org.apache.jackrabbit.oak.plugins.index.IndexPathService;
31 import org.apache.jackrabbit.oak.plugins.index.IndexPathServiceImpl;
32 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
33 import org.apache.jackrabbit.oak.plugins.index.aggregate.SimpleNodeAggregator;
34 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexAugmentorFactory;
35 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexCopier;
36 import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
37 import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
38 import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexInfoProvider;
39 import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProvider;
40 import org.apache.jackrabbit.oak.plugins.index.lucene.directory.ActiveDeletedBlobCollectorFactory;
41 import org.apache.jackrabbit.oak.plugins.index.lucene.directory.BufferedOakDirectory;
42 import org.apache.jackrabbit.oak.plugins.index.lucene.directory.LuceneIndexImporter;
43 import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.DocumentQueue;
44 import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.ExternalObserverBuilder;
45 import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.LocalIndexObserver;
46 import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.NRTIndexFactory;
47 import org.apache.jackrabbit.oak.plugins.index.lucene.property.PropertyIndexCleaner;
48 import org.apache.jackrabbit.oak.plugins.index.lucene.reader.DefaultIndexReaderFactory;
49 import org.apache.jackrabbit.oak.plugins.index.lucene.score.ScorerProviderFactory;
50 import org.apache.jackrabbit.oak.plugins.index.lucene.score.impl.ScorerProviderFactoryImpl;
51 import org.apache.jackrabbit.oak.plugins.index.lucene.util.IndexDefinitionBuilder;
52 import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
53 import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
54 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
55 import org.apache.jackrabbit.oak.plugins.name.Namespaces;
56 import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders;
57 import org.apache.jackrabbit.oak.segment.file.FileStore;
58 import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder;
59 import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
60 import org.apache.jackrabbit.oak.spi.blob.FileBlobStore;
61 import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
62 import org.apache.jackrabbit.oak.spi.commit.BackgroundObserver;
63 import org.apache.jackrabbit.oak.spi.commit.Observer;
64 import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
65 import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
66 import org.apache.jackrabbit.oak.spi.mount.Mounts;
67 import org.apache.jackrabbit.oak.spi.namespace.NamespaceConstants;
68 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
69 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
70 import org.apache.jackrabbit.oak.spi.state.Clusterable;
71 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
72 import org.apache.jackrabbit.oak.spi.state.NodeStore;
73 import org.apache.jackrabbit.oak.stats.StatisticsProvider;
74 import org.jetbrains.annotations.NotNull;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 import javax.annotation.Nonnull;
79 import javax.jcr.Repository;
80 import java.io.Closeable;
81 import java.io.IOException;
82 import java.nio.file.Files;
83 import java.nio.file.Path;
84 import java.nio.file.Paths;
85 import java.util.concurrent.ExecutorService;
86 import java.util.concurrent.LinkedBlockingQueue;
87 import java.util.concurrent.ThreadFactory;
88 import java.util.concurrent.ThreadPoolExecutor;
89 import java.util.concurrent.TimeUnit;
90 import java.util.concurrent.atomic.AtomicInteger;
91
92 import static com.google.common.base.Preconditions.checkNotNull;
93 import static org.apache.archiva.metadata.repository.jcr.OakRepositoryFactory.StoreType.IN_MEMORY_TYPE;
94 import static org.apache.archiva.metadata.repository.jcr.OakRepositoryFactory.StoreType.SEGMENT_FILE_TYPE;
95 import static org.apache.commons.io.FileUtils.ONE_MB;
96 import static org.apache.jackrabbit.JcrConstants.*;
97 import static org.apache.jackrabbit.oak.api.Type.NAME;
98 import static org.apache.archiva.metadata.repository.jcr.JcrConstants.*;
99 import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
100 import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.scheduleWithFixedDelay;
101
102 /**
103  * Created by martin on 14.06.17.
104  *
105  * @author Martin Stockhammer
106  * @since 3.0.0
107  */
108 public class OakRepositoryFactory
109 {
110
111     private Logger log = LoggerFactory.getLogger( OakRepositoryFactory.class );
112
113     private FileStore fileStore;
114
115     private NodeStore nodeStore;
116
117     private IndexTracker tracker;
118
119     private DocumentQueue documentQueue;
120
121     private NRTIndexFactory nrtIndexFactory;
122
123     private IndexCopier indexCopier;
124
125     private ExecutorService executorService;
126     private ExtractedTextCache extractedTextCache;
127
128     private boolean hybridIndex = true;
129     private boolean prefetchEnabled = true;
130     private boolean enableAsyncIndexOpen = true;
131     int queueSize = 10000;
132     int cleanerInterval = 10*60;
133     boolean enableCopyOnWrite = true;
134     boolean enableCopyOnRead = true;
135     int cacheSizeInMB = 20;
136     int cacheExpiryInSecs = 300;
137     int threadPoolSize = 5;
138
139     private StatisticsProvider statisticsProvider;
140
141     private MountInfoProvider mountInfoProvider =  Mounts.defaultMountInfoProvider();
142
143     private AsyncIndexInfoService asyncIndexInfoService = null;
144
145     private LuceneIndexProvider indexProvider;
146
147     private ScorerProviderFactory scorerFactory = new ScorerProviderFactoryImpl( );
148     private IndexAugmentorFactory augmentorFactory = new IndexAugmentorFactory( );
149
150     private ActiveDeletedBlobCollectorFactory.ActiveDeletedBlobCollector activeDeletedBlobCollector = ActiveDeletedBlobCollectorFactory.NOOP;
151
152     private QueryIndex.NodeAggregator nodeAggregator = new SimpleNodeAggregator( );
153
154     private BackgroundObserver backgroundObserver;
155
156     private BackgroundObserver externalIndexObserver;
157
158     private GarbageCollectableBlobStore blobStore;
159
160     private PropertyIndexCleaner cleaner;
161
162     private IndexPathService indexPathService;
163
164     private LuceneIndexEditorProvider editorProvider;
165
166     private Path indexDir;
167
168     public enum StoreType
169     {
170         SEGMENT_FILE_TYPE,
171         IN_MEMORY_TYPE;
172     }
173
174     private StoreType storeType = SEGMENT_FILE_TYPE;
175
176     private Path repositoryPath = Paths.get( "repository" );
177
178     public OakRepositoryFactory() {
179         final OakRepositoryFactory repositoryFactory = this;
180         Runtime.getRuntime().addShutdownHook( new Thread( ( ) -> {
181             if (repositoryFactory!=null)
182             {
183                 repositoryFactory.close( );
184             }
185         } ) );
186     }
187
188     private void initializeExtractedTextCache( StatisticsProvider statisticsProvider) {
189         boolean alwaysUsePreExtractedCache = false;
190
191         extractedTextCache = new ExtractedTextCache(
192             cacheSizeInMB * ONE_MB,
193             cacheExpiryInSecs,
194             alwaysUsePreExtractedCache,
195             indexDir.toFile(), statisticsProvider);
196     }
197
198     private IndexTracker createTracker() throws IOException {
199         IndexTracker tracker;
200         if (enableCopyOnRead){
201             initializeIndexCopier();
202             log.info("Enabling CopyOnRead support. Index files would be copied under {}", indexDir.toAbsolutePath());
203             if (hybridIndex) {
204                 nrtIndexFactory = new NRTIndexFactory(indexCopier, statisticsProvider);
205             }
206             tracker = new IndexTracker(new DefaultIndexReaderFactory(mountInfoProvider, indexCopier), nrtIndexFactory);
207         } else {
208             tracker = new IndexTracker(new DefaultIndexReaderFactory(mountInfoProvider, null));
209         }
210
211         tracker.setAsyncIndexInfoService(asyncIndexInfoService);
212         tracker.refresh();
213         return tracker;
214     }
215
216     private void initializeIndexCopier() throws IOException {
217         if(indexCopier != null){
218             return;
219         }
220
221         if (prefetchEnabled){
222             log.info("Prefetching of index files enabled. Index would be opened after copying all new files locally");
223         }
224
225         indexCopier = new IndexCopier(getExecutorService(), indexDir.toFile(), prefetchEnabled);
226
227     }
228
229     ExecutorService getExecutorService(){
230         if (executorService == null){
231             executorService = createExecutor();
232         }
233         return executorService;
234     }
235
236     private ExecutorService createExecutor() {
237         ThreadPoolExecutor executor = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 60L, TimeUnit.SECONDS,
238             new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
239             private final AtomicInteger counter = new AtomicInteger();
240             private final Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() {
241                 @Override
242                 public void uncaughtException(Thread t, Throwable e) {
243                     log.warn("Error occurred in asynchronous processing ", e);
244                 }
245             };
246             @Override
247             public Thread newThread(@NotNull Runnable r) {
248                 Thread thread = new Thread(r, createName());
249                 thread.setDaemon(true);
250                 thread.setPriority(Thread.MIN_PRIORITY);
251                 thread.setUncaughtExceptionHandler(handler);
252                 return thread;
253             }
254
255             private String createName() {
256                 return "oak-lucene-" + counter.getAndIncrement();
257             }
258         });
259         executor.setKeepAliveTime(1, TimeUnit.MINUTES);
260         executor.allowCoreThreadTimeOut(true);
261         return executor;
262     }
263
264     private void initialize(){
265         if(indexProvider == null){
266             return;
267         }
268
269         if(nodeAggregator != null){
270             log.debug("Using NodeAggregator {}", nodeAggregator.getClass());
271         }
272
273         indexProvider.setAggregator(nodeAggregator);
274     }
275
276     private void registerObserver() {
277         Observer observer = indexProvider;
278         if (enableAsyncIndexOpen) {
279             backgroundObserver = new BackgroundObserver(indexProvider, getExecutorService(), 5);
280             log.info("Registering the LuceneIndexProvider as a BackgroundObserver");
281         }
282     }
283
284     private void registerLocalIndexObserver(IndexTracker tracker) {
285         if (!hybridIndex){
286             log.info("Hybrid indexing feature disabled");
287             return;
288         }
289         documentQueue = new DocumentQueue( queueSize, tracker, getExecutorService(), statisticsProvider);
290         LocalIndexObserver localIndexObserver = new LocalIndexObserver(documentQueue, statisticsProvider);
291
292         int observerQueueSize = 1000;
293         int builderMaxSize = 5000;
294         // regs.add(bundleContext.registerService(JournalPropertyService.class.getName(),
295         //    new LuceneJournalPropertyService(builderMaxSize), null));
296         ExternalObserverBuilder builder = new ExternalObserverBuilder(documentQueue, tracker, statisticsProvider,
297             getExecutorService(), observerQueueSize);
298         log.info("Configured JournalPropertyBuilder with max size {} and backed by BackgroundObserver " +
299             "with queue size {}", builderMaxSize, observerQueueSize);
300
301         Observer observer = builder.build();
302         externalIndexObserver = builder.getBackgroundObserver();
303         log.info("Hybrid indexing enabled for configured indexes with queue size of {}", queueSize );
304     }
305
306     private IndexInfoProvider registerIndexInfoProvider() {
307         return new LuceneIndexInfoProvider(nodeStore, asyncIndexInfoService, getIndexCheckDir().toFile());
308     }
309
310     private Path getIndexCheckDir() {
311         return checkNotNull(indexDir).resolve("indexCheckDir");
312     }
313
314     private LuceneIndexImporter registerIndexImporterProvider() {
315         return new LuceneIndexImporter(blobStore);
316     }
317
318     private void registerPropertyIndexCleaner( ) {
319
320         if (cleanerInterval <= 0) {
321             log.info("Property index cleaner would not be registered");
322             return;
323         }
324
325         cleaner = new PropertyIndexCleaner(nodeStore, indexPathService, asyncIndexInfoService, statisticsProvider);
326
327         //Proxy check for DocumentNodeStore
328         if (nodeStore instanceof Clusterable ) {
329             cleaner.setRecursiveDelete(true);
330             log.info("PropertyIndexCleaner configured to perform recursive delete");
331         }
332         log.info("Property index cleaner configured to run every [{}] seconds", cleanerInterval);
333     }
334
335     private void registerIndexEditor( IndexTracker tracker) throws IOException {
336         boolean enableCopyOnWrite = true;
337         if (enableCopyOnWrite){
338             initializeIndexCopier();
339             editorProvider = new LuceneIndexEditorProvider(indexCopier, tracker, extractedTextCache,
340                 augmentorFactory,  mountInfoProvider, activeDeletedBlobCollector, null, statisticsProvider);
341             log.info("Enabling CopyOnWrite support. Index files would be copied under {}", indexDir.toAbsolutePath());
342         } else {
343             editorProvider = new LuceneIndexEditorProvider(null, tracker, extractedTextCache, augmentorFactory,
344                 mountInfoProvider, activeDeletedBlobCollector, null, statisticsProvider);
345         }
346         editorProvider.setBlobStore(blobStore);
347
348         if (hybridIndex){
349             editorProvider.setIndexingQueue(checkNotNull(documentQueue));
350         }
351
352
353     }
354
355     public Repository createRepository()
356         throws IOException, InvalidFileStoreVersionException
357     {
358
359         indexDir = repositoryPath.resolve( ".index-lucene" );
360         if (!Files.exists( indexDir )) {
361             Files.createDirectories( indexDir );
362         }
363         blobStore = new FileBlobStore( indexDir.resolve( "blobs" ).toAbsolutePath().toString() );
364
365         statisticsProvider = StatisticsProvider.NOOP;
366
367         if ( SEGMENT_FILE_TYPE == storeType )
368         {
369             fileStore = FileStoreBuilder.fileStoreBuilder( repositoryPath.toFile() )
370                 .withStatisticsProvider( statisticsProvider )
371                 .build();
372             nodeStore = SegmentNodeStoreBuilders.builder( fileStore ) //
373                 .withStatisticsProvider( statisticsProvider ) //
374                 .build();
375         }
376         else if ( IN_MEMORY_TYPE == storeType )
377         {
378             nodeStore = new MemoryNodeStore( );
379         }
380         else
381         {
382             throw new IllegalArgumentException( "Store type " + storeType + " not recognized" );
383         }
384
385         asyncIndexInfoService = new AsyncIndexInfoServiceImpl( nodeStore );
386
387         indexPathService = new IndexPathServiceImpl( nodeStore, mountInfoProvider );
388
389         BufferedOakDirectory.setEnableWritingSingleBlobIndexFile( true );
390
391         initializeExtractedTextCache( statisticsProvider );
392
393         tracker = createTracker();
394
395         indexProvider = new LuceneIndexProvider(tracker, scorerFactory, augmentorFactory);
396
397         initialize();
398         registerObserver();
399         registerLocalIndexObserver(tracker);
400         registerIndexInfoProvider();
401         registerIndexImporterProvider();
402         registerPropertyIndexCleaner();
403
404         registerIndexEditor(tracker);
405
406
407
408         RepositoryInitializer repoInitializer = new RepositoryInitializer( )
409         {
410             private IndexDefinitionBuilder.PropertyRule initRegexAll( IndexDefinitionBuilder.IndexRule rule ) {
411                 return rule
412                     .indexNodeName( )
413                     .property(JCR_LASTMODIFIED ).propertyIndex().type( "Date" ).ordered()
414                     .property(JCR_PRIMARYTYPE).propertyIndex()
415                     .property(JCR_MIXINTYPES).propertyIndex()
416                     .property(JCR_PATH).propertyIndex().ordered()
417                     .property( FulltextIndexConstants.REGEX_ALL_PROPS, true )
418                     .propertyIndex().analyzed( ).nodeScopeIndex();
419             }
420
421             private IndexDefinitionBuilder.PropertyRule initBaseRule( IndexDefinitionBuilder.IndexRule rule ) {
422                 return rule
423                     .indexNodeName( )
424                     .property(JCR_CREATED).propertyIndex().type("Date").ordered()
425                     .property(JCR_LASTMODIFIED ).propertyIndex().type( "Date" ).ordered()
426                     .property(JCR_PRIMARYTYPE).propertyIndex()
427                     .property(JCR_MIXINTYPES).propertyIndex()
428                     .property(JCR_PATH).propertyIndex().ordered()
429                     .property( "id" ).propertyIndex().analyzed( );
430             }
431
432             @Override
433             public void initialize( @Nonnull NodeBuilder root )
434             {
435                 NodeBuilder namespaces;
436                 if ( !root.hasChildNode( NamespaceConstants.REP_NAMESPACES ) )
437                 {
438                     namespaces = Namespaces.createStandardMappings( root );
439                     Namespaces.buildIndexNode( namespaces ); // index node for faster lookup
440                 }
441                 else
442                 {
443                     namespaces = root.getChildNode( NamespaceConstants.REP_NAMESPACES );
444                 }
445                 Namespaces.addCustomMapping( namespaces, "http://archiva.apache.org/jcr/", "archiva" );
446
447                 log.info( "Creating index " );
448
449                 NodeBuilder oakIdx = IndexUtils.getOrCreateOakIndex( root );
450                 if (!oakIdx.hasChildNode( "repo-lucene" ))
451                 {
452                     NodeBuilder lucene = oakIdx.child( "repo-lucene" );
453                     lucene.setProperty( JCR_PRIMARYTYPE, "oak:QueryIndexDefinition", NAME );
454
455                     lucene.setProperty( "compatVersion", 2 );
456                     lucene.setProperty( "type", "lucene" );
457                     // lucene.setProperty("async", "async");
458                     // lucene.setProperty( INCLUDE_PROPERTY_TYPES, ImmutableSet.of(  ), Type.STRINGS );
459                     // lucene.setProperty("refresh",true);
460                     NodeBuilder rules = lucene.child( "indexRules" ).
461                         setProperty( JCR_PRIMARYTYPE, NT_UNSTRUCTURED, NAME );
462                     rules.setProperty( ":childOrder", ImmutableSet.of(
463                         REPOSITORY_NODE_TYPE,
464                         NAMESPACE_NODE_TYPE, //
465                         PROJECT_NODE_TYPE,
466                         PROJECT_VERSION_NODE_TYPE, //
467                         ARTIFACT_NODE_TYPE, //
468                         FACET_NODE_TYPE //
469                     ), Type.STRINGS );
470                     IndexDefinitionBuilder idxBuilder = new IndexDefinitionBuilder( lucene );
471                     idxBuilder.async( "async", "nrt", "sync" ).includedPaths( "/repositories" ).evaluatePathRestrictions();
472
473                     initBaseRule(idxBuilder.indexRule( REPOSITORY_NODE_TYPE ));
474                     initBaseRule(idxBuilder.indexRule( NAMESPACE_NODE_TYPE ))
475                         .property( "namespace" ).propertyIndex().analyzed();
476                     initBaseRule(idxBuilder.indexRule( PROJECT_NODE_TYPE ))
477                         .property( "name" ).propertyIndex().analyzed().notNullCheckEnabled().nullCheckEnabled();
478                     initBaseRule( idxBuilder.indexRule( PROJECT_VERSION_NODE_TYPE ) )
479                         .property("name").propertyIndex().analyzed().notNullCheckEnabled().nullCheckEnabled()
480                         .property("description").propertyIndex().analyzed().notNullCheckEnabled().nullCheckEnabled()
481                         .property("url").propertyIndex().analyzed( ).notNullCheckEnabled().nullCheckEnabled()
482                         .property("incomplete").type("Boolean").propertyIndex()
483                         .property("mailinglist/name").propertyIndex().analyzed()
484                         .property("license/license.name").propertyIndex().analyzed();
485                     initBaseRule(idxBuilder.indexRule( ARTIFACT_NODE_TYPE ))
486                         .property( "whenGathered" ).type("Date").propertyIndex().analyzed().ordered()
487                         .property("size").type("Long").propertyIndex().analyzed().ordered()
488                         .property("version").propertyIndex().analyzed().ordered()
489                         .property("checksums/*/value").propertyIndex();
490
491                     initBaseRule( idxBuilder.indexRule( CHECKSUM_NODE_TYPE ) )
492                         .property("type").propertyIndex()
493                         .property("value").propertyIndex();
494
495                     initRegexAll( idxBuilder.indexRule( FACET_NODE_TYPE ) )
496                         .property("archiva:facetId").propertyIndex().analyzed().ordered()
497                         .property("archiva:name").propertyIndex().analyzed().ordered();
498
499
500                     idxBuilder.indexRule( MIXIN_META_SCM )
501                         .property( "scm.connection" ).propertyIndex()
502                         .property( "scm.developerConnection" ).propertyIndex()
503                         .property( "scm.url").type("URI").propertyIndex().analyzed();
504                     idxBuilder.indexRule( MIXIN_META_CI )
505                         .property( "ci.system" ).propertyIndex( )
506                         .property( "ci.ur" ).propertyIndex( ).analyzed( );
507                     idxBuilder.indexRule( MIXIN_META_ISSUE )
508                         .property( "issue.system").propertyIndex()
509                         .property("issue.url").propertyIndex().analyzed();
510                     idxBuilder.indexRule( MIXIN_META_ORGANIZATION )
511                         .property( "org.name" ).propertyIndex( ).analyzed( )
512                         .property( "org.url" ).propertyIndex( ).analyzed( );
513                     idxBuilder.indexRule( MIXIN_META_LICENSE )
514                         .property( "license.name" ).propertyIndex( ).analyzed( )
515                         .property( "license.url" ).propertyIndex( ).analyzed( );
516                     idxBuilder.indexRule( MIXIN_META_MAILINGLIST )
517                         .property( "name" ).propertyIndex().analyzed();
518                     initBaseRule(idxBuilder.indexRule( DEPENDENCY_NODE_TYPE ))
519                         .property( "groupId" ).propertyIndex().analyzed().ordered()
520                         .property( "artifactId").propertyIndex().analyzed().ordered()
521                         .property("version").propertyIndex().analyzed().ordered()
522                         .property("type").propertyIndex().analyzed().ordered()
523                         .property( "classifier" ).propertyIndex().ordered()
524                         .property("scope").propertyIndex()
525                         .property("systemPath").propertyIndex().analyzed()
526                         .property("optional").type("Boolean").propertyIndex();
527
528                     idxBuilder.aggregateRule( PROJECT_VERSION_NODE_TYPE ).include( "dependencies")
529                         .path("dependencies/*" ).relativeNode();
530
531                     idxBuilder.build( );
532
533                     IndexUtils.createIndexDefinition( oakIdx, "baseIndexes", true, false, ImmutableList.of( "jcr:uuid", "rep:principalName" ), null );
534
535                     log.info( "Index: {} repo-lucene: {}", lucene, lucene.getChildNode( "repo-lucene" ) );
536                     log.info( "repo-lucene Properties: {}", lucene.getChildNode( "repo-lucene" ).getProperties( ) );
537                 } else {
538                     log.info( "No Index update" );
539                 }
540                 // IndexUtils.createIndexDefinition(  )
541
542             }
543         };
544
545         //        ExternalObserverBuilder builder = new ExternalObserverBuilder(queue, tracker, statsProvider,
546 //            executorService, queueSize);
547 //        Observer observer = builder.build();
548 //        builder.getBackgroundObserver();
549
550
551
552         log.info( "Starting Jcr repo with nodeStore {}", nodeStore );
553         Jcr jcr = new Jcr( nodeStore ).with( editorProvider ) //
554             .with( backgroundObserver ) //
555             .with( externalIndexObserver )
556             // .with(observer)
557             .with( (QueryIndexProvider) indexProvider )
558             .with (repoInitializer)
559             .withAsyncIndexing( "async", 5 );
560             //
561             //.withAsyncIndexing( "async", 5 );
562         StopWatch stopWatch = new StopWatch();
563         stopWatch.start();
564         Repository r = jcr.createRepository();
565         stopWatch.stop();
566         log.info( "time to create jcr repository: {} ms", stopWatch.getTime() );
567
568         return r;
569
570
571     }
572
573     private void closeSilently( Closeable service) {
574         if (service!=null) {
575             try
576             {
577                 service.close();
578             }
579             catch ( Throwable e )
580             {
581                 //
582             }
583         }
584     }
585
586     public void close()
587     {
588         log.info( "Closing JCR RepositoryFactory" );
589         closeSilently( fileStore );
590         closeSilently( backgroundObserver );
591         closeSilently( externalIndexObserver );
592         closeSilently( indexProvider );
593         indexProvider = null;
594         closeSilently( documentQueue );
595         closeSilently( nrtIndexFactory );
596         closeSilently( indexCopier );
597
598         if (executorService != null){
599             executorService.shutdown();
600             try
601             {
602                 executorService.awaitTermination(1, TimeUnit.MINUTES);
603             }
604             catch ( InterruptedException e )
605             {
606                 e.printStackTrace( );
607             }
608         }
609
610         if (extractedTextCache != null) {
611             extractedTextCache.close();
612         }
613
614     }
615
616     public StoreType getStoreType()
617     {
618         return storeType;
619     }
620
621     public void setStoreType( StoreType storeType )
622     {
623         this.storeType = storeType;
624     }
625
626     public Path getRepositoryPath()
627     {
628         return repositoryPath;
629     }
630
631     public void setRepositoryPath( Path repositoryPath )
632     {
633         this.repositoryPath = repositoryPath;
634     }
635
636     public void setRepositoryPath( String repositoryPath )
637     {
638         this.repositoryPath = Paths.get( repositoryPath );
639         if ( !Files.exists( this.repositoryPath ) )
640         {
641             try
642             {
643                 Files.createDirectories( this.repositoryPath );
644             }
645             catch ( IOException e )
646             {
647                 log.error( e.getMessage(), e );
648                 throw new IllegalArgumentException( "cannot create directory:" + repositoryPath, e );
649             }
650         }
651     }
652
653
654 }