001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.xbean.kernel.standard;
018    
019    import java.util.ArrayList;
020    import java.util.HashMap;
021    import java.util.HashSet;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import java.util.concurrent.locks.Condition;
028    import java.util.concurrent.locks.Lock;
029    import org.apache.xbean.kernel.Kernel;
030    import org.apache.xbean.kernel.ServiceCondition;
031    import org.apache.xbean.kernel.ServiceName;
032    
033    /**
034     * Aggregates a set of ServiceConditions together so the ServiceManager can treat them as a single unit.
035     *
036     * @author Dain Sundstrom
037     * @version $Id$
038     * @since 2.0
039     */
040    public class AggregateCondition {
041        private final Kernel kernel;
042        private final ServiceName serviceName;
043        private final ClassLoader classLoader;
044        private final Lock lock;
045        private final Map conditions = new HashMap();
046        private final Condition satisfiedSignal;
047        private boolean destroyed = false;
048    
049        /**
050         * Creates an aggregate condition.
051         *
052         * @param kernel the kernel in which the service is registered
053         * @param serviceName the name of the service
054         * @param classLoader the class loader for the service
055         * @param lock the lock for the service manager
056         * @param conditions the conditions
057         */
058        public AggregateCondition(Kernel kernel, ServiceName serviceName, ClassLoader classLoader, Lock lock, Set conditions) {
059            this.kernel = kernel;
060            this.serviceName = serviceName;
061            this.classLoader = classLoader;
062            this.lock = lock;
063            satisfiedSignal = lock.newCondition();
064    
065            // add the conditions to the registry
066            if (conditions == null) throw new NullPointerException("conditions is null");
067            for (Iterator iterator = conditions.iterator(); iterator.hasNext();) {
068                ServiceCondition serviceCondition = (ServiceCondition) iterator.next();
069                addCondition(serviceCondition);
070            }
071        }
072    
073        /**
074         * Gets a snapshot of the current conditions.
075         *
076         * @return a snapshot of the current conditions
077         */
078        protected Set getConditions() {
079            return new HashSet(conditions.keySet());
080        }
081    
082        /**
083         * Adds a new condition if not already registered.
084         *
085         * @param condition the new condition
086         */
087        protected final void addCondition(ServiceCondition condition) {
088            if (!conditions.containsKey(condition)) {
089                StandardServiceConditionContext context = new StandardServiceConditionContext(kernel, serviceName, classLoader, lock, satisfiedSignal);
090                condition.initialize(context);
091                conditions.put(condition, context);
092            }
093        }
094    
095        /**
096         * Removes a condition from the registry if present.
097         *
098         * @param condition the condition to remove
099         */
100        protected final void removeCondition(ServiceCondition condition) {
101            if (conditions.remove(condition) != null) {
102                condition.destroy();
103            }
104        }
105    
106        /**
107         * Initializes the conditions.
108         */
109        public void initialize() {
110            if (destroyed) throw new IllegalStateException("destroyed");
111    
112            for (Iterator iterator = conditions.entrySet().iterator(); iterator.hasNext();) {
113                Map.Entry entry = (Map.Entry) iterator.next();
114                ServiceCondition condition = (ServiceCondition) entry.getKey();
115                StandardServiceConditionContext context = (StandardServiceConditionContext) entry.getValue();
116                condition.initialize(context);
117            }
118        }
119    
120        /**
121         * Gets the unsatisfied conditions.
122         *
123         * @return the unstatisfied conditions
124         */
125        public Set getUnsatisfied() {
126            if (destroyed) throw new IllegalStateException("destroyed");
127    
128            Set unsatisfied = new HashSet();
129            for (Iterator iterator = conditions.entrySet().iterator(); iterator.hasNext();) {
130                Map.Entry entry = (Map.Entry) iterator.next();
131                ServiceCondition condition = (ServiceCondition) entry.getKey();
132                StandardServiceConditionContext context = (StandardServiceConditionContext) entry.getValue();
133                if (!context.isSatisfied()) {
134                    if (condition.isSatisfied()) {
135                        // the condition is satisfied
136                        // record this fact in the context
137                        context.setSatisfied();
138                    } else {
139                        unsatisfied.add(condition);
140                    }
141                }
142            }
143    
144            // notify anyone awaiting satisfaction
145            if (unsatisfied.isEmpty()) {
146                satisfiedSignal.signalAll();
147            }
148            return unsatisfied;
149        }
150    
151        /**
152         * Gets the destroyed status.
153         *
154         * @return true if this AggregateCondition been destroyed; false otherwise
155         */
156        public boolean isDestroyed() {
157            return destroyed;
158        }
159    
160        /**
161         * Destroys all condtions.
162         *
163         * @return a list of the Exceptions or Errors that occured while destroying the conditon objects.
164         */
165        public List destroy() {
166            List stopErrors = new ArrayList();
167            if (!destroyed) {
168                destroyed = true;
169                for (Iterator iterator = conditions.keySet().iterator(); iterator.hasNext();) {
170                    ServiceCondition condition = (ServiceCondition) iterator.next();
171                    try {
172                        condition.destroy();
173                    } catch (RuntimeException stopError) {
174                        stopErrors.add(stopError);
175                    } catch (Error stopError) {
176                        stopErrors.add(stopError);
177                    }
178                }
179                // notify anyone awaiting satisfaction
180                satisfiedSignal.signalAll();
181            }
182            return stopErrors;
183        }
184    
185        /**
186         * Causes the current thread to wait until the conditons is satisfied.
187         *
188         * @throws InterruptedException if the thread is interrupted
189         */
190        public void awaitSatisfaction() throws InterruptedException {
191            while (!destroyed) {
192                if (getUnsatisfied().isEmpty()) {
193                    return;
194                }
195                satisfiedSignal.await();
196            }
197        }
198    }