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 }