001    package org.apache.fulcrum.yaafi.service.shutdown;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.File;
023    import java.security.MessageDigest;
024    
025    import org.apache.avalon.framework.activity.Disposable;
026    import org.apache.avalon.framework.activity.Initializable;
027    import org.apache.avalon.framework.activity.Startable;
028    import org.apache.avalon.framework.configuration.Configuration;
029    import org.apache.avalon.framework.configuration.ConfigurationException;
030    import org.apache.avalon.framework.configuration.Reconfigurable;
031    import org.apache.avalon.framework.context.Context;
032    import org.apache.avalon.framework.context.ContextException;
033    import org.apache.avalon.framework.context.Contextualizable;
034    import org.apache.avalon.framework.logger.AbstractLogEnabled;
035    import org.apache.avalon.framework.service.ServiceException;
036    import org.apache.avalon.framework.service.ServiceManager;
037    import org.apache.avalon.framework.service.Serviceable;
038    
039    
040    /**
041     * Monitors the componentConfiguration.xml and triggers a reconfiguration
042     * if the content of the component configuration file  has changed.
043     *
044     * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
045     */
046    
047    public class ShutdownServiceImpl
048        extends AbstractLogEnabled
049        implements ShutdownService, Serviceable, Contextualizable,
050            Reconfigurable, Initializable, Runnable, Startable, Disposable
051    {
052        /** the interval between two checks in ms */
053        private int interval;
054    
055        /** shall the worker thread terminate immediately */
056        private boolean terminateNow;
057    
058        /** the worker thread polling the resource */
059        private Thread workerThread;
060    
061        /** the ServiceManager to use */
062        private ServiceManager serviceManager;
063    
064        /** the application directory */
065        private File applicationDir;
066    
067        /** our own and only shutdown entry */
068        private ShutdownEntry shutdownEntry;
069    
070        /////////////////////////////////////////////////////////////////////////
071        // Avalon Service Lifecycle Implementation
072        /////////////////////////////////////////////////////////////////////////
073    
074        /**
075         * Constructor
076         */
077        public ShutdownServiceImpl()
078        {
079            this.terminateNow = false;
080        }
081    
082        /**
083         * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
084         */
085        public void service(ServiceManager manager) throws ServiceException
086        {
087            this.serviceManager = manager;
088        }
089    
090        /**
091         * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
092         */
093        public void contextualize(Context context) throws ContextException
094        {
095            this.applicationDir  = (File) context.get("urn:avalon:home");
096        }
097    
098        /**
099         * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
100         */
101        public void configure(Configuration configuration) throws ConfigurationException
102        {
103            // limit to minimum interval of 1 second
104    
105            this.interval = Math.max( configuration.getAttributeAsInteger("interval",5000), 1000 );
106    
107            this.getLogger().debug( "Monitoring the resources every " + this.interval + " ms" );
108    
109            if( configuration.getChild("entry",false) != null )
110            {
111                Configuration shutdownConfig = configuration.getChild("entry");
112    
113                String shutdownEntryLocation = shutdownConfig.getChild("location").getValue();
114    
115                this.shutdownEntry = new ShutdownEntry(
116                    this.getLogger(),
117                    this.applicationDir,
118                    shutdownEntryLocation,
119                    shutdownConfig.getChild("useSystemExit").getValueAsBoolean(false)
120                    );
121    
122                this.getLogger().debug( "Using a shutdown entry : " + shutdownEntryLocation );
123            }
124            else
125            {
126                this.shutdownEntry = null;
127                this.getLogger().debug( "No shutdown entry defined" );
128            }
129        }
130    
131        /**
132         * @see org.apache.avalon.framework.activity.Initializable#initialize()
133         */
134        public void initialize() throws Exception
135        {
136            // request a SHA-1 to make sure that it is supported
137    
138            MessageDigest.getInstance( "SHA1" );
139    
140            // check that the ServiceManager inplements Disposable
141    
142            if( (this.serviceManager instanceof Disposable) == false )
143            {
144                String msg = "The ServiceManager instance does not implement Disposable?!";
145                throw new IllegalArgumentException( msg );
146            }
147    
148            // create the worker thread polling the target
149    
150            this.workerThread = new Thread( this, "ShutdownService" );
151        }
152    
153        /**
154         * @see org.apache.avalon.framework.activity.Startable#start()
155         */
156        public void start() throws Exception
157        {
158            this.getLogger().debug( "Starting worker thread ..." );
159            this.workerThread.start();
160        }
161    
162        /**
163         * @see org.apache.avalon.framework.activity.Startable#stop()
164         */
165        public void stop() throws Exception
166        {
167            this.getLogger().debug( "Stopping worker thread ..." );
168            this.terminateNow = true;
169            this.workerThread.interrupt();
170            this.workerThread.join( 10000 );
171        }
172    
173        /**
174         * @see org.apache.avalon.framework.activity.Disposable#dispose()
175         */
176        public void dispose()
177        {
178            this.terminateNow = false;
179            this.applicationDir = null;
180            this.workerThread = null;
181            this.serviceManager = null;
182        }
183    
184        /**
185         * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
186         */
187        public void reconfigure(Configuration configuration)
188            throws ConfigurationException
189        {
190            this.configure(configuration);
191        }
192    
193        /////////////////////////////////////////////////////////////////////////
194        // Service interface implementation
195        /////////////////////////////////////////////////////////////////////////
196    
197        /**
198         * @see java.lang.Runnable#run()
199         */
200        public void run()
201        {
202            while( this.terminateNow == false )
203            {
204                try
205                {
206                    Thread.sleep( this.interval );
207                }
208                catch (InterruptedException e)
209                {
210                    // nothing to do
211                }
212    
213                if( this.hasShutdownEntry() && this.getShutdownEntry().hasChanged() )
214                {
215                    if( this.serviceManager instanceof Disposable )
216                    {
217                        if( this.getShutdownEntry().isUseSystemExit() )
218                        {
219                            this.getLogger().warn( "Forcing a shutdown using System.exit() ..." );
220                        }
221                        else
222                        {
223                            this.getLogger().warn( "Forcing a shutdown ..." );
224                        }
225    
226                        // create a demon thread to shutdown the container
227    
228                        Shutdown shutdown = new Shutdown(
229                            (Disposable) this.serviceManager,
230                            this.getShutdownEntry().isUseSystemExit()
231                            );
232    
233                        Thread shutdownThread = new Thread( shutdown, "ShutdownServiceThread" );
234                        shutdownThread.setDaemon(true);
235                        shutdownThread.start();
236                    }
237                }
238            }
239        }
240    
241        /////////////////////////////////////////////////////////////////////////
242        // Service implementation
243        /////////////////////////////////////////////////////////////////////////
244    
245        /**
246         * @return Returns the shutdownEntry.
247         */
248        private ShutdownEntry getShutdownEntry()
249        {
250            return this.shutdownEntry;
251        }
252    
253        /**
254         * @return Is a shutdown entry defined?
255         */
256        private boolean hasShutdownEntry()
257        {
258            return ( this.shutdownEntry != null ? true : false );
259        }
260    }