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 package org.modeshape.connector.filesystem;
26
27 import java.io.File;
28 import java.io.FilenameFilter;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.Hashtable;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.regex.Pattern;
35 import javax.naming.Context;
36 import javax.naming.Reference;
37 import javax.naming.StringRefAddr;
38 import javax.naming.spi.ObjectFactory;
39 import net.jcip.annotations.ThreadSafe;
40 import org.modeshape.common.annotation.Category;
41 import org.modeshape.common.annotation.Description;
42 import org.modeshape.common.annotation.Label;
43 import org.modeshape.common.i18n.I18n;
44 import org.modeshape.common.util.CheckArg;
45 import org.modeshape.common.util.Logger;
46 import org.modeshape.common.util.StringUtil;
47 import org.modeshape.connector.filesystem.FileSystemRepository.FileSystemTransaction;
48 import org.modeshape.graph.ExecutionContext;
49 import org.modeshape.graph.connector.RepositoryConnection;
50 import org.modeshape.graph.connector.RepositorySource;
51 import org.modeshape.graph.connector.RepositorySourceCapabilities;
52 import org.modeshape.graph.connector.RepositorySourceException;
53 import org.modeshape.graph.connector.base.AbstractRepositorySource;
54 import org.modeshape.graph.connector.base.Connection;
55 import org.modeshape.graph.connector.base.PathNode;
56 import org.modeshape.graph.property.Binary;
57 import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
58
59
60
61
62
63
64 @ThreadSafe
65 public class FileSystemSource extends AbstractRepositorySource implements ObjectFactory {
66
67
68
69
70 private static final long serialVersionUID = 1L;
71
72
73
74
75 public static final String DEFAULT_NAME_OF_DEFAULT_WORKSPACE = "default";
76
77 protected static final String SOURCE_NAME = "sourceName";
78 protected static final String DEFAULT_WORKSPACE = "defaultWorkspace";
79 protected static final String WORKSPACE_ROOT = "workspaceRootPath";
80 protected static final String PREDEFINED_WORKSPACE_NAMES = "predefinedWorkspaceNames";
81 protected static final String ALLOW_CREATING_WORKSPACES = "allowCreatingWorkspaces";
82 protected static final String MAX_PATH_LENGTH = "maxPathLength";
83 protected static final String EXCLUSION_PATTERN = "exclusionPattern";
84 protected static final String INCLUSION_PATTERN = "inclusionPattern";
85 protected static final String FILENAME_FILTER = "filenameFilter";
86 protected static final String CUSTOM_PROPERTY_FACTORY = "customPropertyFactory";
87 protected static final String EAGER_FILE_LOADING = "eagerFileLoading";
88 protected static final String DETERMINE_MIME_TYPE_USING_CONTENT = "determineMimeTypeUsingContent";
89 protected static final String EXTRA_PROPERTIES = "extraProperties";
90
91
92
93
94 protected static final boolean SUPPORTS_EVENTS = true;
95
96
97
98 protected static final boolean SUPPORTS_SAME_NAME_SIBLINGS = true;
99
100
101
102 protected static final boolean DEFAULT_SUPPORTS_CREATING_WORKSPACES = true;
103
104
105
106 public static final boolean DEFAULT_SUPPORTS_UPDATES = false;
107
108
109
110
111 public static final String DEFAULT_EXTRA_PROPERTIES = "log";
112
113
114
115
116 protected static final boolean SUPPORTS_REFERENCES = false;
117
118
119
120
121 public static final boolean DEFAULT_EAGER_FILE_LOADING = false;
122
123
124
125
126 public static final boolean DEFAULT_DETERMINE_MIME_TYPE_USING_CONTENT = false;
127
128 public static final int DEFAULT_MAX_PATH_LENGTH = 255;
129 public static final String DEFAULT_EXCLUSION_PATTERN = null;
130 public static final String DEFAULT_INCLUSION_PATTERN = null;
131 public static final FilenameFilter DEFAULT_FILENAME_FILTER = null;
132 private static final FilenameFilter ACCEPT_ALL_FILTER = new InclusionExclusionFilenameFilter();
133
134 protected static Map<String, CustomPropertiesFactory> EXTRA_PROPERTIES_CLASSNAME_BY_KEY;
135
136 static {
137 Map<String, CustomPropertiesFactory> byKey = new HashMap<String, CustomPropertiesFactory>();
138 byKey.put(DEFAULT_EXTRA_PROPERTIES, new LogProperties(Logger.getLogger(FileSystemSource.class)));
139 byKey.put("store", new StoreProperties());
140 byKey.put("error", new ThrowProperties());
141 byKey.put("ignore", new IgnoreProperties());
142 EXTRA_PROPERTIES_CLASSNAME_BY_KEY = Collections.unmodifiableMap(byKey);
143 }
144
145 @Description( i18n = FileSystemI18n.class, value = "defaultWorkspaceNamePropertyDescription" )
146 @Label( i18n = FileSystemI18n.class, value = "defaultWorkspaceNamePropertyLabel" )
147 @Category( i18n = FileSystemI18n.class, value = "defaultWorkspaceNamePropertyCategory" )
148 private volatile String defaultWorkspaceName = DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
149
150 @Description( i18n = FileSystemI18n.class, value = "workspaceRootPathPropertyDescription" )
151 @Label( i18n = FileSystemI18n.class, value = "workspaceRootPathPropertyLabel" )
152 @Category( i18n = FileSystemI18n.class, value = "workspaceRootPathPropertyCategory" )
153 private volatile String workspaceRootPath;
154
155 @Description( i18n = FileSystemI18n.class, value = "predefinedWorkspacesPropertyDescription" )
156 @Label( i18n = FileSystemI18n.class, value = "predefinedWorkspacesPropertyLabel" )
157 @Category( i18n = FileSystemI18n.class, value = "predefinedWorkspacesPropertyCategory" )
158 private volatile String[] predefinedWorkspaces = new String[] {};
159
160 @Description( i18n = FileSystemI18n.class, value = "maxPathLengthPropertyDescription" )
161 @Label( i18n = FileSystemI18n.class, value = "maxPathLengthPropertyLabel" )
162 @Category( i18n = FileSystemI18n.class, value = "maxPathLengthPropertyCategory" )
163 private volatile int maxPathLength = DEFAULT_MAX_PATH_LENGTH;
164
165 @Description( i18n = FileSystemI18n.class, value = "eagerFileLoadingPropertyDescription" )
166 @Label( i18n = FileSystemI18n.class, value = "eagerFileLoadingPropertyLabel" )
167 @Category( i18n = FileSystemI18n.class, value = "eagerFileLoadingPropertyCategory" )
168 private volatile boolean eagerFileLoading = DEFAULT_EAGER_FILE_LOADING;
169
170 @Description( i18n = FileSystemI18n.class, value = "determineMimeTypeUsingContentPropertyDescription" )
171 @Label( i18n = FileSystemI18n.class, value = "determineMimeTypeUsingContentPropertyLabel" )
172 @Category( i18n = FileSystemI18n.class, value = "determineMimeTypeUsingContentPropertyCategory" )
173 private volatile boolean determineMimeTypeUsingContent = DEFAULT_DETERMINE_MIME_TYPE_USING_CONTENT;
174
175 @Description( i18n = FileSystemI18n.class, value = "extraPropertiesPropertyDescription" )
176 @Label( i18n = FileSystemI18n.class, value = "extraPropertiesPropertyLabel" )
177 @Category( i18n = FileSystemI18n.class, value = "extraPropertiesPropertyCategory" )
178 private volatile String extraProperties = DEFAULT_EXTRA_PROPERTIES;
179
180 private volatile FilenameFilter filenameFilter = DEFAULT_FILENAME_FILTER;
181 private volatile InclusionExclusionFilenameFilter inclusionExclusionFilenameFilter = new InclusionExclusionFilenameFilter();
182
183 private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(
184 SUPPORTS_SAME_NAME_SIBLINGS,
185 DEFAULT_SUPPORTS_UPDATES,
186 SUPPORTS_EVENTS,
187 DEFAULT_SUPPORTS_CREATING_WORKSPACES,
188 SUPPORTS_REFERENCES);
189 private transient FileSystemRepository repository;
190 private volatile CustomPropertiesFactory customPropertiesFactory;
191
192 private ExecutionContext defaultContext = new ExecutionContext();
193
194
195
196
197 public FileSystemSource() {
198 }
199
200
201
202
203
204
205 public RepositorySourceCapabilities getCapabilities() {
206 return capabilities;
207 }
208
209
210
211
212
213
214 public boolean getUpdatesAllowed() {
215 return capabilities.supportsUpdates();
216 }
217
218
219
220
221
222
223
224 public String getWorkspaceRootPath() {
225 return workspaceRootPath;
226 }
227
228
229
230
231
232
233
234 public synchronized void setWorkspaceRootPath( String workspaceRootPath ) {
235 this.workspaceRootPath = workspaceRootPath;
236 }
237
238
239
240
241
242
243
244 @Description( i18n = FileSystemI18n.class, value = "exclusionPatternPropertyDescription" )
245 @Label( i18n = FileSystemI18n.class, value = "exclusionPatternPropertyLabel" )
246 @Category( i18n = FileSystemI18n.class, value = "exclusionPatternPropertyCategory" )
247 public String getExclusionPattern() {
248 return this.inclusionExclusionFilenameFilter.getExclusionPattern();
249 }
250
251
252
253
254
255
256
257
258
259
260
261 public synchronized void setExclusionPattern( String exclusionPattern ) {
262 this.inclusionExclusionFilenameFilter.setExclusionPattern(exclusionPattern);
263 }
264
265
266
267
268
269
270
271 @Description( i18n = FileSystemI18n.class, value = "inclusionPatternPropertyDescription" )
272 @Label( i18n = FileSystemI18n.class, value = "inclusionPatternPropertyLabel" )
273 @Category( i18n = FileSystemI18n.class, value = "inclusionPatternPropertyCategory" )
274 public String getInclusionPattern() {
275 return this.inclusionExclusionFilenameFilter.getInclusionPattern();
276 }
277
278
279
280
281
282
283
284
285
286
287
288 public synchronized void setInclusionPattern( String inclusionPattern ) {
289 this.inclusionExclusionFilenameFilter.setInclusionPattern(inclusionPattern);
290 }
291
292
293
294
295
296 public FilenameFilter getFilenameFilter() {
297 return this.filenameFilter;
298 }
299
300
301
302
303
304
305
306
307
308
309
310 public synchronized void setFilenameFilter( FilenameFilter filenameFilter ) {
311 this.filenameFilter = filenameFilter;
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331 public synchronized void setFilenameFilter( String filenameFilterClassName )
332 throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException {
333 if (filenameFilterClassName == null) {
334 this.filenameFilter = null;
335 return;
336 }
337
338 Class<?> filenameFilterClass = Class.forName(filenameFilterClassName);
339
340 this.filenameFilter = (FilenameFilter)filenameFilterClass.newInstance();
341 }
342
343 FilenameFilter filenameFilter( boolean hideFilesForCustomProperties ) {
344 if (this.filenameFilter != null) return this.filenameFilter;
345 if (this.getInclusionPattern() != null || this.getExclusionPattern() != null) return this.inclusionExclusionFilenameFilter;
346
347
348 FilenameFilter filenameFilter = null;
349 final String filterPattern = this.inclusionExclusionFilenameFilter.getExclusionPattern();
350 if (filterPattern != null) {
351 filenameFilter = new FilenameFilter() {
352 Pattern filter = Pattern.compile(filterPattern);
353
354 public boolean accept( File dir,
355 String name ) {
356 return !filter.matcher(name).matches();
357 }
358 };
359 }
360
361 if (hideFilesForCustomProperties) {
362
363 CustomPropertiesFactory customPropsFactory = customPropertiesFactory();
364 if (customPropsFactory instanceof BasePropertiesFactory) {
365 filenameFilter = ((BasePropertiesFactory)customPropsFactory).getFilenameFilter(filenameFilter);
366 }
367 }
368
369
370 if (filenameFilter == null) filenameFilter = ACCEPT_ALL_FILTER;
371
372 return filenameFilter;
373 }
374
375
376
377
378
379
380 public int getMaxPathLength() {
381 return maxPathLength;
382 }
383
384
385
386
387
388
389
390
391
392
393
394
395 public synchronized void setMaxPathLength( int maxPathLength ) {
396 CheckArg.isNonNegative(maxPathLength, "maxPathLength");
397 this.maxPathLength = maxPathLength;
398 }
399
400
401
402
403
404
405 public String getDefaultWorkspaceName() {
406 return defaultWorkspaceName;
407 }
408
409
410
411
412
413
414
415 public synchronized void setDefaultWorkspaceName( String nameOfDefaultWorkspace ) {
416 this.defaultWorkspaceName = nameOfDefaultWorkspace != null ? nameOfDefaultWorkspace : DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
417 }
418
419
420
421
422
423
424
425
426
427 public synchronized String[] getPredefinedWorkspaceNames() {
428 String[] copy = new String[predefinedWorkspaces.length];
429 System.arraycopy(predefinedWorkspaces, 0, copy, 0, predefinedWorkspaces.length);
430 return copy;
431 }
432
433
434
435
436
437
438
439
440
441
442 public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspaceNames ) {
443 this.predefinedWorkspaces = predefinedWorkspaceNames;
444 }
445
446
447
448
449
450
451
452
453
454 public boolean isCreatingWorkspacesAllowed() {
455 return capabilities.supportsCreatingWorkspaces();
456 }
457
458
459
460
461
462
463
464
465
466
467 public synchronized void setCreatingWorkspacesAllowed( boolean allowWorkspaceCreation ) {
468 capabilities = new RepositorySourceCapabilities(capabilities.supportsSameNameSiblings(), capabilities.supportsUpdates(),
469 capabilities.supportsEvents(), allowWorkspaceCreation,
470 capabilities.supportsReferences());
471 }
472
473
474
475
476
477
478
479 @Description( i18n = FileSystemI18n.class, value = "updatesAllowedPropertyDescription" )
480 @Label( i18n = FileSystemI18n.class, value = "updatesAllowedPropertyLabel" )
481 @Category( i18n = FileSystemI18n.class, value = "updatesAllowedPropertyCategory" )
482 @Override
483 public boolean areUpdatesAllowed() {
484 return capabilities.supportsUpdates();
485 }
486
487
488
489
490
491
492
493
494 public synchronized void setUpdatesAllowed( boolean allowUpdates ) {
495 capabilities = new RepositorySourceCapabilities(capabilities.supportsSameNameSiblings(), allowUpdates,
496 capabilities.supportsEvents(), capabilities.supportsCreatingWorkspaces(),
497 capabilities.supportsReferences());
498 }
499
500
501
502
503
504
505 public boolean isContentUsedToDetermineMimeType() {
506 return determineMimeTypeUsingContent;
507 }
508
509
510
511
512
513
514 public void setContentUsedToDetermineMimeType( boolean contentUsedToDetermineMimeType ) {
515 determineMimeTypeUsingContent = contentUsedToDetermineMimeType;
516 }
517
518
519
520
521
522
523
524 public String getExtraPropertiesBehavior() {
525 return extraProperties;
526 }
527
528
529
530
531
532
533
534
535 public void setExtraPropertiesBehavior( String behavior ) {
536 if (behavior != null) behavior = behavior.trim().toLowerCase();
537 if (EXTRA_PROPERTIES_CLASSNAME_BY_KEY.containsKey(behavior)) {
538 this.extraProperties = behavior;
539 } else {
540 this.extraProperties = DEFAULT_EXTRA_PROPERTIES;
541 }
542 }
543
544
545
546
547
548
549 public synchronized CustomPropertiesFactory getCustomPropertiesFactory() {
550 return customPropertiesFactory;
551 }
552
553 CustomPropertiesFactory customPropertiesFactory() {
554 if (customPropertiesFactory == null) {
555 customPropertiesFactory = EXTRA_PROPERTIES_CLASSNAME_BY_KEY.get(extraProperties);
556 if (customPropertiesFactory == null) {
557 customPropertiesFactory = EXTRA_PROPERTIES_CLASSNAME_BY_KEY.get(DEFAULT_EXTRA_PROPERTIES);
558 }
559 assert customPropertiesFactory != null;
560 }
561 return customPropertiesFactory;
562 }
563
564
565
566
567
568
569
570 public synchronized void setCustomPropertiesFactory( CustomPropertiesFactory customPropertiesFactory ) {
571 this.customPropertiesFactory = customPropertiesFactory;
572 }
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590 public synchronized void setCustomPropertiesFactory( String customPropertiesFactoryClassName )
591 throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException {
592 if (customPropertiesFactoryClassName == null) {
593 this.customPropertiesFactory = null;
594 return;
595 }
596
597 Class<?> customPropertiesFactoryClass = Class.forName(customPropertiesFactoryClassName);
598 this.customPropertiesFactory = (CustomPropertiesFactory)customPropertiesFactoryClass.newInstance();
599 }
600
601
602
603
604
605
606
607
608
609 public boolean isEagerFileLoading() {
610 return eagerFileLoading;
611 }
612
613
614
615
616 public void setEagerFileLoading( boolean eagerFileLoading ) {
617 this.eagerFileLoading = eagerFileLoading;
618 }
619
620
621
622
623
624
625 public synchronized Reference getReference() {
626 String className = getClass().getName();
627 String factoryClassName = this.getClass().getName();
628 Reference ref = new Reference(className, factoryClassName, null);
629
630 if (getName() != null) {
631 ref.add(new StringRefAddr(SOURCE_NAME, getName()));
632 }
633 ref.add(new StringRefAddr(DEFAULT_WORKSPACE, getDefaultWorkspaceName()));
634 ref.add(new StringRefAddr(ALLOW_CREATING_WORKSPACES, Boolean.toString(isCreatingWorkspacesAllowed())));
635 ref.add(new StringRefAddr(MAX_PATH_LENGTH, String.valueOf(maxPathLength)));
636 ref.add(new StringRefAddr(EXTRA_PROPERTIES, String.valueOf(extraProperties)));
637 String[] workspaceNames = getPredefinedWorkspaceNames();
638 if (workspaceNames != null && workspaceNames.length != 0) {
639 ref.add(new StringRefAddr(PREDEFINED_WORKSPACE_NAMES, StringUtil.combineLines(workspaceNames)));
640 }
641 if (getCustomPropertiesFactory() != null) {
642 ref.add(new StringRefAddr(CUSTOM_PROPERTY_FACTORY, getCustomPropertiesFactory().getClass().getName()));
643 }
644 if (this.inclusionExclusionFilenameFilter.getExclusionPattern() != null) {
645 ref.add(new StringRefAddr(EXCLUSION_PATTERN, this.inclusionExclusionFilenameFilter.getExclusionPattern()));
646 }
647 if (this.inclusionExclusionFilenameFilter.getInclusionPattern() != null) {
648 ref.add(new StringRefAddr(INCLUSION_PATTERN, this.inclusionExclusionFilenameFilter.getInclusionPattern()));
649 }
650 if (filenameFilter != null) {
651 ref.add(new StringRefAddr(FILENAME_FILTER, filenameFilter.getClass().getName()));
652 }
653 ref.add(new StringRefAddr(EAGER_FILE_LOADING, Boolean.toString(isEagerFileLoading())));
654 ref.add(new StringRefAddr(DETERMINE_MIME_TYPE_USING_CONTENT, Boolean.toString(isContentUsedToDetermineMimeType())));
655
656 return ref;
657 }
658
659
660
661
662 public Object getObjectInstance( Object obj,
663 javax.naming.Name name,
664 Context nameCtx,
665 Hashtable<?, ?> environment ) throws Exception {
666 if (obj instanceof Reference) {
667 Map<String, Object> values = valuesFrom((Reference)obj);
668
669 String sourceName = (String)values.get(SOURCE_NAME);
670 String defaultWorkspace = (String)values.get(DEFAULT_WORKSPACE);
671 String createWorkspaces = (String)values.get(ALLOW_CREATING_WORKSPACES);
672 String exclusionPattern = (String)values.get(EXCLUSION_PATTERN);
673 String inclusionPattern = (String)values.get(INCLUSION_PATTERN);
674 String filenameFilterClassName = (String)values.get(FILENAME_FILTER);
675 String maxPathLength = (String)values.get(DEFAULT_MAX_PATH_LENGTH);
676 String customPropertiesFactoryClassName = (String)values.get(CUSTOM_PROPERTY_FACTORY);
677 String extraPropertiesBehavior = (String)values.get(EXTRA_PROPERTIES);
678 String eagerFileLoading = (String)values.get(EAGER_FILE_LOADING);
679 String useContentForMimeType = (String)values.get(DETERMINE_MIME_TYPE_USING_CONTENT);
680
681 String combinedWorkspaceNames = (String)values.get(PREDEFINED_WORKSPACE_NAMES);
682 String[] workspaceNames = null;
683 if (combinedWorkspaceNames != null) {
684 List<String> paths = StringUtil.splitLines(combinedWorkspaceNames);
685 workspaceNames = paths.toArray(new String[paths.size()]);
686 }
687
688
689 FileSystemSource source = new FileSystemSource();
690 if (sourceName != null) source.setName(sourceName);
691 if (defaultWorkspace != null) source.setDefaultWorkspaceName(defaultWorkspace);
692 if (createWorkspaces != null) source.setCreatingWorkspacesAllowed(Boolean.parseBoolean(createWorkspaces));
693 if (workspaceNames != null && workspaceNames.length != 0) source.setPredefinedWorkspaceNames(workspaceNames);
694 if (exclusionPattern != null) source.setExclusionPattern(exclusionPattern);
695 if (inclusionPattern != null) source.setInclusionPattern(inclusionPattern);
696 if (filenameFilterClassName != null) source.setFilenameFilter(filenameFilterClassName);
697 if (maxPathLength != null) source.setMaxPathLength(Integer.valueOf(maxPathLength));
698 if (extraPropertiesBehavior != null) source.setExtraPropertiesBehavior(extraPropertiesBehavior);
699 if (customPropertiesFactoryClassName != null) source.setCustomPropertiesFactory(customPropertiesFactoryClassName);
700 if (eagerFileLoading != null) source.setEagerFileLoading(Boolean.parseBoolean(eagerFileLoading));
701 if (useContentForMimeType != null) source.setContentUsedToDetermineMimeType(Boolean.parseBoolean(useContentForMimeType));
702 return source;
703 }
704 return null;
705 }
706
707
708
709
710
711
712 public synchronized RepositoryConnection getConnection() throws RepositorySourceException {
713 String sourceName = getName();
714 if (sourceName == null || sourceName.trim().length() == 0) {
715 I18n msg = FileSystemI18n.propertyIsRequired;
716 throw new RepositorySourceException(getName(), msg.text("name"));
717 }
718
719 if (repository == null) {
720 repository = new FileSystemRepository(this);
721
722 ExecutionContext context = repositoryContext != null ? repositoryContext.getExecutionContext() : defaultContext;
723 FileSystemTransaction txn = repository.startTransaction(context, false);
724 try {
725
726 for (String initialName : getPredefinedWorkspaceNames()) {
727 repository.createWorkspace(txn, initialName, CreateConflictBehavior.DO_NOT_CREATE, null);
728 }
729 } finally {
730 txn.commit();
731 }
732
733 }
734 return new Connection<PathNode, FileSystemWorkspace>(this, repository);
735 }
736 }