001 package org.apache.fulcrum.yaafi.service.reconfiguration; 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.io.InputStream; 024 import java.security.MessageDigest; 025 026 import org.apache.avalon.framework.activity.Disposable; 027 import org.apache.avalon.framework.activity.Initializable; 028 import org.apache.avalon.framework.activity.Startable; 029 import org.apache.avalon.framework.activity.Suspendable; 030 import org.apache.avalon.framework.configuration.Configuration; 031 import org.apache.avalon.framework.configuration.ConfigurationException; 032 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 033 import org.apache.avalon.framework.configuration.Reconfigurable; 034 import org.apache.avalon.framework.context.Context; 035 import org.apache.avalon.framework.context.ContextException; 036 import org.apache.avalon.framework.context.Contextualizable; 037 import org.apache.avalon.framework.logger.AbstractLogEnabled; 038 import org.apache.avalon.framework.service.ServiceException; 039 import org.apache.avalon.framework.service.ServiceManager; 040 import org.apache.avalon.framework.service.Serviceable; 041 import org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager; 042 043 044 /** 045 * Monitors the componentConfiguration.xml and triggers a reconfiguration 046 * if the content of the component configuration file has changed. 047 * 048 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a> 049 */ 050 051 public class ReconfigurationServiceImpl 052 extends AbstractLogEnabled 053 implements ReconfigurationService, Serviceable, Contextualizable, 054 Reconfigurable, Initializable, Runnable, Startable, Disposable 055 { 056 /** the interval between two checks in ms */ 057 private int interval; 058 059 /** shall the worker thread terminate immediately */ 060 private boolean terminateNow; 061 062 /** the worker thread polling the componentConfiguraton */ 063 private Thread workerThread; 064 065 /** the ServiceManager to use */ 066 private ServiceManager serviceManager; 067 068 /** the application directory */ 069 private File applicationDir; 070 071 /** our list of resources to monitor */ 072 private ReconfigurationEntry[] reconfigurationEntryList; 073 074 /** the interface to reconfigure individual services */ 075 private ServiceLifecycleManager serviceLifecycleManager; 076 077 ///////////////////////////////////////////////////////////////////////// 078 // Avalon Service Lifecycle Implementation 079 ///////////////////////////////////////////////////////////////////////// 080 081 /** 082 * Constructor 083 */ 084 public ReconfigurationServiceImpl() 085 { 086 this.terminateNow = false; 087 } 088 089 /** 090 * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) 091 */ 092 public void service(ServiceManager manager) throws ServiceException 093 { 094 this.serviceManager = manager; 095 this.serviceLifecycleManager = (ServiceLifecycleManager) manager; 096 } 097 098 /** 099 * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) 100 */ 101 public void contextualize(Context context) throws ContextException 102 { 103 this.applicationDir = (File) context.get("urn:avalon:home"); 104 } 105 106 /** 107 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) 108 */ 109 public void configure(Configuration configuration) throws ConfigurationException 110 { 111 // limit to minimum interval of 1 second 112 113 this.interval = Math.max( configuration.getAttributeAsInteger("interval",5000), 1000 ); 114 115 this.getLogger().debug( "Monitoring the resources every " + this.interval + " ms" ); 116 117 // parse the resources to monitor 118 119 Configuration entry = null; 120 Configuration services = null; 121 Configuration[] serviceEntries = null; 122 Configuration[] entryList = configuration.getChildren("entry"); 123 124 String location = null; 125 String serviceName = null; 126 String[] serviceNameList = null; 127 ReconfigurationEntry reconfigurationEntry = null; 128 ReconfigurationEntry[] list = new ReconfigurationEntry[entryList.length]; 129 130 for( int i=0; i<entryList.length; i++ ) 131 { 132 entry = entryList[i]; 133 location = entry.getChild("location").getValue(); 134 services = entry.getChild("services",false); 135 136 this.getLogger().debug( "Adding the following resource to monitor : " + location ); 137 138 if( services != null ) 139 { 140 serviceEntries = services.getChildren("service"); 141 serviceNameList = new String[serviceEntries.length]; 142 143 for( int j=0; j<serviceEntries.length; j++ ) 144 { 145 serviceName = serviceEntries[j].getAttribute("name"); 146 serviceNameList[j] = serviceName; 147 } 148 } 149 150 reconfigurationEntry = new ReconfigurationEntry( 151 this.getLogger(), 152 this.applicationDir, 153 location, 154 serviceNameList 155 ); 156 157 list[i] = reconfigurationEntry; 158 } 159 160 this.getLogger().debug( "Monitoring " + list.length + " resources" ); 161 162 this.setReconfigurationEntryList(list); 163 } 164 165 /** 166 * @see org.apache.avalon.framework.activity.Initializable#initialize() 167 */ 168 public void initialize() throws Exception 169 { 170 // request a SHA-1 to make sure that it is supported 171 172 MessageDigest.getInstance( "SHA1" ); 173 174 // check that the ServiceManager inplements Reconfigurable 175 176 if( (this.serviceManager instanceof ServiceLifecycleManager) == false ) 177 { 178 String msg = "The ServiceManager instance does not implement ServiceLifecycleManager?!"; 179 throw new IllegalArgumentException( msg ); 180 } 181 182 // create the worker thread polling the target 183 184 this.workerThread = new Thread( this, "ReconfigurationService" ); 185 } 186 187 /** 188 * @see org.apache.avalon.framework.activity.Startable#start() 189 */ 190 public void start() throws Exception 191 { 192 this.getLogger().debug( "Starting worker thread ..." ); 193 this.workerThread.start(); 194 } 195 196 /** 197 * @see org.apache.avalon.framework.activity.Startable#stop() 198 */ 199 public void stop() throws Exception 200 { 201 this.getLogger().debug( "Stopping worker thread ..." ); 202 this.terminateNow = true; 203 this.workerThread.interrupt(); 204 this.workerThread.join( 10000 ); 205 } 206 207 /** 208 * @see org.apache.avalon.framework.activity.Disposable#dispose() 209 */ 210 public void dispose() 211 { 212 this.terminateNow = false; 213 this.applicationDir = null; 214 this.workerThread = null; 215 this.serviceManager = null; 216 this.reconfigurationEntryList = null; 217 } 218 219 /** 220 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration) 221 */ 222 public void reconfigure(Configuration configuration) 223 throws ConfigurationException 224 { 225 this.configure(configuration); 226 } 227 228 ///////////////////////////////////////////////////////////////////////// 229 // Service interface implementation 230 ///////////////////////////////////////////////////////////////////////// 231 232 /** 233 * Polls for changes in the confguration to reconfigure either the 234 * whole container or just a list of services. 235 * 236 * @see java.lang.Runnable#run() 237 */ 238 public void run() 239 { 240 ReconfigurationEntry reconfigurationEntry = null; 241 ReconfigurationEntry[] list = null; 242 243 while( this.terminateNow == false ) 244 { 245 list = this.getReconfigurationEntryList(); 246 247 try 248 { 249 for( int i=0; i<list.length; i++ ) 250 { 251 reconfigurationEntry = list[i]; 252 253 if( reconfigurationEntry.hasChanged() ) 254 { 255 this.onReconfigure( reconfigurationEntry ); 256 } 257 } 258 259 Thread.sleep( this.interval ); 260 } 261 catch( InterruptedException e ) 262 { 263 continue; 264 } 265 catch(Exception e) 266 { 267 String msg = "The ReconfigurationService had a problem"; 268 this.getLogger().error(msg,e); 269 continue; 270 } 271 } 272 } 273 274 ///////////////////////////////////////////////////////////////////////// 275 // Service implementation 276 ///////////////////////////////////////////////////////////////////////// 277 278 /** 279 * Reconfigure either the whole container or a list of services. This 280 * method is called within a seperate worker thred. 281 * 282 * @param reconfigurationEntry the configuration what to reconfigure 283 * @throws Exception the reconfiguration failed 284 */ 285 protected void onReconfigure( ReconfigurationEntry reconfigurationEntry ) 286 throws Exception 287 { 288 if( reconfigurationEntry.getServiceList() == null ) 289 { 290 // reconfigure the whole container using Avalon Lifecycle Spec 291 292 InputStream is = reconfigurationEntry.locate(); 293 DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); 294 Configuration configuration = builder.build(is); 295 is.close(); 296 is = null; 297 298 this.getLogger().warn( "Starting to reconfigure the container" ); 299 300 if( this.serviceManager instanceof Suspendable) 301 { 302 this.getLogger().info( "Calling suspend() of the container" ); 303 ((Suspendable) this.serviceManager).suspend(); 304 } 305 306 if( this.serviceManager instanceof Reconfigurable) 307 { 308 this.getLogger().info( "Calling reconfigure() of the container" ); 309 ((Reconfigurable) this.serviceManager).reconfigure(configuration); 310 } 311 312 if( this.serviceManager instanceof Suspendable) 313 { 314 this.getLogger().info( "Calling resume() of the container" ); 315 ((Suspendable) this.serviceManager).resume(); 316 } 317 318 this.getLogger().info( "Reconfiguring the container was successful" ); 319 } 320 else 321 { 322 String[] serviceList = reconfigurationEntry.getServiceList(); 323 this.getLogger().warn( "Calling reconfigure() on individual services : " + serviceList.length ); 324 this.serviceLifecycleManager.reconfigure(serviceList); 325 } 326 } 327 328 /** 329 * @return Returns the reconfigurationEntryList. 330 */ 331 private synchronized ReconfigurationEntry [] getReconfigurationEntryList() 332 { 333 return reconfigurationEntryList; 334 } 335 336 /** 337 * @param reconfigurationEntryList The reconfigurationEntryList to set. 338 */ 339 private synchronized void setReconfigurationEntryList( 340 ReconfigurationEntry [] reconfigurationEntryList) 341 { 342 this.reconfigurationEntryList = reconfigurationEntryList; 343 } 344 }