View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6   * See the AUTHORS.txt file in the distribution for a full listing of 
7   * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.connector.filesystem;
25  
26  import java.io.File;
27  import java.util.LinkedList;
28  import java.util.UUID;
29  import org.modeshape.common.i18n.I18n;
30  import org.modeshape.graph.ExecutionContext;
31  import org.modeshape.graph.JcrNtLexicon;
32  import org.modeshape.graph.ModeShapeLexicon;
33  import org.modeshape.graph.connector.RepositoryContext;
34  import org.modeshape.graph.connector.base.PathNode;
35  import org.modeshape.graph.connector.base.PathTransaction;
36  import org.modeshape.graph.connector.base.Processor;
37  import org.modeshape.graph.connector.base.Repository;
38  import org.modeshape.graph.connector.base.Transaction;
39  import org.modeshape.graph.observe.Observer;
40  import org.modeshape.graph.property.Path;
41  import org.modeshape.graph.property.Property;
42  import org.modeshape.graph.property.Path.Segment;
43  import org.modeshape.graph.request.InvalidRequestException;
44  import org.modeshape.graph.request.InvalidWorkspaceException;
45  import org.modeshape.graph.request.MoveBranchRequest;
46  import org.modeshape.graph.request.processor.RequestProcessor;
47  
48  /**
49   * Implementation of {@code Repository} that provides access to an underlying file system. This repository only natively supports
50   * nodes of primary types {@link JcrNtLexicon#FOLDER nt:folder}, {@link JcrNtLexicon#FILE nt:file}, and
51   * {@link ModeShapeLexicon#RESOURCE mode:resource}, although the {@link CustomPropertiesFactory} allows for the addition of mixin
52   * types to any and all primary types.
53   */
54  public class FileSystemRepository extends Repository<PathNode, FileSystemWorkspace> {
55  
56      protected final FileSystemSource source;
57      private File repositoryRoot;
58  
59      public FileSystemRepository( FileSystemSource source ) {
60          super(source);
61  
62          this.source = source;
63          initialize();
64      }
65  
66      /**
67       * Creates any predefined workspaces, including the default workspace.
68       */
69      @Override
70      protected void initialize() {
71          String repositoryRootPath = source.getWorkspaceRootPath();
72          String sourceName = this.getSourceName();
73  
74          if (repositoryRootPath != null) {
75              this.repositoryRoot = new File(repositoryRootPath);
76              if (!this.repositoryRoot.exists()) {
77                  throw new IllegalStateException(FileSystemI18n.pathForWorkspaceRootDoesNotExist.text(repositoryRootPath,
78                                                                                                       sourceName));
79              }
80              if (!this.repositoryRoot.isDirectory()) {
81                  throw new IllegalStateException(FileSystemI18n.pathForWorkspaceRootIsNotDirectory.text(repositoryRootPath,
82                                                                                                         sourceName));
83              }
84              if (!this.repositoryRoot.canRead()) {
85                  throw new IllegalStateException(FileSystemI18n.pathForWorkspaceRootCannotBeRead.text(repositoryRootPath,
86                                                                                                       sourceName));
87              }
88          }
89  
90          super.initialize();
91      }
92      private void createDirectory( File directory ) {
93          File parent = directory.getParentFile();
94          
95          if (!parent.exists()) {
96              createDirectory(parent);
97          } else if (parent.isFile()) {
98              I18n msg = FileSystemI18n.ancestorInPathIsFile;
99              throw new InvalidWorkspaceException(msg.text(source.getName(), parent.getPath()));
100         }
101 
102         if (!directory.mkdir()) {
103             I18n msg = FileSystemI18n.couldNotCreateDirectory;
104             throw new InvalidWorkspaceException(msg.text(source.getName(), directory.getPath()));
105         }
106     }
107 
108     /**
109      * @param workspaceName the name of the workspace for which the root directory should be returned
110      * @return the directory that maps to the root node in the named workspace; may be null if the directory does not exist, is a
111      *         file, or cannot be read.
112      */
113     protected File getWorkspaceDirectory( String workspaceName ) {
114         if (workspaceName == null) workspaceName = source.getDefaultWorkspaceName();
115 
116         File directory = this.repositoryRoot == null ? new File(workspaceName) : new File(repositoryRoot, workspaceName);
117         if (!directory.exists()) {
118             createDirectory(directory.getAbsoluteFile());
119         }
120 
121         if (!directory.canRead()) {
122             I18n msg = FileSystemI18n.pathForWorkspaceCannotBeRead;
123             throw new InvalidWorkspaceException(msg.text(getSourceName(), directory.getAbsolutePath(), workspaceName));
124         }
125 
126         if (!directory.isDirectory()) {
127             I18n msg = FileSystemI18n.pathForWorkspaceIsNotDirectory;
128             throw new InvalidWorkspaceException(msg.text(getSourceName(), directory.getAbsolutePath(), workspaceName));
129         }
130 
131         return directory;
132     }
133 
134     @Override
135     public FileSystemTransaction startTransaction( ExecutionContext context,
136                                                                         boolean readonly ) {
137         return new FileSystemTransaction(this, source.getRootNodeUuidObject());
138     }
139 
140     @Override
141     public RequestProcessor createRequestProcessor( Transaction<PathNode, FileSystemWorkspace> txn ) {
142         RepositoryContext repositoryContext = this.source.getRepositoryContext();
143         Observer observer = repositoryContext != null ? repositoryContext.getObserver() : null;
144         return new FileSystemProcessor(txn, this, observer, source.areUpdatesAllowed());
145     }
146 
147     /**
148      * Implementation of the {@link PathTransaction} interface for the file system connector
149      */
150     class FileSystemTransaction extends PathTransaction<PathNode, FileSystemWorkspace> {
151 
152         public FileSystemTransaction( FileSystemRepository repository,
153                                       UUID rootNodeUuid ) {
154             super(repository, rootNodeUuid);
155         }
156 
157         @Override
158         protected PathNode createNode( Segment name,
159                                        Path parentPath,
160                                        Iterable<Property> properties ) {
161             return new PathNode(null, parentPath, name, properties, new LinkedList<Segment>());
162         }
163 
164         @Override
165         public boolean destroyWorkspace( FileSystemWorkspace workspace ) throws InvalidWorkspaceException {
166             return true;
167         }
168 
169         @Override
170         public FileSystemWorkspace getWorkspace( String name,
171                                                  FileSystemWorkspace originalToClone ) throws InvalidWorkspaceException {
172             FileSystemRepository repository = FileSystemRepository.this;
173 
174             if (originalToClone != null) {
175                 return new FileSystemWorkspace(name, originalToClone, repository.getWorkspaceDirectory(name));
176             }
177             return new FileSystemWorkspace(repository, name);
178         }
179 
180         @Override
181         protected void validateNode( FileSystemWorkspace workspace,
182                                      PathNode node ) {
183             workspace.validate(node);
184         }
185     }
186 
187     /**
188      * Custom {@link Processor} for the file system connector. This processor throws accurate exceptions on attempts to reorder
189      * nodes, since the file system connector does not support node ordering. Otherwise, it provides default behavior.
190      */
191     class FileSystemProcessor extends Processor<PathNode, FileSystemWorkspace> {
192 
193         public FileSystemProcessor( Transaction<PathNode, FileSystemWorkspace> txn,
194                                     Repository<PathNode, FileSystemWorkspace> repository,
195                                     Observer observer,
196                                     boolean updatesAllowed ) {
197             super(txn, repository, observer, updatesAllowed);
198         }
199 
200         @Override
201         public void process( MoveBranchRequest request ) {
202             if (request.before() != null) {
203                 I18n msg = FileSystemI18n.nodeOrderingNotSupported;
204                 throw new InvalidRequestException(msg.text(source.getName()));
205             }
206             super.process(request);
207         }
208 
209     }
210 }