001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006     * See the AUTHORS.txt file in the distribution for a full listing of 
007     * individual contributors. 
008     *
009     * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010     * is licensed to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     *
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.dna.graph.request;
025    
026    import java.io.Serializable;
027    import java.util.concurrent.atomic.AtomicBoolean;
028    import org.jboss.dna.graph.GraphI18n;
029    import org.jboss.dna.graph.connector.RepositoryConnection;
030    
031    /**
032     * The abstract base class for all classes representing requests to be executed against a {@link RepositoryConnection}.
033     * 
034     * @author Randall Hauch
035     */
036    public abstract class Request implements Serializable {
037    
038        private static final long serialVersionUID = 1L;
039    
040        private Throwable error;
041        private AtomicBoolean cancelled = new AtomicBoolean(false);
042        private final AtomicBoolean frozen = new AtomicBoolean(false);
043    
044        protected Request() {
045        }
046    
047        /**
048         * Set the error for this request.
049         * 
050         * @param error the error to be associated with this request, or null if this request is to have no error
051         * @throws IllegalStateException if the request is frozen
052         */
053        public void setError( Throwable error ) {
054            checkNotFrozen();
055            this.error = error;
056        }
057    
058        /**
059         * Return whether there is an error associated with this request
060         * 
061         * @return true if there is an error, or false otherwise
062         */
063        public boolean hasError() {
064            return this.error != null;
065        }
066    
067        /**
068         * Get the error associated with this request, if there is such an error.
069         * 
070         * @return the error, or null if there is none
071         */
072        public Throwable getError() {
073            return error;
074        }
075    
076        /**
077         * Check whether this request has been cancelled. Although it is a recommendation that the result of this method be followed
078         * wherever possible, it is not required to immediately stop processing the request if this method returns <code>true</code>.
079         * For example, if processing is almost complete, it may be appropriate to simply finish processing the request.
080         * <p>
081         * This method is safe to be called by different threads.
082         * </p>
083         * 
084         * @return true if this request has been cancelled, or false otherwise.
085         */
086        public boolean isCancelled() {
087            return cancelled.get();
088        }
089    
090        /**
091         * Set the cancelled state of this request. All requests are initially marked as not cancelled. Note that this is designed so
092         * that the same {@link AtomicBoolean} instance can be passed to multiple requests, allowing a single flag to dictate the
093         * cancelled state of all of those requests.
094         * <p>
095         * So, by default, each request should already be set up to not be cancelled, so for most cases this method does not need to
096         * be called at all. This method should be called when this flag is to be shared among multiple requests, usually when the
097         * requests are being initialized or assembled.
098         * </p>
099         * 
100         * @param cancelled the new (potentially shared) cancelled state for the request; may not be null
101         */
102        /*package*/void setCancelledFlag( AtomicBoolean cancelled ) {
103            assert cancelled != null;
104            this.cancelled = cancelled;
105        }
106    
107        /**
108         * Get this request's cancelled flag.
109         * 
110         * @return the cancelled flag
111         */
112        /*package*/AtomicBoolean getCancelledFlag() {
113            return cancelled;
114        }
115    
116        /**
117         * Cancel this request. After this method is called, the {@link #isCancelled() cancellation flag} is set, and any current or
118         * future processing of the request may be affected by the cancellation. (Note however, that processors may choose to not
119         * respect this request.)
120         * <p>
121         * This method is safe to be called by different threads.
122         * </p>
123         * 
124         * @throws IllegalStateException if the request is frozen
125         */
126        public void cancel() {
127            checkNotFrozen();
128            this.cancelled.set(true);
129        }
130    
131        /**
132         * Return whether this request only reads information.
133         * 
134         * @return true if this request reads information, or false if it requests that the repository content be changed in some way
135         */
136        public abstract boolean isReadOnly();
137    
138        /**
139         * Determine whether this request has been frozen, preventing any further updates.
140         * 
141         * @return true if the request has been frozen, or false otherwise
142         */
143        public boolean isFrozen() {
144            return frozen.get();
145        }
146    
147        /**
148         * Freeze this request to prevent any further modification. This method does nothing if the request is already frozen.
149         */
150        public void freeze() {
151            frozen.set(true);
152        }
153    
154        /**
155         * Utility method to check that the request is not frozen, and if it is to throw an {@link IllegalStateException}.
156         * 
157         * @throws IllegalStateException if the request is frozen
158         */
159        protected void checkNotFrozen() throws IllegalStateException {
160            if (frozen.get()) {
161                throw new IllegalStateException(GraphI18n.requestIsFrozenAndMayNotBeChanged.text(this));
162            }
163        }
164    }