001    /* 
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.felix.framework;
020    
021    import java.util.ArrayList;
022    import java.util.List;
023    
024    import org.osgi.framework.AdminPermission;
025    import org.osgi.framework.Bundle;
026    import org.osgi.service.startlevel.StartLevel;
027    
028    /**
029     * StartLevel service implementation.
030     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
031    **/
032    public class StartLevelImpl implements StartLevel, Runnable
033    {
034        private static final int BUNDLE_IDX = 0;
035        private static final int STARTLEVEL_IDX = 1;
036    
037        private final Felix m_felix;
038        private final List m_requestList = new ArrayList();
039        private Thread m_thread = null;
040    
041        public StartLevelImpl(Felix felix)
042        {
043            m_felix = felix;
044            // Start a thread to perform asynchronous package refreshes.
045            m_thread = new Thread(this, "FelixStartLevel");
046            m_thread.setDaemon(true);
047            m_thread.start();
048        }
049        
050        /**
051         * Stops the FelixStartLevel thread on system shutdown. Shutting down the
052         * thread explicitly is required in the embedded case, where Felix may be
053         * stopped without the Java VM being stopped. In this case the
054         * FelixStartLevel thread must be stopped explicitly.
055         * <p>
056         * This method is called by the
057         * {@link StartLevelActivator#stop(BundleContext)} method.
058         */
059        void stop()
060        {
061            synchronized (m_requestList)
062            {
063                if (m_thread != null)
064                {
065                    // Null thread variable to signal to the thread that
066                    // we want it to exit.
067                    m_thread = null;
068                    
069                    // Wake up the thread, if it is currently in the wait() state
070                    // for more work.
071                    m_requestList.notifyAll();
072                }
073            }
074        }
075        
076        /* (non-Javadoc)
077         * @see org.osgi.service.startlevel.StartLevel#getStartLevel()
078        **/
079        public int getStartLevel()
080        {
081            return m_felix.getActiveStartLevel();
082        }
083    
084        /* (non-Javadoc)
085         * @see org.osgi.service.startlevel.StartLevel#setStartLevel(int)
086        **/
087        public void setStartLevel(int startlevel)
088        {
089            Object sm = System.getSecurityManager();
090            
091            if (sm != null)
092            {
093                ((SecurityManager) sm).checkPermission(
094                    new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
095            }
096            
097            if (startlevel <= 0)
098            {
099                throw new IllegalArgumentException(
100                    "Start level must be greater than zero.");
101            }
102            
103            synchronized (m_requestList)
104            {
105                m_requestList.add(new Integer(startlevel));
106                m_requestList.notifyAll();
107            }
108        }
109    
110        /**
111         * This method is currently only called by the by the thread that calls
112         * the Felix.start() method and the shutdown thread when the
113         * framework is shutting down.
114         * @param startlevel
115        **/
116        /* package */ void setStartLevelAndWait(int startlevel)
117        {
118            Object request = new Integer(startlevel);
119            synchronized (request)
120            {
121                synchronized (m_requestList)
122                {
123                    m_requestList.add(request);
124                    m_requestList.notifyAll();
125                }
126    
127                try
128                {
129                    request.wait();
130                }
131                catch (InterruptedException ex)
132                {
133                    // Log it and ignore since it won't cause much of an issue.
134                    m_felix.getLogger().log(
135                        Logger.LOG_WARNING,
136                        "Wait for start level change during shutdown interrupted.",
137                        ex);
138                }
139            }
140        }
141    
142        /* (non-Javadoc)
143         * @see org.osgi.service.startlevel.StartLevel#getBundleStartLevel(org.osgi.framework.Bundle)
144        **/
145        public int getBundleStartLevel(Bundle bundle)
146        {
147            return m_felix.getBundleStartLevel(bundle);
148        }
149    
150        /* (non-Javadoc)
151         * @see org.osgi.service.startlevel.StartLevel#setBundleStartLevel(org.osgi.framework.Bundle, int)
152        **/
153        public void setBundleStartLevel(Bundle bundle, int startlevel)
154        {
155            Object sm = System.getSecurityManager();
156            
157            if (sm != null)
158            {
159                ((SecurityManager) sm).checkPermission(
160                    new AdminPermission(bundle, AdminPermission.EXECUTE));
161            }
162            
163            if (bundle.getBundleId() == 0)
164            {
165                throw new IllegalArgumentException(
166                    "Cannot change system bundle start level.");
167            }
168            else if (startlevel <= 0)
169            {
170                throw new IllegalArgumentException(
171                    "Start level must be greater than zero.");
172            }
173            synchronized (m_requestList)
174            {
175                // Synchronously persists the start level.
176                ((BundleImpl) bundle).setStartLevel(startlevel);
177                // Asynchronously process the start level change.
178                m_requestList.add(new Object[] { bundle, new Integer(startlevel) });
179                m_requestList.notifyAll();
180            }
181        }
182    
183        /* (non-Javadoc)
184         * @see org.osgi.service.startlevel.StartLevel#getInitialBundleStartLevel()
185        **/
186        public int getInitialBundleStartLevel()
187        {
188            return m_felix.getInitialBundleStartLevel();
189        }
190    
191        /* (non-Javadoc)
192         * @see org.osgi.service.startlevel.StartLevel#setInitialBundleStartLevel(int)
193        **/
194        public void setInitialBundleStartLevel(int startlevel)
195        {
196            Object sm = System.getSecurityManager();
197            
198            if (sm != null)
199            {
200                ((SecurityManager) sm).checkPermission(
201                    new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
202            }
203            m_felix.setInitialBundleStartLevel(startlevel);
204        }
205    
206        /* (non-Javadoc)
207         * @see org.osgi.service.startlevel.StartLevel#isBundlePersistentlyStarted(org.osgi.framework.Bundle)
208        **/
209        public boolean isBundlePersistentlyStarted(Bundle bundle)
210        {
211            return m_felix.isBundlePersistentlyStarted(bundle);
212        }
213    
214        /* (non-Javadoc)
215         * @see org.osgi.service.startlevel.StartLevel#isBundleActivationPolicyUsed(org.osgi.framework.Bundle)
216        **/
217            public boolean isBundleActivationPolicyUsed(Bundle bundle)
218        {
219            return m_felix.isBundleActivationPolicyUsed(bundle);
220        }
221    
222        public void run()
223        {
224            // This thread loops forever, thus it should
225            // be a daemon thread.
226            while (true)
227            {
228                Object request = null;
229                synchronized (m_requestList)
230                {
231                    // Wait for a request.
232                    while (m_requestList.size() == 0)
233                    {
234                        // Terminate the thread if requested to do so (see stop()).
235                        if (m_thread == null)
236                        {
237                            return;
238                        }
239                        
240                        try
241                        {
242                            m_requestList.wait();
243                        }
244                        catch (InterruptedException ex)
245                        {
246                            // Ignore.
247                        }
248                    }
249                    
250                    // Get the requested start level.
251                    request = m_requestList.remove(0);
252                }
253    
254                // If the request object is an Integer, then the request
255                // is to set the framework start level. If the request is
256                // an Object array, then the request is to set the start
257                // level for a bundle.
258                // NOTE: We don't catch any exceptions here, because
259                // the invoked methods shield us from exceptions by
260                // catching Throwables when they invoke callbacks.
261                if (request instanceof Integer)
262                {
263                    // Set the new framework start level.
264                    m_felix.setActiveStartLevel(((Integer) request).intValue());
265                }
266                else
267                {
268                    Bundle bundle = (Bundle) ((Object[]) request)[BUNDLE_IDX];
269                    int startlevel = ((Integer) ((Object[]) request)[STARTLEVEL_IDX]).intValue();
270                    m_felix.setBundleStartLevel(bundle, startlevel);
271                }
272    
273                // Notify any waiting thread that this request is done.
274                synchronized (request)
275                {
276                    request.notifyAll();
277                }
278            }
279        }
280    }