001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.connector.federation.executor;
023    
024    import java.util.Set;
025    import net.jcip.annotations.NotThreadSafe;
026    import org.jboss.dna.common.util.CheckArg;
027    import org.jboss.dna.connector.federation.Projection;
028    import org.jboss.dna.graph.ExecutionContext;
029    import org.jboss.dna.graph.Location;
030    import org.jboss.dna.graph.connectors.RepositoryConnection;
031    import org.jboss.dna.graph.connectors.RepositoryConnectionFactory;
032    import org.jboss.dna.graph.connectors.RepositorySource;
033    import org.jboss.dna.graph.connectors.RepositorySourceException;
034    import org.jboss.dna.graph.properties.DateTime;
035    import org.jboss.dna.graph.properties.Path;
036    import org.jboss.dna.graph.properties.PathFactory;
037    import org.jboss.dna.graph.properties.PathNotFoundException;
038    import org.jboss.dna.graph.properties.Property;
039    import org.jboss.dna.graph.requests.CopyBranchRequest;
040    import org.jboss.dna.graph.requests.CreateNodeRequest;
041    import org.jboss.dna.graph.requests.DeleteBranchRequest;
042    import org.jboss.dna.graph.requests.MoveBranchRequest;
043    import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
044    import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
045    import org.jboss.dna.graph.requests.ReadNodeRequest;
046    import org.jboss.dna.graph.requests.Request;
047    import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
048    import org.jboss.dna.graph.requests.processor.RequestProcessor;
049    
050    /**
051     * @author Randall Hauch
052     */
053    @NotThreadSafe
054    public class SingleProjectionCommandExecutor extends RequestProcessor {
055    
056        private final Projection projection;
057        private final PathFactory pathFactory;
058        private final RepositoryConnectionFactory connectionFactory;
059        private RepositoryConnection connection;
060    
061        /**
062         * @param context the execution context in which the executor will be run; may not be null
063         * @param sourceName the name of the {@link RepositorySource} that is making use of this executor; may not be null or empty
064         * @param projection the projection used for the cached information; may not be null and must have exactly one
065         *        {@link Projection#getRules() rule}
066         * @param connectionFactory the factory for {@link RepositoryConnection} instances
067         */
068        public SingleProjectionCommandExecutor( ExecutionContext context,
069                                                String sourceName,
070                                                Projection projection,
071                                                RepositoryConnectionFactory connectionFactory ) {
072            this(context, sourceName, null, projection, connectionFactory);
073        }
074    
075        /**
076         * @param context the execution context in which the executor will be run; may not be null
077         * @param sourceName the name of the {@link RepositorySource} that is making use of this executor; may not be null or empty
078         * @param now the current time; may be null if the system time is to be used
079         * @param projection the projection used for the cached information; may not be null and must have exactly one
080         *        {@link Projection#getRules() rule}
081         * @param connectionFactory the factory for {@link RepositoryConnection} instances
082         */
083        public SingleProjectionCommandExecutor( ExecutionContext context,
084                                                String sourceName,
085                                                DateTime now,
086                                                Projection projection,
087                                                RepositoryConnectionFactory connectionFactory ) {
088            super(sourceName, context, now);
089            assert connectionFactory != null;
090            assert projection != null;
091            assert projection.getRules().size() == 1;
092            this.projection = projection;
093            this.connectionFactory = connectionFactory;
094            this.pathFactory = context.getValueFactories().getPathFactory();
095            assert this.pathFactory != null;
096        }
097    
098        protected RepositoryConnection getConnection() throws RepositorySourceException {
099            if (connection == null) {
100                // Create a connection ...
101                connection = this.connectionFactory.createConnection(this.projection.getSourceName());
102            }
103            return connection;
104        }
105    
106        /**
107         * {@inheritDoc}
108         * 
109         * @see RequestProcessor#close()
110         */
111        @Override
112        public void close() {
113            if (this.connection != null) {
114                try {
115                    this.connection.close();
116                } finally {
117                    this.connection = null;
118                }
119            }
120            super.close();
121        }
122    
123        /**
124         * {@inheritDoc}
125         * 
126         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllChildrenRequest)
127         */
128        @Override
129        public void process( ReadAllChildrenRequest request ) {
130            Location locationInSource = projectIntoSource(request.of());
131            ReadAllChildrenRequest projected = new ReadAllChildrenRequest(locationInSource);
132            getConnection().execute(this.getExecutionContext(), projected);
133            if (projected.hasError()) {
134                return;
135            }
136            for (Location child : projected.getChildren()) {
137                request.addChild(child);
138            }
139        }
140    
141        /**
142         * {@inheritDoc}
143         * 
144         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllPropertiesRequest)
145         */
146        @Override
147        public void process( ReadAllPropertiesRequest request ) {
148            Location locationInSource = projectIntoSource(request.at());
149            ReadAllPropertiesRequest projected = new ReadAllPropertiesRequest(locationInSource);
150            getConnection().execute(this.getExecutionContext(), projected);
151            if (projected.hasError()) {
152                projectError(projected, request.at(), request);
153                return;
154            }
155            for (Property property : projected.getProperties()) {
156                request.addProperty(property);
157            }
158        }
159    
160        /**
161         * {@inheritDoc}
162         * 
163         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadNodeRequest)
164         */
165        @Override
166        public void process( ReadNodeRequest request ) {
167            Location locationInSource = projectIntoSource(request.at());
168            ReadNodeRequest projected = new ReadNodeRequest(locationInSource);
169            getConnection().execute(this.getExecutionContext(), projected);
170            if (projected.hasError()) {
171                projectError(projected, request.at(), request);
172                return;
173            }
174            for (Property property : projected.getProperties()) {
175                request.addProperty(property);
176            }
177            for (Location child : projected.getChildren()) {
178                request.addChild(child);
179            }
180        }
181    
182        /**
183         * {@inheritDoc}
184         * 
185         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CreateNodeRequest)
186         */
187        @Override
188        public void process( CreateNodeRequest request ) {
189            Location locationInSource = projectIntoSource(request.at());
190            CreateNodeRequest projected = new CreateNodeRequest(locationInSource, request.properties());
191            getConnection().execute(this.getExecutionContext(), projected);
192            if (projected.hasError()) {
193                projectError(projected, request.at(), request);
194                return;
195            }
196        }
197    
198        /**
199         * {@inheritDoc}
200         * 
201         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.UpdatePropertiesRequest)
202         */
203        @Override
204        public void process( UpdatePropertiesRequest request ) {
205            Location locationInSource = projectIntoSource(request.on());
206            UpdatePropertiesRequest projected = new UpdatePropertiesRequest(locationInSource, request.properties());
207            getConnection().execute(this.getExecutionContext(), projected);
208            if (projected.hasError()) {
209                projectError(projected, request.on(), request);
210                return;
211            }
212        }
213    
214        /**
215         * {@inheritDoc}
216         * 
217         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.DeleteBranchRequest)
218         */
219        @Override
220        public void process( DeleteBranchRequest request ) {
221            Location locationInSource = projectIntoSource(request.at());
222            DeleteBranchRequest projected = new DeleteBranchRequest(locationInSource);
223            getConnection().execute(this.getExecutionContext(), projected);
224            if (projected.hasError()) {
225                projectError(projected, request.at(), request);
226                return;
227            }
228        }
229    
230        /**
231         * {@inheritDoc}
232         * 
233         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.MoveBranchRequest)
234         */
235        @Override
236        public void process( MoveBranchRequest request ) {
237            Location fromLocationInSource = projectIntoSource(request.from());
238            Location intoLocationInSource = projectIntoSource(request.into());
239            MoveBranchRequest projected = new MoveBranchRequest(fromLocationInSource, intoLocationInSource);
240            getConnection().execute(this.getExecutionContext(), projected);
241            if (projected.hasError()) {
242                projectError(projected, null, request);
243                return;
244            }
245        }
246    
247        /**
248         * {@inheritDoc}
249         * 
250         * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CopyBranchRequest)
251         */
252        @Override
253        public void process( CopyBranchRequest request ) {
254            Location fromLocationInSource = projectIntoSource(request.from());
255            Location intoLocationInSource = projectIntoSource(request.into());
256            CopyBranchRequest projected = new CopyBranchRequest(fromLocationInSource, intoLocationInSource);
257            getConnection().execute(this.getExecutionContext(), projected);
258            if (projected.hasError()) {
259                projectError(projected, null, request);
260                return;
261            }
262        }
263    
264        protected Location projectIntoSource( Location pathInRepository ) {
265            Path path = pathInRepository.getPath();
266            CheckArg.isNotNull(path, "pathInRepository.getPath()");
267            Set<Path> paths = this.projection.getPathsInSource(path, pathFactory);
268            if (paths.isEmpty()) return null;
269            Path projectedPath = paths.iterator().next();
270            Location location = null;
271            if (pathInRepository.hasIdProperties()) {
272                location = new Location(projectedPath, pathInRepository.getIdProperties());
273            } else {
274                new Location(projectedPath);
275            }
276            return location;
277        }
278    
279        protected Location projectIntoRepository( Location pathInSource ) {
280            Path path = pathInSource.getPath();
281            CheckArg.isNotNull(path, "pathInSource.getPath()");
282            Path projectedPath = this.projection.getPathsInRepository(path, pathFactory).iterator().next();
283            Location location = null;
284            if (pathInSource.hasIdProperties()) {
285                location = new Location(projectedPath, pathInSource.getIdProperties());
286            } else {
287                new Location(projectedPath);
288            }
289            return location;
290        }
291    
292        protected void projectError( Request original,
293                                     Location originalLocation,
294                                     Request projected ) {
295            Throwable error = original.getError();
296            if (error instanceof PathNotFoundException) {
297                PathNotFoundException pnf = (PathNotFoundException)error;
298                Path lowestExisting = pnf.getLowestAncestorThatDoesExist();
299                if (lowestExisting != null) lowestExisting = projectIntoRepository(new Location(lowestExisting)).getPath();
300                if (originalLocation == null) originalLocation = projectIntoRepository(pnf.getLocation());
301                error = new PathNotFoundException(originalLocation, lowestExisting, pnf.getMessage());
302            }
303            projected.setError(error);
304        }
305    
306    }