-
Notifications
You must be signed in to change notification settings - Fork 216
/
RepositoryConfiguration.java
1915 lines (1709 loc) · 84.7 KB
/
RepositoryConfiguration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* ModeShape (http://www.modeshape.org)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* ModeShape is free software. Unless otherwise indicated, all code in ModeShape
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* ModeShape is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.modeshape.jcr;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.login.LoginException;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.search.DefaultSimilarity;
import org.apache.lucene.util.Version;
import org.infinispan.manager.CacheContainer;
import org.infinispan.schematic.SchemaLibrary;
import org.infinispan.schematic.SchemaLibrary.Problem;
import org.infinispan.schematic.SchemaLibrary.Results;
import org.infinispan.schematic.Schematic;
import org.infinispan.schematic.document.Array;
import org.infinispan.schematic.document.Changes;
import org.infinispan.schematic.document.Document;
import org.infinispan.schematic.document.Document.Field;
import org.infinispan.schematic.document.EditableDocument;
import org.infinispan.schematic.document.Editor;
import org.infinispan.schematic.document.Json;
import org.infinispan.schematic.document.ParsingException;
import org.infinispan.transaction.lookup.GenericTransactionManagerLookup;
import org.infinispan.transaction.lookup.TransactionManagerLookup;
import org.infinispan.util.FileLookup;
import org.infinispan.util.FileLookupFactory;
import org.infinispan.util.ReflectionUtil;
import org.infinispan.util.Util;
import org.jgroups.Channel;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.collection.Problems;
import org.modeshape.common.collection.SimpleProblems;
import org.modeshape.common.logging.Logger;
import org.modeshape.common.util.ObjectUtil;
import org.modeshape.jcr.clustering.DefaultChannelProvider;
import org.modeshape.jcr.security.AnonymousProvider;
import org.modeshape.jcr.security.JaasProvider;
import org.modeshape.jcr.value.binary.AbstractBinaryStore;
import org.modeshape.jcr.value.binary.DatabaseBinaryStore;
import org.modeshape.jcr.value.binary.FileSystemBinaryStore;
import org.modeshape.jcr.value.binary.InfinispanBinaryStore;
import org.modeshape.jcr.value.binary.TransientBinaryStore;
/**
* A representation of the configuration for a {@link JcrRepository JCR Repository}.
* <p>
* Each repository configuration is loaded from a JSON document. A {@link #validate() valid} repository configuration requires
* that the JSON document validates using the ModeShape repository configuration JSON Schema.
* </p>
* <p>
* Variables may appear anywhere within the document's string field values. If a variable is to be used within a non-string field,
* simply use a string field within the JSON document. When a RepositoryConfiguration instance is created from a JSON document,
* these variables will be replaced with the System properties of the same name, and any resulting fields that are expected to be
* non-string values will be converted into the expected field type. As expected, use {@link #validate()} to ensure the
* configuration is valid.
* </p>
* <p>
* Variables take the form:
*
* <pre>
* variable := '${' variableNames [ ':' defaultValue ] '}'
*
* variableNames := variableName [ ',' variableNames ]
*
* variableName := /* any characters except ',' and ':' and '}'
*
* defaultValue := /* any characters except
* </pre>
*
* Note that <i>variableName</i> is the name used to look up a System property via {@link System#getProperty(String)}.
* </p>
* Notice that the syntax supports multiple <i>variables</i>. The logic will process the <i>variables</i> from let to right, until
* an existing System property is found. And at that point, it will stop and will not attempt to find values for the other
* <i>variables</i>.
* <p>
*/
@Immutable
public class RepositoryConfiguration {
/**
* The standard identifier of the root node is '{@value} '.
*/
public static final String ROOT_NODE_ID = "/";
/**
* The name of the 'system' workspace.
*/
public static final String SYSTEM_WORKSPACE_NAME = "system";
/**
* The default JNDI location for repositories is "java:jcr/local/<name>", where "<name>" is the name of the repository.
*/
public static final String DEFAULT_JNDI_PREFIX_OF_NAME = "java:jcr/local/";
final static TimeUnit GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT = TimeUnit.MINUTES;
/**
* The process of garbage collecting locks and binary values runs periodically, and this value controls how often it runs. The
* value is currently set to 5 minutes.
*/
final static int GARBAGE_COLLECTION_SWEEP_PERIOD = (int)TimeUnit.MILLISECONDS.convert(5, GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
/**
* Each time the garbage collection process runs, session-scoped locks that are still used by active sessions will have their
* expiry times extended by this amount of time. Each repository instance in the ModeShape cluster will run its own cleanup
* process, which will extend the expiry times of its own locks. As soon as a repository is no longer running the cleanup
* process, we know that there can be no active sessions.
* <p>
* The extension interval is generally twice the length of the period that the garbage collection runs, ensuring that any
* slight deviation in the period does not cause locks to be expired prematurely.
* </p>
*/
final static int LOCK_EXTENSION_INTERVAL_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
/**
* The amount of time that a lock may be expired before being removed. The sweep process will extend the locks for active
* sessions, so only unused locks will have an unmodified expiry time. The value is currently twice the sweep period.
*/
final static int LOCK_EXPIRY_AGE_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
/**
* As binary values are no longer used, they are quarantined in the binary store. When the garbage collection process runs,
* any binary values that have been quarantined longer than this duration will be removed.
* <p>
* The age is generally twice the length of the period that the garbage collection process runs, ensuring that any slight
* deviation in the period does not cause binary values to be removed prematurely.
* </p>
*/
final static int UNUSED_BINARY_VALUE_AGE_IN_MILLIS = (int)TimeUnit.MILLISECONDS.convert(GARBAGE_COLLECTION_SWEEP_PERIOD * 2,
GARBAGE_COLLECTION_SWEEP_PERIOD_UNIT);
protected static final Document EMPTY = Schematic.newDocument();
protected static final Map<String, String> PROVIDER_ALIASES;
protected static final Map<String, String> SEQUENCER_ALIASES;
protected static final Map<String, String> EXTRACTOR_ALIASES;
protected static SchemaLibrary SCHEMA_LIBRARY;
public static final String JSON_SCHEMA_URI = "http://modeshape.org/3.0/repository-config#";
public static final String JSON_SCHEMA_RESOURCE_PATH = "org/modeshape/jcr/repository-config-schema.json";
public static class FieldName {
/**
* The name for the field specifying the repository's name.
*/
public static final String NAME = "name";
/**
* The name for the field specifying a description.
*/
public static final String DESCRIPTION = "description";
/**
* The name for the optional field specifying where in JNDI this repository should be registered.
*/
public static final String JNDI_NAME = "jndiName";
/**
* The specification of whether the repository should expect and detect whether JCR clients modify the content within
* transactions. The default value of 'auto' will automatically detect the use of both user- and container-managed
* transactions and also works when the JCR client does not use transactions; this will work in most situations. The value
* of 'none' specifies that the repository should not attempt to detect existing transactions; this setting is an
* optimization that should be used *only* if JCR clients will never use transactions to change the repository content.
*/
public static final String TRANSACTION_MODE = "transactionMode";
/**
* The name for the field whose value is a document containing the monitoring information.
*/
public static final String MONITORING = "monitoring";
/**
* The name for the optional field specifying whether the monitoring system is enabled or disabled.
*/
public static final String MONITORING_ENABLED = "enabled";
/**
* The name for the field whose value is a document containing the Infinispan storage information.
*/
public static final String STORAGE = "storage";
/**
* The name for the field containing the name of the Infinispan cache that this repository should use. If not specified,
* the repository's name is used as the Infinispan cache name.
*/
public static final String CACHE_NAME = "cacheName";
/**
* The name for the field containing the name of the Infinispan configuration file. If a file could not be found (on the
* thread context classloader, on the application's classpath, or on the system classpath), then the name is used to look
* in JNDI for an Infinispan CacheContainer instance. If no such container is found, then a default Infinispan
* configuration (a basic, local mode, non-clustered cache) will be used.
*/
public static final String CACHE_CONFIGURATION = "cacheConfiguration";
/**
* The name for the field containing the name of the Infinispan transaction manager lookup class. This is only used if no
* {@link #CACHE_CONFIGURATION cacheConfiguration} value is specified and ModeShape needs to instantiate the Infinispan
* {@link CacheContainer}. By default, the {@link GenericTransactionManagerLookup} class is used.
*/
public static final String CACHE_TRANSACTION_MANAGER_LOOKUP = "transactionManagerLookup";
/**
* The size threshold that dictates whether String and binary values should be stored in the binary store. String and
* binary values smaller than this value are stored with the node, whereas string and binary values with a size equal to
* or greater than this limit will be stored separately from the node and in the binary store, keyed by the SHA-1 hash of
* the value. This is a space and performance optimization that stores each unique large value only once. The default
* value is '4096' bytes, or 4 kilobytes.
*/
public static final String MINIMUM_BINARY_SIZE_IN_BYTES = "minimumBinarySizeInBytes";
/**
* The name for the field whose value is a document containing workspace information.
*/
public static final String WORKSPACES = "workspaces";
/**
* The name for the field under "workspaces" specifying the array of names for the predefined (existing) workspaces.
*/
public static final String PREDEFINED = "predefined";
/**
* The name for the field under "workspaces" specifying whether users can create additional workspaces beyond the
* predefined, system, and default workspaces.
*/
public static final String ALLOW_CREATION = "allowCreation";
/**
* The name for the field under "workspaces" specifying the name of the workspace that should be used by default when
* creating sessions where the workspace is not specified.
*/
public static final String DEFAULT = "default";
/**
* The name for the field whose value is a document containing binary storage information.
*/
public static final String BINARY_STORAGE = "binaryStorage";
/**
* The name for the field whose value is a document containing security information.
*/
public static final String SECURITY = "security";
/**
* The name for the field under "security" specifying the optional JAAS configuration.
*/
public static final String JAAS = "jaas";
/**
* The name for the field under "security/jaas" specifying the JAAS policy that should be used. An empty string value
* implies that JAAS should not be used.
*/
public static final String JAAS_POLICY_NAME = "policyName";
/**
* The name for the field under "security" specifying the optional anonymous security configuration.
*/
public static final String ANONYMOUS = "anonymous";
/**
* The name for the field under "security/anonymous" specifying the roles that should be granted to anonymous users. By
* default, anonymous users are granted the "admin" role, but this can be completely disabled by providing an empty array.
*/
public static final String ANONYMOUS_ROLES = "roles";
/**
* The name for the field under "security/anonymous" specifying the username that should be used for anonymous users. The
* default is "<anonymous>";
*/
public static final String ANONYMOUS_USERNAME = "username";
/**
* The name for the field under "security/anonymous" specifying whether clients that fail authentication should instead be
* granted anonymous credentials.
*/
public static final String USE_ANONYMOUS_ON_FAILED_LOGINS = "useOnFailedLogin";
public static final String PROVIDERS = "providers";
public static final String TYPE = "type";
public static final String DIRECTORY = "directory";
public static final String CLASSLOADER = "classloader";
public static final String CLASSNAME = "classname";
public static final String DATA_SOURCE_JNDI_NAME = "dataSourceJndiName";
public static final String DATA_CACHE_NAME = "dataCacheName";
public static final String METADATA_CACHE_NAME = "metadataCacheName";
public static final String QUERY = "query";
public static final String QUERY_ENABLED = "enabled";
public static final String REBUILD_UPON_STARTUP = "rebuildUponStartup";
public static final String INDEX_STORAGE = "indexStorage";
public static final String INDEXING = "indexing";
public static final String INDEXING_BACKEND = "backend";
public static final String TABLES_INCLUDE_INHERITED_COLUMNS = "tablesIncludeInheritedColumns";
public static final String EXTRACTORS = "extractors";
public static final String SEQUENCING = "sequencing";
public static final String SEQUENCERS = "sequencers";
public static final String PATH_EXPRESSION = "pathExpression";
public static final String PATH_EXPRESSIONS = "pathExpressions";
/**
* The name for the field (under "sequencing" and "query") specifying the thread pool that should be used for sequencing.
* By default, all repository instances will use the same thread pool within the engine. To use a dedicated thread pool
* for a single repository, simply use a name that is unique from all other repositories.
*/
public static final String THREAD_POOL = "threadPool";
public static final String REMOVE_DERIVED_CONTENT_WITH_ORIGINAL = "removeDerivedContentWithOriginal";
public static final String INDEXING_ANALYZER = "analyzer";
public static final String INDEXING_ANALYZER_CLASSPATH = "analyzerClasspath";
public static final String INDEXING_SIMILARITY = "similarity";
public static final String INDEXING_BATCH_SIZE = "batchSize";
public static final String INDEXING_INDEX_FORMAT = "indexFormat";
public static final String INDEXING_READER_STRATEGY = "readerStrategy";
public static final String INDEXING_MODE = "mode";
public static final String INDEXING_ASYNC_THREAD_POOL_SIZE = "asyncThreadPoolSize";
public static final String INDEXING_ASYNC_MAX_QUEUE_SIZE = "asyncMaxQueueSize";
public static final String INDEX_STORAGE_LOCATION = "location";
public static final String INDEX_STORAGE_SOURCE_LOCATION = "sourceLocation";
public static final String INDEX_STORAGE_LOCKING_STRATEGY = "lockingStrategy";
public static final String INDEX_STORAGE_FILE_SYSTEM_ACCESS_TYPE = "fileSystemAccessType";
public static final String INDEX_STORAGE_REFRESH_IN_SECONDS = "refreshInSeconds";
public static final String INDEX_STORAGE_COPY_BUFFER_SIZE_IN_MEGABYTES = "copyBufferSizeInMegabytes";
public static final String INDEX_STORAGE_RETRY_MARKER_LOOKUP = "retryMarkerLookup";
public static final String INDEX_STORAGE_RETRY_INITIALIZE_PERIOD_IN_SECONDS = "retryInitializePeriodInSeconds";
public static final String INDEX_STORAGE_INFINISPAN_LOCK_CACHE = "lockCacheName";
public static final String INDEX_STORAGE_INFINISPAN_DATA_CACHE = "dataCacheName";
public static final String INDEX_STORAGE_INFINISPAN_META_CACHE = "metadataCacheName";
public static final String INDEX_STORAGE_INFINISPAN_CONTAINER = "cacheConfiguration";
public static final String INDEX_STORAGE_INFINISPAN_CHUNK_SIZE_IN_BYTES = "chunkSizeInBytes";
public static final String INDEXING_BACKEND_JMS_CONNECTION_FACTORY_JNDI_NAME = "connectionFactoryJndiName";
public static final String INDEXING_BACKEND_JMS_QUEUE_JNDI_NAME = "queueJndiName";
public static final String INDEXING_BACKEND_JGROUPS_CHANNEL_NAME = "channelName";
public static final String INDEXING_BACKEND_JGROUPS_CHANNEL_CONFIGURATION = "channelConfiguration";
/**
* The name of the clustering top-level configuration document
*/
public static final String CLUSTERING = "clustering";
/**
* The name of the cluster as used by JChannel.connect
*/
public static final String CLUSTER_NAME = "clusterName";
/**
* The fully qualified name of the {@link org.modeshape.jcr.clustering.ChannelProvider} implementation which will provide
* the JChannel instance
*/
public static final String CHANNEL_PROVIDER = "channelProvider";
/**
* The optional string representing a valid JGroups channel configuration object
*/
public static final String CHANNEL_CONFIGURATION = "channelConfiguration";
}
public static class Default {
/**
* The default value of the {@link FieldName#MINIMUM_BINARY_SIZE_IN_BYTES} field is '{@value} ' (4 kilobytes).
*/
public static final long MINIMUM_BINARY_SIZE_IN_BYTES = 4 * 1024L;
/**
* The default value of the {@link FieldName#ALLOW_CREATION} field is '{@value} '.
*/
public static final boolean ALLOW_CREATION = true;
/**
* The default value of the {@link FieldName#DEFAULT} field is '{@value} '.
*/
public static final String DEFAULT = "default";
/**
* The default value of the {@link FieldName#TRANSACTION_MODE} field is '{@value} '.
*/
public static final TransactionMode TRANSACTION_MODE = TransactionMode.AUTO;
/**
* The default value of the {@link FieldName#CACHE_TRANSACTION_MANAGER_LOOKUP} field is
* "org.infinispan.transaction.lookup.GenericTransactionManagerLookup".
*/
public static final String CACHE_TRANSACTION_MANAGER_LOOKUP = GenericTransactionManagerLookup.class.getName();
/**
* The default value of the {@link FieldName#JAAS_POLICY_NAME} field is '{@value} '.
*/
public static final String JAAS_POLICY_NAME = "modeshape-jcr";
/**
* The default value of the {@link FieldName#ANONYMOUS_ROLES} field is a list with 'admin' as the role.
*/
public static final Set<String> ANONYMOUS_ROLES = Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList(new String[] {ModeShapeRoles.ADMIN})));
/**
* The default value of the {@link FieldName#USE_ANONYMOUS_ON_FAILED_LOGINS} field is '{@value} '.
*/
public static final boolean USE_ANONYMOUS_ON_FAILED_LOGINS = false;
public static final String ANONYMOUS_USERNAME = "<anonymous>";
public static final boolean QUERY_ENABLED = true;
public static final boolean MONITORING_ENABLED = true;
public static final boolean REMOVE_DERIVED_CONTENT_WITH_ORIGINAL = true;
/**
* The default value of the {@link FieldName#THREAD_POOL} field is '{@value} '.
*/
public static final String THREAD_POOL = "modeshape-workers";
public static final String INDEXING_ANALYZER = StandardAnalyzer.class.getName();
public static final String INDEXING_SIMILARITY = DefaultSimilarity.class.getName();
public static final String INDEXING_BATCH_SIZE = "-1";
@SuppressWarnings( "deprecation" )
public static final String INDEXING_INDEX_FORMAT = Version.LUCENE_CURRENT.name();
public static final IndexReaderStrategy INDEXING_READER_STRATEGY = IndexReaderStrategy.SHARED;
public static final IndexingMode INDEXING_MODE = IndexingMode.SYNC;
public static final String INDEXING_ASYNC_THREAD_POOL_SIZE = "1";
public static final String INDEXING_ASYNC_MAX_QUEUE_SIZE = "1";
public static final FileSystemLockingStrategy INDEX_STORAGE_LOCKING_STRATEGY = FileSystemLockingStrategy.NATIVE;
public static final FileSystemAccessType INDEX_STORAGE_FILE_SYSTEM_ACCESS_TYPE = FileSystemAccessType.AUTO;
public static final String INDEX_STORAGE_REFRESH_IN_SECONDS = "3600";
public static final String INDEX_STORAGE_COPY_BUFFER_SIZE_IN_MEGABYTES = "16";
public static final String INDEX_STORAGE_RETRY_MARKER_LOOKUP = "0";
public static final String INDEX_STORAGE_RETRY_INITIALIZE_PERIOD_IN_SECONDS = "0";
public static final String INDEX_STORAGE_INFINISPAN_CHUNK_SIZE_IN_BYTES = "16834";
public static final String INDEXING_BACKEND_TYPE = "lucene";
public static final String CLUSTER_NAME = "ModeShape-JCR";
public static final String CHANNEL_PROVIDER = DefaultChannelProvider.class.getName();
}
public static final class FieldValue {
public static final String INDEX_STORAGE_RAM = "ram";
public static final String INDEX_STORAGE_FILESYSTEM = "filesystem";
public static final String INDEX_STORAGE_FILESYSTEM_MASTER = "filesystem-master";
public static final String INDEX_STORAGE_FILESYSTEM_SLAVE = "filesystem-slave";
public static final String INDEX_STORAGE_INFINISPAN = "infinispan";
public static final String INDEX_STORAGE_CUSTOM = "custom";
public static final String INDEXING_BACKEND_TYPE_LUCENE = "lucene";
public static final String INDEXING_BACKEND_TYPE_JMS_MASTER = "jms-master";
public static final String INDEXING_BACKEND_TYPE_JMS_SLAVE = "jms-slave";
public static final String INDEXING_BACKEND_TYPE_JGROUPS_MASTER = "jgroups-master";
public static final String INDEXING_BACKEND_TYPE_JGROUPS_SLAVE = "jgroups-slave";
public static final String INDEXING_BACKEND_TYPE_BLACKHOLE = "blackhole";
public static final String INDEXING_BACKEND_TYPE_CUSTOM = "custom";
public static final String BINARY_STORAGE_TYPE_FILE = "file";
public static final String BINARY_STORAGE_TYPE_CACHE = "cache";
public static final String BINARY_STORAGE_TYPE_DATABASE = "database";
public static final String BINARY_STORAGE_TYPE_CUSTOM = "custom";
}
public enum IndexingMode {
SYNC,
ASYNC;
}
public enum IndexReaderStrategy {
SHARED,
NOT_SHARED;
}
public enum FileSystemLockingStrategy {
SIMPLE,
NATIVE,
SINGLE,
NONE;
}
public enum FileSystemAccessType {
AUTO,
SIMPLE,
NIO,
MMAP;
}
/**
* The set of field names that should be skipped when {@link Component#createInstance(ClassLoader) instantiating a component}.
*/
protected static final Set<String> COMPONENT_SKIP_PROPERTIES;
/**
* Flag which is used to determine whether clustering should be enabled or not
*/
protected static final boolean JGROUPS_PRESENT = isJGroupsInClasspath();
static {
Set<String> skipProps = new HashSet<String>();
skipProps.add(FieldName.CLASSLOADER);
skipProps.add(FieldName.TYPE);
COMPONENT_SKIP_PROPERTIES = Collections.unmodifiableSet(skipProps);
String jaasProvider = "org.modeshape.jcr.security.JaasProvider";
String servletProvider = "org.modeshape.jcr.security.ServletProvider";
Map<String, String> aliases = new HashMap<String, String>();
aliases.put("jaas", jaasProvider);
aliases.put("jaasprovider", jaasProvider);
aliases.put("servlet", servletProvider);
aliases.put("servlets", servletProvider);
aliases.put("servletprovider", servletProvider);
PROVIDER_ALIASES = Collections.unmodifiableMap(aliases);
String cndSequencer = "org.modeshape.sequencer.cnd.CndSequencer";
String classfileSequencer = "org.modeshape.sequencer.classfile.ClassFileSequencer";
String ddlSequencer = "org.modeshape.sequencer.ddl.DdlSequencer";
String imageSequencer = "org.modeshape.sequencer.image.ImageMetadataSequencer";
String javaSequencer = "org.modeshape.sequencer.javafile.JavaFileSequencer";
String modelSequencer = "org.modeshape.sequencer.teiid.ModelSequencer";
String vdbSequencer = "org.modeshape.sequencer.teiid.VdbSequencer";
String msofficeSequencer = "org.modeshape.sequencer.msoffice.MSOfficeMetadataSequencer";
String wsdlSequencer = "org.modeshape.sequencer.wsdl.WsdlSequencer";
String xsdSequencer = "org.modeshape.sequencer.xsd.XsdSequencer";
String xmlSequencer = "org.modeshape.sequencer.xml.XmlSequencer";
String zipSequencer = "org.modeshape.sequencer.zip.ZipSequencer";
String mp3Sequencer = "org.modeshape.sequencer.mp3.Mp3MetadataSequencer";
String fixedWidthTextSequencer = "org.modeshape.sequencer.text.FixedWidthTextSequencer";
String delimitedTextSequencer = "org.modeshape.sequencer.text.DelimitedTextSequencer";
aliases = new HashMap<String, String>();
aliases.put("cnd", cndSequencer);
aliases.put("cndsequencer", cndSequencer);
aliases.put("class", classfileSequencer);
aliases.put("classfile", classfileSequencer);
aliases.put("classsequencer", classfileSequencer);
aliases.put("classfilesequencer", classfileSequencer);
aliases.put("ddl", ddlSequencer);
aliases.put("ddlsequencer", ddlSequencer);
aliases.put("image", imageSequencer);
aliases.put("imagesequencer", imageSequencer);
aliases.put("java", javaSequencer);
aliases.put("javasource", javaSequencer);
aliases.put("javasequencer", javaSequencer);
aliases.put("javasourcesequencer", javaSequencer);
aliases.put("model", modelSequencer);
aliases.put("modelsequencer", modelSequencer);
aliases.put("vdb", vdbSequencer);
aliases.put("vdbsequencer", vdbSequencer);
aliases.put("msoffice", msofficeSequencer);
aliases.put("msofficesequencer", msofficeSequencer);
aliases.put("wsdl", wsdlSequencer);
aliases.put("wsdlsequencer", wsdlSequencer);
aliases.put("xsd", xsdSequencer);
aliases.put("xsdsequencer", xsdSequencer);
aliases.put("xml", xmlSequencer);
aliases.put("xmlsequencer", xmlSequencer);
aliases.put("zip", zipSequencer);
aliases.put("zipsequencer", zipSequencer);
aliases.put("mp3", mp3Sequencer);
aliases.put("mp3sequencer", mp3Sequencer);
aliases.put("fixedwidthtext", fixedWidthTextSequencer);
aliases.put("fixedwidthtextsequencer", fixedWidthTextSequencer);
aliases.put("delimitedtext", delimitedTextSequencer);
aliases.put("delimitedtextsequencer", delimitedTextSequencer);
SEQUENCER_ALIASES = Collections.unmodifiableMap(aliases);
String tikaExtractor = "org.modeshape.extractor.tika.TikaTextExtractor";
String vdbExtractor = "org.modeshape.extractor.teiid.TeiidVdbTextExtractor";
aliases = new HashMap<String, String>();
aliases.put("tika", tikaExtractor);
aliases.put("tikaextractor", tikaExtractor);
aliases.put("tikatextextractor", tikaExtractor);
aliases.put("vdb", vdbExtractor);
aliases.put("vdbextractor", vdbExtractor);
aliases.put("vdbtextextractor", vdbExtractor);
EXTRACTOR_ALIASES = Collections.unmodifiableMap(aliases);
SCHEMA_LIBRARY = Schematic.createSchemaLibrary("ModeShape Repository Configuration Schemas");
FileLookup factory = FileLookupFactory.newInstance();
InputStream configStream = factory.lookupFile(JSON_SCHEMA_RESOURCE_PATH, RepositoryConfiguration.class.getClassLoader());
if (configStream == null) {
Logger.getLogger(RepositoryConfiguration.class).error(JcrI18n.unableToFindRepositoryConfigurationSchema,
JSON_SCHEMA_RESOURCE_PATH);
}
try {
Document configDoc = Json.read(configStream);
SCHEMA_LIBRARY.put(JSON_SCHEMA_URI, configDoc);
} catch (IOException e) {
Logger.getLogger(RepositoryConfiguration.class).error(e,
JcrI18n.unableToLoadRepositoryConfigurationSchema,
JSON_SCHEMA_RESOURCE_PATH);
}
}
/**
* Utility method to replace all system property variables found within the specified document.
*
* @param doc the document; may not be null
* @return the modified document if system property variables were found, or the <code>doc</code> instance if no such
* variables were found
*/
protected static Document replaceSystemPropertyVariables( Document doc ) {
if (doc.isEmpty()) return doc;
Document modified = doc.withVariablesReplacedWithSystemProperties();
if (modified == doc) return doc;
// Otherwise, we changed some values. Note that the system properties can only be used in
// string values, whereas the schema may expect non-string values. Therefore, we need to validate
// the document against the schema and possibly perform some conversions of values ...
return SCHEMA_LIBRARY.convertValues(doc, JSON_SCHEMA_URI);
}
/**
* Resolve the supplied URL to a JSON document, read the contents, and parse into a {@link RepositoryConfiguration}.
*
* @param url the URL; may not be null
* @return the parsed repository configuration; never null
* @throws ParsingException if the content could not be parsed as a valid JSON document
*/
public static RepositoryConfiguration read( URL url ) throws ParsingException {
Document doc = Json.read(url);
return new RepositoryConfiguration(doc, withoutExtension(url.getFile()));
}
/**
* Read the supplied JSON file and parse into a {@link RepositoryConfiguration}.
*
* @param file the file; may not be null
* @return the parsed repository configuration; never null
* @throws ParsingException if the content could not be parsed as a valid JSON document
* @throws FileNotFoundException if the file could not be found
*/
public static RepositoryConfiguration read( File file ) throws ParsingException, FileNotFoundException {
Document doc = Json.read(new FileInputStream(file));
return new RepositoryConfiguration(doc, withoutExtension(file.getName()));
}
/**
* Read the supplied stream containing a JSON file, and parse into a {@link RepositoryConfiguration}.
*
* @param stream the file; may not be null
* @param name the name of the resource; may not be null
* @return the parsed repository configuration; never null
* @throws ParsingException if the content could not be parsed as a valid JSON document
* @throws FileNotFoundException if the file could not be found
*/
public static RepositoryConfiguration read( InputStream stream,
String name ) throws ParsingException, FileNotFoundException {
Document doc = Json.read(stream);
return new RepositoryConfiguration(doc, withoutExtension(name));
}
/**
* Read the repository configuration given by the supplied path to a file on the file system, the path a classpath resource
* file, or a string containg the actual JSON content.
*
* @param resourcePathOrJsonContentString the path to a file on the file system, the path to a classpath resource file or the
* JSON content string; may not be null
* @return the parsed repository configuration; never null
* @throws ParsingException if the content could not be parsed as a valid JSON document
* @throws FileNotFoundException if the file could not be found
*/
public static RepositoryConfiguration read( String resourcePathOrJsonContentString )
throws ParsingException, FileNotFoundException {
FileLookup factory = FileLookupFactory.newInstance();
InputStream stream = factory.lookupFile(resourcePathOrJsonContentString, Thread.currentThread().getContextClassLoader());
if (stream == null) {
stream = factory.lookupFile(resourcePathOrJsonContentString, RepositoryConfiguration.class.getClassLoader());
}
if (stream != null) {
Document doc = Json.read(stream);
return new RepositoryConfiguration(doc, withoutExtension(resourcePathOrJsonContentString));
}
// Try a file ...
File file = new File(resourcePathOrJsonContentString);
if (file.exists() && file.isFile()) {
return read(file);
}
String content = resourcePathOrJsonContentString.trim();
if (content.startsWith("{")) {
// Try to parse the document ...
Document doc = Json.read(content);
return new RepositoryConfiguration(doc, null);
}
throw new FileNotFoundException(resourcePathOrJsonContentString);
}
private static String withoutExtension( String name ) {
int index = name.lastIndexOf('.');
if (index > 0) {
name = name.substring(0, index);
}
return name;
}
private static boolean isEmpty( String str ) {
return str == null || str.trim().length() == 0;
}
private static Document ensureNamed( Document document,
String documentName ) {
String name = document.getString(FieldName.NAME);
if (isEmpty(name) && documentName != null && documentName.trim().length() != 0) {
EditableDocument doc = Schematic.newDocument(document);
doc.setString(FieldName.NAME, documentName);
document = doc;
}
return document;
}
private static boolean isJGroupsInClasspath() {
List<String> requiredJGroupsClasses = Arrays.asList("org.jgroups.JChannel",
"org.jgroups.ReceiverAdapter",
"org.jgroups.ChannelListener");
try {
ClassLoader classLoader = RepositoryConfiguration.class.getClassLoader();
for (String jGroupsClass : requiredJGroupsClasses) {
Class.forName(jGroupsClass, false, classLoader);
}
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* An empty {@link RepositoryConfiguration} that uses all the defaults.
*/
public static final RepositoryConfiguration DEFAULT_CONFIGURATION = new RepositoryConfiguration();
private final String docName;
private final Document doc;
private transient Environment environment = new LocalEnvironment();
private volatile Problems problems = null;
public RepositoryConfiguration() {
this(Schematic.newDocument(), null);
}
public RepositoryConfiguration( String name ) {
this(Schematic.newDocument(), name);
}
public RepositoryConfiguration( Document document,
String documentName ) {
Document replaced = replaceSystemPropertyVariables(document);
this.doc = ensureNamed(replaced, documentName);
this.docName = documentName;
}
public RepositoryConfiguration( String name,
Environment environment ) {
this(Schematic.newDocument(), name != null ? name : Default.DEFAULT);
this.environment = environment;
}
public RepositoryConfiguration( Document document,
String documentName,
Environment environment ) {
Document replaced = replaceSystemPropertyVariables(document);
this.doc = ensureNamed(replaced, documentName);
this.docName = documentName;
this.environment = environment;
}
protected Environment environment() {
return this.environment;
}
public String getName() {
return doc.getString(FieldName.NAME, docName);
}
public Document getDocument() {
return doc;
}
public String getJndiName() {
return doc.getString(FieldName.JNDI_NAME, DEFAULT_JNDI_PREFIX_OF_NAME + getName());
}
public String getStoreName() {
return getCacheName();
}
public String getCacheName() {
Document storage = doc.getDocument(FieldName.STORAGE);
if (storage != null) {
return storage.getString(FieldName.CACHE_NAME, getName());
}
return getName();
}
public String getCacheConfiguration() {
Document storage = doc.getDocument(FieldName.STORAGE);
if (storage != null) {
return storage.getString(FieldName.CACHE_CONFIGURATION);
}
return null;
}
public String getCacheTransactionManagerLookupClassName() {
Document storage = doc.getDocument(FieldName.STORAGE);
if (storage != null) {
return storage.getString(FieldName.CACHE_TRANSACTION_MANAGER_LOOKUP, Default.CACHE_TRANSACTION_MANAGER_LOOKUP);
}
return Default.CACHE_TRANSACTION_MANAGER_LOOKUP;
}
CacheContainer getContentCacheContainer() throws IOException, NamingException {
return getCacheContainer(null);
}
protected CacheContainer getCacheContainer( String config ) throws IOException, NamingException {
if (config == null) config = getCacheConfiguration();
return environment.getCacheContainer(config);
}
@SuppressWarnings( "unchecked" )
protected Class<? extends TransactionManagerLookup> getCacheTransactionManagerLookupClass() {
String txnMgrLookupClassName = getCacheTransactionManagerLookupClassName();
try {
return (Class<TransactionManagerLookup>)getClass().getClassLoader().loadClass(txnMgrLookupClassName);
} catch (ClassNotFoundException e) {
return GenericTransactionManagerLookup.class;
}
}
public BinaryStorage getBinaryStorage() {
Document storage = doc.getDocument(FieldName.STORAGE);
if (storage == null) {
storage = Schematic.newDocument();
}
return new BinaryStorage(storage.getDocument(FieldName.BINARY_STORAGE));
}
public Clustering getClustering() {
return new Clustering(doc.getDocument(FieldName.CLUSTERING));
}
/**
* The binary-storage-related configuration information.
*/
@Immutable
public class BinaryStorage {
private final Document binaryStorage;
protected BinaryStorage( Document binaryStorage ) {
this.binaryStorage = binaryStorage != null ? binaryStorage : EMPTY;
}
public long getMinimumBinarySizeInBytes() {
return binaryStorage.getLong(FieldName.MINIMUM_BINARY_SIZE_IN_BYTES, Default.MINIMUM_BINARY_SIZE_IN_BYTES);
}
public AbstractBinaryStore getBinaryStore() throws NamingException, IOException {
String type = binaryStorage.getString(FieldName.TYPE, "transient");
AbstractBinaryStore store = null;
if (type.equalsIgnoreCase("transient")) {
store = TransientBinaryStore.get();
} else if (type.equalsIgnoreCase("file")) {
String directory = binaryStorage.getString(FieldName.DIRECTORY);
assert directory != null;
File dir = new File(directory);
store = FileSystemBinaryStore.create(dir);
} else if (type.equalsIgnoreCase("database")) {
store = new DatabaseBinaryStore();
} else if (type.equalsIgnoreCase("cache")) {
String cacheName = binaryStorage.getString(FieldName.CACHE_NAME, getName());
String cacheConfiguration = binaryStorage.getString(FieldName.CACHE_CONFIGURATION); // may be null
if (cacheConfiguration == null) cacheConfiguration = getCacheConfiguration();
CacheContainer cacheContainer = getCacheContainer(cacheConfiguration);
// String cacheTransactionManagerLookupClass = binaryStorage.getString(FieldName.CACHE_TRANSACTION_MANAGER_LOOKUP,
// Default.CACHE_TRANSACTION_MANAGER_LOOKUP);
store = new InfinispanBinaryStore(cacheName, cacheContainer);
}
if (store == null) store = TransientBinaryStore.get();
store.setMinimumBinarySizeInBytes(getMinimumBinarySizeInBytes());
return store;
}
}
public boolean isCreatingWorkspacesAllowed() {
Document workspaces = doc.getDocument(FieldName.WORKSPACES);
if (workspaces != null) {
return workspaces.getBoolean(FieldName.ALLOW_CREATION, Default.ALLOW_CREATION);
}
return Default.ALLOW_CREATION;
}
public TransactionMode getTransactionMode() {
String mode = doc.getString(FieldName.TRANSACTION_MODE);
return mode != null ? TransactionMode.valueOf(mode.trim().toUpperCase()) : Default.TRANSACTION_MODE;
}
/**
* Get the name of the workspace that should be used for sessions where the client does not specify the name of the workspace.
*
* @return the default workspace name; never null
*/
public String getDefaultWorkspaceName() {
Document workspaces = doc.getDocument(FieldName.WORKSPACES);
if (workspaces != null) {
return workspaces.getString(FieldName.DEFAULT, Default.DEFAULT);
}
return Default.DEFAULT;
}
/**
* Obtain the names of the workspaces that were listed as being predefined. This includes the name
* {@link #getDefaultWorkspaceName() default workspace}.
*
* @return the set of predefined (non-system) workspace names; never null
*/
public Set<String> getPredefinedWorkspaceNames() {
Set<String> names = new HashSet<String>();
Document workspaces = doc.getDocument(FieldName.WORKSPACES);
if (workspaces != null) {
List<?> predefined = workspaces.getArray(FieldName.PREDEFINED);
if (predefined != null) {
for (Object value : predefined) {
if (value instanceof String) names.add((String)value);
}
}
}
names.add(getDefaultWorkspaceName());
return names;
}
/**
* Obtain all of the workspace names specified by this repository, including the {@link #getPredefinedWorkspaceNames()
* predefined workspaces} and the {@link #getDefaultWorkspaceName() default workspace}. The result does <i>not</i> contain the
* names of any dynamically-created workspaces (e.g., those not specified in the configuration).
*
* @return the set of all workspace names defined by the configuration; never null
*/
public Set<String> getAllWorkspaceNames() {
Set<String> names = getPredefinedWorkspaceNames();
names.add(getDefaultWorkspaceName());
return names;
}
/**
* Get the configuration for the security-related aspects of this repository.
*
* @return the security configuration; never null
*/
public Security getSecurity() {
return new Security(doc.getDocument(FieldName.SECURITY));
}
/**
* The security-related configuration information.
*/
@Immutable
public class Security {
private final Document security;