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.repository.service;
25  
26  import java.util.Locale;
27  import net.jcip.annotations.GuardedBy;
28  import net.jcip.annotations.ThreadSafe;
29  import org.modeshape.common.i18n.I18n;
30  import org.modeshape.common.util.Logger;
31  import org.modeshape.repository.RepositoryI18n;
32  
33  /**
34   * Simple abstract implementation of the service administrator interface that can be easily subclassed by services that require an
35   * administrative interface.
36   */
37  @ThreadSafe
38  public abstract class AbstractServiceAdministrator implements ServiceAdministrator {
39  
40      private volatile State state;
41      private final I18n serviceName;
42      private final Logger logger;
43  
44      protected AbstractServiceAdministrator( I18n serviceName,
45                                              State initialState ) {
46          assert initialState != null;
47          assert serviceName != null;
48          this.state = initialState;
49          this.serviceName = serviceName;
50          this.logger = Logger.getLogger(getClass());
51      }
52  
53      /**
54       * Return the current state of this service.
55       * 
56       * @return the current state
57       */
58      public State getState() {
59          return this.state;
60      }
61  
62      /**
63       * Set the state of the service. This method does nothing if the desired state matches the current state.
64       * 
65       * @param state the desired state
66       * @return this object for method chaining purposes
67       * @see #setState(String)
68       * @see #start()
69       * @see #pause()
70       * @see #shutdown()
71       */
72      @GuardedBy( "this" )
73      public synchronized ServiceAdministrator setState( State state ) {
74          switch (state) {
75              case STARTED:
76                  start();
77                  break;
78              case PAUSED:
79                  pause();
80                  break;
81              case SHUTDOWN:
82              case TERMINATED:
83                  shutdown();
84                  break;
85          }
86          return this;
87      }
88  
89      /**
90       * Set the state of the service. This method does nothing if the desired state matches the current state.
91       * 
92       * @param state the desired state in string form
93       * @return this object for method chaining purposes
94       * @throws IllegalArgumentException if the specified state string is null or does not match one of the predefined
95       *         {@link ServiceAdministrator.State predefined enumerated values}
96       * @see #setState(State)
97       * @see #start()
98       * @see #pause()
99       * @see #shutdown()
100      */
101     public ServiceAdministrator setState( String state ) {
102         State newState = state == null ? null : State.valueOf(state.toUpperCase());
103         if (newState == null) {
104             throw new IllegalArgumentException(RepositoryI18n.invalidStateString.text(state));
105         }
106         return setState(newState);
107     }
108 
109     /**
110      * Start monitoring and sequence the events. This method can be called multiple times, including after the service is
111      * {@link #pause() paused}. However, once the service is {@link #shutdown() shutdown}, it cannot be started or paused.
112      * 
113      * @return this object for method chaining purposes
114      * @throws IllegalStateException if called when the service has been {@link #shutdown() shutdown}.
115      * @see #pause()
116      * @see #shutdown()
117      * @see #isStarted()
118      */
119     public synchronized ServiceAdministrator start() {
120         switch (this.state) {
121             case STARTED:
122                 break;
123             case PAUSED:
124                 logger.trace("Starting \"{0}\"", getServiceName());
125                 doStart(this.state);
126                 this.state = State.STARTED;
127                 logger.trace("Started \"{0}\"", getServiceName());
128                 break;
129             case SHUTDOWN:
130             case TERMINATED:
131                 throw new IllegalStateException(RepositoryI18n.serviceShutdowAndMayNotBeStarted.text(getServiceName()));
132         }
133         return this;
134     }
135 
136     /**
137      * Implementation of the functionality to switch to the started state. This method is only called if the state from which the
138      * service is transitioning is appropriate ({@link ServiceAdministrator.State#PAUSED}). This method does nothing by default,
139      * and should be overridden if needed.
140      * 
141      * @param fromState the state from which this service is transitioning; never null
142      * @throws IllegalStateException if the service is such that it cannot be transitioned from the supplied state
143      */
144     @GuardedBy( "this" )
145     protected void doStart( State fromState ) {
146     }
147 
148     /**
149      * Temporarily stop monitoring and sequencing events. This method can be called multiple times, including after the service is
150      * {@link #start() started}. However, once the service is {@link #shutdown() shutdown}, it cannot be started or paused.
151      * 
152      * @return this object for method chaining purposes
153      * @throws IllegalStateException if called when the service has been {@link #shutdown() shutdown}.
154      * @see #start()
155      * @see #shutdown()
156      * @see #isPaused()
157      */
158     public synchronized ServiceAdministrator pause() {
159         switch (this.state) {
160             case STARTED:
161                 logger.trace("Pausing \"{0}\"", getServiceName());
162                 doPause(this.state);
163                 this.state = State.PAUSED;
164                 logger.trace("Paused \"{0}\"", getServiceName());
165                 break;
166             case PAUSED:
167                 break;
168             case SHUTDOWN:
169             case TERMINATED:
170                 throw new IllegalStateException(RepositoryI18n.serviceShutdowAndMayNotBePaused.text(getServiceName()));
171         }
172         return this;
173     }
174 
175     /**
176      * Implementation of the functionality to switch to the paused state. This method is only called if the state from which the
177      * service is transitioning is appropriate ({@link ServiceAdministrator.State#STARTED}). This method does nothing by default,
178      * and should be overridden if needed.
179      * 
180      * @param fromState the state from which this service is transitioning; never null
181      * @throws IllegalStateException if the service is such that it cannot be transitioned from the supplied state
182      */
183     @GuardedBy( "this" )
184     protected void doPause( State fromState ) {
185     }
186 
187     /**
188      * Permanently stop monitoring and sequencing events. This method can be called multiple times, but only the first call has an
189      * effect. Once the service has been shutdown, it may not be {@link #start() restarted} or {@link #pause() paused}.
190      * 
191      * @return this object for method chaining purposes
192      * @see #start()
193      * @see #pause()
194      * @see #isShutdown()
195      */
196     public synchronized ServiceAdministrator shutdown() {
197         switch (this.state) {
198             case STARTED:
199             case PAUSED:
200                 logger.trace("Initiating shutdown of \"{0}\"", getServiceName());
201                 this.state = State.SHUTDOWN;
202                 doShutdown(this.state);
203                 logger.trace("Initiated shutdown of \"{0}\"", getServiceName());
204                 isTerminated();
205                 break;
206             case SHUTDOWN:
207             case TERMINATED:
208                 isTerminated();
209                 break;
210         }
211         return this;
212     }
213 
214     /**
215      * Implementation of the functionality to switch to the shutdown state. This method is only called if the state from which the
216      * service is transitioning is appropriate ({@link ServiceAdministrator.State#STARTED} or
217      * {@link ServiceAdministrator.State#PAUSED}). This method does nothing by default, and should be overridden if needed.
218      * 
219      * @param fromState the state from which this service is transitioning; never null
220      * @throws IllegalStateException if the service is such that it cannot be transitioned from the supplied state
221      */
222     @GuardedBy( "this" )
223     protected void doShutdown( State fromState ) {
224     }
225 
226     /**
227      * Return whether this service has been started and is currently running.
228      * 
229      * @return true if started and currently running, or false otherwise
230      * @see #start()
231      * @see #pause()
232      * @see #isPaused()
233      * @see #isShutdown()
234      */
235     public boolean isStarted() {
236         return this.state == State.STARTED;
237     }
238 
239     /**
240      * Return whether this service is currently paused.
241      * 
242      * @return true if currently paused, or false otherwise
243      * @see #pause()
244      * @see #start()
245      * @see #isStarted()
246      * @see #isShutdown()
247      */
248     public boolean isPaused() {
249         return this.state == State.PAUSED;
250     }
251 
252     /**
253      * Return whether this service is stopped and unable to be restarted.
254      * 
255      * @return true if currently shutdown, or false otherwise
256      * @see #shutdown()
257      * @see #isPaused()
258      * @see #isStarted()
259      */
260     public boolean isShutdown() {
261         return this.state == State.SHUTDOWN || this.state == State.TERMINATED;
262     }
263 
264     /**
265      * {@inheritDoc}
266      */
267     public boolean isTerminated() {
268         switch (this.state) {
269             case PAUSED:
270             case STARTED:
271             case SHUTDOWN:
272                 if (doCheckIsTerminated()) {
273                     this.state = State.TERMINATED;
274                     logger.trace("Service \"{0}\" has terminated", getServiceName());
275                     return true;
276                 }
277                 return false;
278             case TERMINATED:
279                 return true;
280         }
281         return false;
282     }
283 
284     /**
285      * Subclasses should implement this method to determine whether the service has completed shutdown.
286      * 
287      * @return true if terminated, or false otherwise
288      */
289     protected abstract boolean doCheckIsTerminated();
290 
291     /**
292      * Get the name of this service in the current locale.
293      * 
294      * @return the service name
295      */
296     public String getServiceName() {
297         return this.serviceName.text();
298     }
299 
300     /**
301      * Get the name of this service in the specified locale.
302      * 
303      * @param locale the locale in which the service name is to be returned; may be null if the default locale is to be used
304      * @return the service name
305      */
306     public String getServiceName( Locale locale ) {
307         return this.serviceName.text(locale);
308     }
309 
310 }