View Javadoc

1   /*
2    * Copyright (C) 2006  Tom Gibara
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   */
18  package com.tomgibara.pronto.control.impl;
19  
20  import java.util.Set;
21  import java.util.logging.Level;
22  
23  import com.tomgibara.pronto.control.SignalControllerSettings;
24  import com.tomgibara.pronto.state.PathType;
25  import com.tomgibara.pronto.state.ProntoStateException;
26  import com.tomgibara.pronto.state.StateEngine;
27  import com.tomgibara.pronto.util.Waiter;
28  
29  /**
30   * A ControllerPart implementation that provides functionality for the
31   * SignalControllerSettings interface.
32   * 
33   * @param <S>
34   *            the type of state
35   * @param <L>
36   *            the type of label
37   * @param <P>
38   *            the type of parameter
39   * 
40   * @author Tom Gibara
41   * 
42   */
43  
44  public class SignalControllerPart<S, L, P> implements ControllerPart<S, L, P> {
45  
46      // fields
47  
48      /**
49       * The lock that must be held when performing any action that is dependent
50       * upon the object's lifespan.
51       */
52      private final Object lock = new Object();
53  
54      /**
55       * The controller that aggregates this part.
56       */
57      private final ControllerImpl<S, L, P> controller;
58  
59      /**
60       * The settings used to construct this part. Currently unused because there
61       * are no settings.
62       */
63      private final SignalControllerSettings settings;
64  
65      /**
66       * The thread that is registered as a shutdown hook to perform termination
67       * on the state engine. The value of this field is used to record whether
68       * the part has been started (null=stopped, not-null=started).
69       */
70      private SignalThread thread = null;
71  
72      /**
73       * Indicates that the termination state transitions have been completed.
74       */
75      private boolean finished = false;
76  
77      // constructors
78  
79      /**
80       * Creates a new SignalControllerPart.
81       * 
82       * @param controller
83       *            the controller that aggregates this part
84       * @param settings
85       *            the settings for this part
86       */
87  
88      public SignalControllerPart(final ControllerImpl<S, L, P> controller, final SignalControllerSettings settings) {
89          this.controller = controller;
90          this.settings = settings;
91      }
92  
93      // controller part methods
94  
95      /**
96       * Causes a shutdown hook to be registered for the purpose of performing
97       * termination state transitions.
98       */
99  
100     public void start() {
101         synchronized (lock) {
102             if (thread != null) return;
103 
104             thread = new SignalThread();
105             Runtime.getRuntime().addShutdownHook(thread);
106         }
107     }
108 
109     /**
110      * If a shutdown hook as been registered, the hook is deregistered and the
111      * method returns immediately unless JVM shutdown is in progress in which
112      * case the method blocks (subject to the specified timeout) until
113      * termination state transitions have been completed.
114      * 
115      * @param timeout
116      *            the (approx.) number of milliseconds for which a call to the
117      *            method will block, or 0L.
118      * @return true if the part was able to confirm that all operation had
119      *         ceased
120      */
121 
122     public boolean stop(final long timeout) {
123         synchronized (lock) {
124             if (thread == null) return true;
125 
126             try {
127                 Runtime.getRuntime().removeShutdownHook(thread);
128                 return true;
129             } catch (IllegalStateException e) {
130                 // this means we are shutting down so
131                 // wait until any ongoing checking has ceased
132                 return new Waiter(lock).waitForCondition(timeout, new Waiter.Condition() {
133                     public boolean isMet() {
134                         return finished;
135                     }
136                 });
137             } finally {
138                 thread = null;
139             }
140         }
141     }
142 
143     public ControllerImpl<S, L, P> getController() {
144         return controller;
145     }
146 
147     // private utility methods
148 
149     private void terminate() {
150         StateEngine<S, L, P> engine = controller.engine;
151         Set<S> terminalStates = engine.getGraph().getTerminalStates();
152         if (terminalStates.size() != 1) return;
153         S terminalState = terminalStates.iterator().next();
154         //TODO identify a more sound way to deal with this
155         if (engine.getState() == terminalState) return; //nothing to do
156         try {
157             engine.pathTransition(terminalState, null, PathType.strictlyUniqueStates, null);
158         } catch (ProntoStateException e) {
159             ControlFactoryImpl.LOGGER.log(Level.WARNING, "State transition failed in response to signal", e);
160         }
161     }
162 
163     // inner classes
164 
165     /**
166      * An instance of this thread remains registered as a shutdown hook while
167      * the part is running. Its sole duty is to perform the termination
168      * transitions on the state engine.
169      */
170 
171     class SignalThread extends Thread {
172 
173         public SignalThread() {
174             super("pronto controller shutdown");
175         }
176         
177         @Override
178         public void run() {
179             synchronized (lock) {
180                 try {
181                     terminate();
182                 } finally {
183                     finished = true;
184                 }
185             }
186         }
187 
188     }
189 
190 }