KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > dependency > plugins > AbstractController


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.dependency.plugins;
23
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.concurrent.locks.ReentrantReadWriteLock JavaDoc;
30
31 import org.jboss.dependency.spi.Controller;
32 import org.jboss.dependency.spi.ControllerContext;
33 import org.jboss.dependency.spi.ControllerMode;
34 import org.jboss.dependency.spi.ControllerState;
35 import org.jboss.dependency.spi.DependencyInfo;
36 import org.jboss.dependency.spi.DependencyItem;
37 import org.jboss.util.JBossObject;
38 import org.jboss.util.collection.CollectionsFactory;
39
40 /**
41  * Abstract controller.
42  *
43  * @author <a HREF="adrian@jboss.com">Adrian Brock</a>
44  * @version $Revision: 55952 $
45  */

46 public class AbstractController extends JBossObject implements Controller
47 {
48    /** The lock */
49    private ReentrantReadWriteLock JavaDoc lock = new ReentrantReadWriteLock JavaDoc();
50
51    /** The states in order List<ControllerState> */
52    protected List JavaDoc<ControllerState> states = CollectionsFactory.createCopyOnWriteList();
53
54    /** All contexts by name Map<Object, ControllerContext> */
55    protected Map JavaDoc<Object JavaDoc, ControllerContext> allContexts = CollectionsFactory.createConcurrentReaderMap();
56
57    /** The contexts by state Map<ControllerState, Set<ControllerContext>> */
58    protected Map JavaDoc<ControllerState, Set JavaDoc<ControllerContext>> contextsByState = CollectionsFactory.createConcurrentReaderMap();
59
60    /** The error contexts Map<Name, ControllerContext> */
61    protected Map JavaDoc<Object JavaDoc, ControllerContext> errorContexts = CollectionsFactory.createConcurrentReaderMap();
62
63    /** The contexts that are currently being installed */
64    protected Set JavaDoc<ControllerContext> installing = CollectionsFactory.createCopyOnWriteSet();
65
66    /** Whether an on demand context has been enabled */
67    protected boolean onDemandEnabled = true;
68
69    /**
70     * Create an abstract controller
71     *
72     * @throws Exception for any error
73     */

74    public AbstractController() throws Exception JavaDoc
75    {
76       addState(ControllerState.NOT_INSTALLED, null);
77       addState(ControllerState.DESCRIBED, null);
78       addState(ControllerState.INSTANTIATED, null);
79       addState(ControllerState.CONFIGURED, null);
80       addState(ControllerState.CREATE, null);
81       addState(ControllerState.START, null);
82       addState(ControllerState.INSTALLED, null);
83    }
84
85    public void addState(ControllerState state, ControllerState before)
86    {
87       lockWrite();
88       try
89       {
90          if (before == null)
91          {
92             states.add(state);
93          }
94          else
95          {
96             int index = states.indexOf(before);
97             if (index == -1)
98                throw new IllegalStateException JavaDoc(before + " is not a state in the controller.");
99             states.add(index, state);
100          }
101
102          Set JavaDoc<ControllerContext> contexts = CollectionsFactory.createCopyOnWriteSet();
103          contextsByState.put(state, contexts);
104       }
105       finally
106       {
107          unlockWrite();
108       }
109    }
110
111    public ControllerContext getContext(Object JavaDoc name, ControllerState state)
112    {
113       if (name == null)
114          throw new IllegalArgumentException JavaDoc("Null name");
115
116       lockRead();
117       try
118       {
119          ControllerContext result = allContexts.get(name);
120          if (result != null && state != null)
121          {
122             int required = states.indexOf(state);
123             if (required == -1)
124                throw new IllegalArgumentException JavaDoc("Unknown state " + state + " states=" + states);
125             int current = states.indexOf(result.getState());
126             if (current < required)
127                return null;
128          }
129          return result;
130       }
131       finally
132       {
133          unlockRead();
134       }
135    }
136
137    public ControllerContext getInstalledContext(Object JavaDoc name)
138    {
139       return getContext(name, ControllerState.INSTALLED);
140    }
141
142    public Set JavaDoc<ControllerContext> getNotInstalled()
143    {
144       lockWrite();
145       try
146       {
147          Set JavaDoc<ControllerContext> result = new HashSet JavaDoc<ControllerContext>(errorContexts.values());
148          for (int i = 0; ControllerState.INSTALLED.equals(states.get(i)) == false; ++i)
149          {
150             Set JavaDoc<ControllerContext> stateContexts = contextsByState.get(states.get(i));
151             result.addAll(stateContexts);
152          }
153          return result;
154       }
155       finally
156       {
157          unlockWrite();
158       }
159    }
160
161    public List JavaDoc<ControllerState> getStates()
162    {
163       return states;
164    }
165
166    public void install(ControllerContext context) throws Throwable JavaDoc
167    {
168       boolean trace = log.isTraceEnabled();
169
170       if (context == null)
171          throw new IllegalArgumentException JavaDoc("Null context");
172
173       Object JavaDoc name = context.getName();
174       if (name == null)
175          throw new IllegalArgumentException JavaDoc("Null name " + context.toShortString());
176
177       install(context, trace);
178    }
179
180    public void change(ControllerContext context, ControllerState state) throws Throwable JavaDoc
181    {
182       boolean trace = log.isTraceEnabled();
183
184       if (context == null)
185          throw new IllegalArgumentException JavaDoc("Null context");
186
187       if (state == null)
188          throw new IllegalArgumentException JavaDoc("Null state");
189
190       change(context, state, trace);
191    }
192
193    public void enableOnDemand(ControllerContext context) throws Throwable JavaDoc
194    {
195       boolean trace = log.isTraceEnabled();
196
197       if (context == null)
198          throw new IllegalArgumentException JavaDoc("Null context");
199
200       enableOnDemand(context, trace);
201    }
202
203    public ControllerContext uninstall(Object JavaDoc name)
204    {
205       boolean trace = log.isTraceEnabled();
206
207       if (name == null)
208          throw new IllegalArgumentException JavaDoc("Null name");
209
210       lockWrite();
211       try
212       {
213          if (errorContexts.remove(name) != null && trace)
214             log.trace("Tidied up context in error state: " + name);
215
216          ControllerContext context = allContexts.get(name);
217          if (context == null)
218             throw new IllegalStateException JavaDoc("Not installed: " + name);
219
220          if (trace)
221             log.trace("Uninstalling " + context.toShortString());
222
223          uninstallContext(context, ControllerState.NOT_INSTALLED, trace);
224
225          allContexts.remove(name);
226          return context;
227       }
228       finally
229       {
230          unlockWrite();
231       }
232    }
233
234    /**
235     * Install a context
236     *
237     * @param context the context
238     * @param trace whether trace is enabled
239     * @throws Throwable for any error
240     */

241    protected void install(ControllerContext context, boolean trace) throws Throwable JavaDoc
242    {
243       lockWrite();
244       try
245       {
246          Object JavaDoc name = context.getName();
247
248          if (allContexts.get(name) != null)
249             throw new IllegalStateException JavaDoc(name + " is already installed.");
250
251          if (ControllerMode.AUTOMATIC.equals(context.getMode()))
252             context.setRequiredState(ControllerState.INSTALLED);
253
254          if (trace)
255             log.trace("Installing " + context.toShortString());
256
257          context.setController(this);
258          DependencyInfo dependencies = context.getDependencyInfo();
259          if (trace)
260          {
261             String JavaDoc dependsOn = null;
262             if( dependencies != null )
263             {
264                Set JavaDoc set = dependencies.getIDependOn(null);
265                if( set != null )
266                   dependsOn = set.toString();
267             }
268             log.trace("Dependencies for " + name + ": " + dependsOn);
269          }
270
271          if (incrementState(context, trace))
272          {
273             allContexts.put(context.getName(), context);
274             resolveContexts(trace);
275          }
276          else
277          {
278             errorContexts.remove(context);
279             throw context.getError();
280          }
281       }
282       finally
283       {
284          unlockWrite();
285       }
286    }
287
288    /**
289     * Change a context's state
290     *
291     * @param context the context
292     * @param state the required state
293     * @param trace whether trace is enabled
294     * @throws Throwable for any error
295     */

296    protected void change(ControllerContext context, ControllerState state, boolean trace) throws Throwable JavaDoc
297    {
298       lockWrite();
299       try
300       {
301          ControllerState fromState = context.getState();
302          int currentIndex = states.indexOf(fromState);
303          int requiredIndex = states.indexOf(state);
304          if (requiredIndex == -1)
305             throw new IllegalArgumentException JavaDoc("Unknown state: " + state);
306
307          if (currentIndex == requiredIndex)
308          {
309             if (trace)
310                log.trace("No change required toState=" + state.getStateString() + " " + context.toShortString());
311             return;
312          }
313
314          if (trace)
315             log.trace("Change toState=" + state.getStateString() + " " + context.toShortString());
316
317          context.setRequiredState(state);
318
319          if (currentIndex < requiredIndex)
320             resolveContexts(trace);
321          else
322          {
323             while (currentIndex > requiredIndex)
324             {
325                uninstallContext(context, trace);
326                currentIndex = states.indexOf(context.getState());
327             }
328          }
329       }
330       finally
331       {
332          unlockWrite();
333       }
334    }
335
336    /**
337     * Enable an on demand context
338     *
339     * @param context the context
340     * @param trace whether trace is enabled
341     * @throws Throwable for any error
342     */

343    protected void enableOnDemand(ControllerContext context, boolean trace) throws Throwable JavaDoc
344    {
345       lockWrite();
346       try
347       {
348          if (ControllerMode.ON_DEMAND.equals(context.getMode()) == false)
349             throw new IllegalStateException JavaDoc("Context is not ON DEMAND: " + context.toShortString());
350
351          if (allContexts.containsKey(context.getName()) == false)
352             throw new IllegalStateException JavaDoc("Unknown context: " + context.toShortString());
353
354          // Already done
355
if (ControllerState.INSTALLED.equals(context.getRequiredState()))
356             return;
357          context.setRequiredState(ControllerState.INSTALLED);
358
359          if (trace)
360             log.trace("Enable onDemand: " + context.toShortString());
361
362          onDemandEnabled = true;
363       }
364       finally
365       {
366          unlockWrite();
367       }
368    }
369
370    /**
371     * Increment state<p>
372     *
373     * This method must be invoked with the write lock taken.
374     *
375     * @param context the context
376     * @param trace whether trace is enabled
377     * @return whether the suceeded
378     */

379    protected boolean incrementState(ControllerContext context, boolean trace)
380    {
381       ControllerState fromState = context.getState();
382
383       Set JavaDoc fromContexts = null;
384
385       int currentIndex = -1;
386       if (ControllerState.ERROR.equals(fromState))
387       {
388          errorContexts.remove(context);
389          Throwable JavaDoc error = null;
390          unlockWrite();
391          try
392          {
393             install(context, ControllerState.ERROR, ControllerState.NOT_INSTALLED);
394          }
395          catch (Throwable JavaDoc t)
396          {
397             error = t;
398          }
399          finally
400          {
401             lockWrite();
402             if (error != null)
403             {
404                log.error("Error during initial installation: " + context.toShortString(), error);
405                context.setError(error);
406                errorContexts.put(context.getName(), context);
407                return false;
408             }
409          }
410          Set JavaDoc<ControllerContext> notInstalled = contextsByState.get(ControllerState.NOT_INSTALLED);
411          notInstalled.add(context);
412       }
413       else
414       {
415          currentIndex = states.indexOf(fromState);
416          fromContexts = contextsByState.get(fromState);
417          if (fromContexts.contains(context) == false)
418             throw new IllegalStateException JavaDoc("Context not found in previous state: " + context.toShortString());
419       }
420
421       int toIndex = currentIndex + 1;
422       ControllerState toState = states.get(toIndex);
423       Set JavaDoc<ControllerContext> toContexts = contextsByState.get(toState);
424
425       unlockWrite();
426       Throwable JavaDoc error = null;
427       try
428       {
429          install(context, fromState, toState);
430       }
431       catch (Throwable JavaDoc t)
432       {
433          error = t;
434       }
435       finally
436       {
437          lockWrite();
438          if (error != null)
439          {
440             log.error("Error installing to " + toState.getStateString() + ": " + context.toShortString(), error);
441             uninstallContext(context, ControllerState.NOT_INSTALLED, trace);
442             errorContexts.put(context.getName(), context);
443             context.setError(error);
444             return false;
445          }
446       }
447
448       if (fromContexts != null)
449          fromContexts.remove(context);
450       toContexts.add(context);
451       return true;
452    }
453
454    /**
455     * Resolve unresolved contexts<p>
456     *
457     * This method must be invoked with the write lock taken
458     *
459     * @param trace whether trace is enabled
460     */

461    protected void resolveContexts(boolean trace)
462    {
463       boolean resolutions = true;
464       while (resolutions || onDemandEnabled)
465       {
466          onDemandEnabled = false;
467          resolutions = false;
468          for (int i = 0; i < states.size()-1; ++i)
469          {
470             ControllerState fromState = states.get(i);
471             ControllerState toState = states.get(i+1);
472             if (resolveContexts(fromState, toState, trace))
473             {
474                resolutions = true;
475                break;
476             }
477          }
478       }
479
480       if (trace)
481       {
482          for (int i = 0; i < states.size()-1; ++i)
483          {
484             ControllerState state = states.get(i);
485             ControllerState nextState = states.get(i+1);
486             Set JavaDoc<ControllerContext> stillUnresolved = contextsByState.get(state);
487             if (stillUnresolved.isEmpty() == false)
488             {
489                for (Iterator JavaDoc j = stillUnresolved.iterator(); j.hasNext();)
490                {
491                   ControllerContext ctx = (ControllerContext) j.next();
492                   if (advance(ctx))
493                      log.trace("Still unresolved " + nextState.getStateString() + ": " + ctx);
494                }
495             }
496          }
497       }
498    }
499
500    /**
501     * Resolve contexts<p>
502     *
503     * This method must be invoked with the write lock taken
504     *
505     * @param fromState the from state
506     * @param toState the to state
507     * @param trace whether trace is enabled
508     * @return true when there were resolutions
509     */

510    protected boolean resolveContexts(ControllerState fromState, ControllerState toState, boolean trace)
511    {
512       boolean resolutions = false;
513       Set JavaDoc<ControllerContext> unresolved = contextsByState.get(fromState);
514       Set JavaDoc<ControllerContext> resolved = resolveContexts(unresolved, toState, trace);
515       if (resolved.isEmpty() == false)
516       {
517          for (Iterator JavaDoc i = resolved.iterator(); i.hasNext();)
518          {
519             ControllerContext context = (ControllerContext) i.next();
520             Object JavaDoc name = context.getName();
521             if (fromState.equals(context.getState()) == false)
522             {
523                if (trace)
524                   log.trace("Skipping already installed " + name + " for " + toState.getStateString());
525             }
526             else if (installing.add(context) == false)
527             {
528                if (trace)
529                   log.trace("Already installing " + name + " for " + toState.getStateString());
530             }
531             else
532             {
533                try
534                {
535                   if (trace)
536                      log.trace("Dependencies resolved " + name + " for " + toState.getStateString());
537
538                   if (incrementState(context, trace))
539                   {
540                      resolutions = true;
541                      if (trace)
542                         log.trace(name + " " + toState.getStateString());
543                   }
544                }
545                finally
546                {
547                   installing.remove(context);
548                }
549             }
550          }
551       }
552
553       return resolutions;
554    }
555
556    /**
557     * Resolve contexts<p>
558     *
559     * This method must be invoked with the write lock taken
560     *
561     * @param contexts the contexts
562     * @param state the state
563     * @param trace whether trace is enabled
564     * @return the set of resolved contexts
565     */

566    protected Set JavaDoc<ControllerContext> resolveContexts(Set JavaDoc<ControllerContext> contexts, ControllerState state, boolean trace)
567    {
568       HashSet JavaDoc<ControllerContext> result = new HashSet JavaDoc<ControllerContext>();
569
570       if (contexts.isEmpty() == false)
571       {
572          for (Iterator JavaDoc i = contexts.iterator(); i.hasNext();)
573          {
574             ControllerContext ctx = (ControllerContext) i.next();
575             if (advance(ctx))
576             {
577                DependencyInfo dependencies = ctx.getDependencyInfo();
578                if (dependencies.resolveDependencies(this, state))
579                   result.add(ctx);
580             }
581          }
582       }
583
584       return result;
585    }
586
587    /**
588     * Uninstall a context
589     *
590     * This method must be invoked with the write lock taken
591     *
592     * @param context the context to uninstall
593     * @param toState the target state
594     * @param trace whether trace is enabled
595     */

596    protected void uninstallContext(ControllerContext context, ControllerState toState, boolean trace)
597    {
598       int targetState = states.indexOf(toState);
599       if (targetState == -1)
600          log.error("INTERNAL ERROR: unknown state " + toState + " states=" + states, new Exception JavaDoc("STACKTRACE"));
601
602       while (true)
603       {
604          ControllerState fromState = context.getState();
605          if (ControllerState.ERROR.equals(fromState))
606             return;
607          int currentState = states.indexOf(fromState);
608          if (currentState == -1)
609             log.error("INTERNAL ERROR: current state not found: " + context.toShortString(), new Exception JavaDoc("STACKTRACE"));
610          if (targetState > currentState)
611             return;
612          else
613             uninstallContext(context, trace);
614       }
615    }
616
617    /**
618     * Uninstall a context<p>
619     *
620     * This method must be invoked with the write lock taken
621     *
622     * @param context the context to uninstall
623     * @param trace whether trace is enabled
624     */

625    protected void uninstallContext(ControllerContext context, boolean trace)
626    {
627       Object JavaDoc name = context.getName();
628
629       ControllerState fromState = context.getState();
630       int currentIndex = states.indexOf(fromState);
631
632       if (trace)
633          log.trace("Uninstalling " + name + " from " + fromState.getStateString());
634
635       Set JavaDoc<ControllerContext> fromContexts = contextsByState.get(fromState);
636       if (fromContexts == null || fromContexts.remove(context) == false)
637       {
638          log.error("INTERNAL ERROR: context not found in previous state " + fromState.getStateString() + " context=" + context.toShortString(), new Exception JavaDoc("STACKTRACE"));
639          return;
640       }
641
642       DependencyInfo dependencies = context.getDependencyInfo();
643       Set JavaDoc dependsOnMe = dependencies.getDependsOnMe(null);
644       if (dependsOnMe.isEmpty() == false)
645       {
646          for (Iterator JavaDoc i = dependsOnMe.iterator(); i.hasNext();)
647          {
648             DependencyItem item = (DependencyItem) i.next();
649             if (item.isResolved())
650             {
651                ControllerState dependentState = item.getDependentState();
652                if (dependentState == null || dependentState.equals(fromState))
653                {
654                   item.unresolved(this);
655                   ControllerContext dependent = getContext(item.getName(), null);
656                   if (dependent != null)
657                   {
658                      ControllerState whenRequired = item.getWhenRequired();
659                      if (whenRequired == null)
660                         whenRequired = ControllerState.NOT_INSTALLED;
661                      int proposed = states.indexOf(whenRequired);
662                      int actual = states.indexOf(dependent.getState());
663                      if (proposed <= actual)
664                         uninstallContext(dependent, whenRequired, trace);
665                   }
666                }
667             }
668          }
669       }
670
671       int toIndex = currentIndex-1;
672       if (toIndex == -1)
673       {
674          context.setError(new IllegalStateException JavaDoc("Cannot uninstall from " + fromState));
675          return;
676       }
677
678       ControllerState toState = states.get(toIndex);
679       Set JavaDoc<ControllerContext> toContexts = contextsByState.get(toState);
680       toContexts.add(context);
681
682       unlockWrite();
683       try
684       {
685          uninstall(context, fromState, toState);
686       }
687       catch (Throwable JavaDoc t)
688       {
689          log.warn("Error uninstalling from " + fromState.getStateString() + ": " + context.toShortString(), t);
690       }
691       finally
692       {
693          lockWrite();
694       }
695    }
696
697    /**
698     * Install a context<p>
699     *
700     * This method must be invoked with NO locks taken
701     *
702     * @param context the context
703     * @param fromState the from state
704     * @param toState the toState
705     * @throws Throwable for any error
706     */

707    protected void install(ControllerContext context, ControllerState fromState, ControllerState toState) throws Throwable JavaDoc
708    {
709       context.install(fromState, toState);
710    }
711
712    /**
713     * Uninstall a context<p>
714     *
715     * This method must be invoked with NO locks taken
716     *
717     * @param context the context
718     * @param fromState the from state
719     * @param toState the to state
720     */

721    protected void uninstall(ControllerContext context, ControllerState fromState, ControllerState toState)
722    {
723       context.uninstall(fromState, toState);
724    }
725
726    /**
727     * Whether we should advance the context<p>
728     *
729     * This method must be invoked with the write lock taken
730     *
731     * @param context the context
732     * @return true when we should advance the context
733     */

734    protected boolean advance(ControllerContext context)
735    {
736       ControllerMode mode = context.getMode();
737
738       // Never advance for disabled
739
if (ControllerMode.DISABLED.equals(mode))
740          return false;
741
742       ControllerState fromState = context.getState();
743       int fromIndex = states.indexOf(fromState);
744       ControllerState requiredState = context.getRequiredState();
745       int requiredIndex = states.indexOf(requiredState);
746
747       return fromIndex < requiredIndex;
748    }
749
750    /**
751     * Lock for read
752     */

753    protected void lockRead()
754    {
755       lock.readLock().lock();
756    }
757
758    /**
759     * Unlock for read
760     */

761    protected void unlockRead()
762    {
763       lock.readLock().unlock();
764    }
765
766    /**
767     * Lock for write
768     */

769    protected void lockWrite()
770    {
771       lock.writeLock().lock();
772    }
773
774    /**
775     * Unlock for write
776     */

777    protected void unlockWrite()
778    {
779       lock.writeLock().unlock();
780    }
781 }
782
Popular Tags