Coverage report

  %line %branch
nl.toolforge.karma.core.cmd.CommandContext
0% 
0% 

 1  
 /*
 2  
 Karma core - Core of the Karma application
 3  
 Copyright (C) 2004  Toolforge <www.toolforge.nl>
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, write to the Free Software
 17  
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 */
 19  
 package nl.toolforge.karma.core.cmd;
 20  
 
 21  
 import nl.toolforge.core.util.listener.ChangeListener;
 22  
 import nl.toolforge.core.util.listener.ListenerManager;
 23  
 import nl.toolforge.core.util.listener.ListenerManagerException;
 24  
 import nl.toolforge.karma.core.KarmaRuntimeException;
 25  
 import nl.toolforge.karma.core.boot.WorkingContext;
 26  
 import nl.toolforge.karma.core.boot.WorkingContextConfiguration;
 27  
 import nl.toolforge.karma.core.cmd.event.CommandFailedEvent;
 28  
 import nl.toolforge.karma.core.cmd.event.CommandFinishedEvent;
 29  
 import nl.toolforge.karma.core.cmd.event.CommandResponseListener;
 30  
 import nl.toolforge.karma.core.cmd.event.CommandStartedEvent;
 31  
 import nl.toolforge.karma.core.cmd.event.ErrorEvent;
 32  
 import nl.toolforge.karma.core.cmd.event.MessageEvent;
 33  
 import nl.toolforge.karma.core.cmd.event.SimpleMessage;
 34  
 import nl.toolforge.karma.core.location.LocationException;
 35  
 import nl.toolforge.karma.core.manifest.Manifest;
 36  
 import nl.toolforge.karma.core.manifest.ManifestException;
 37  
 import nl.toolforge.karma.core.manifest.ManifestFactory;
 38  
 import nl.toolforge.karma.core.manifest.ManifestLoader;
 39  
 import nl.toolforge.karma.core.manifest.ManifestStructure;
 40  
 import nl.toolforge.karma.core.module.Module;
 41  
 import org.apache.commons.logging.Log;
 42  
 import org.apache.commons.logging.LogFactory;
 43  
 
 44  
 import java.io.File;
 45  
 import java.util.ArrayList;
 46  
 import java.util.Collection;
 47  
 import java.util.HashMap;
 48  
 import java.util.Iterator;
 49  
 import java.util.Map;
 50  
 
 51  
 /**
 52  
  * <p>The command context is the class that provides a runtime for commands to run in. The command context maintains
 53  
  * access to the current manifest and all commands that are valid. A <code>CommandContext</code> must be initialized
 54  
  * through its {@link #init} method so it can initialize all resources it requires to properly run commands. The
 55  
  * <code>init</code> method can only be run once.
 56  
  *
 57  
  * @author D.A. Smedes
 58  
  * @version $Id: CommandContext.java,v 1.83 2004/11/16 22:31:57 asmedes Exp $
 59  
  */
 60  
 public final class CommandContext implements ChangeListener {
 61  
 
 62  0
   private static final Log logger = LogFactory.getLog(CommandContext.class);
 63  
 
 64  
   private Manifest currentManifest;
 65  0
   private Map modificationMap = new HashMap();
 66  0
   private boolean managed = false;
 67  
 
 68  
   private static ListenerManager manager;
 69  
 
 70  0
   private CommandResponseHandler handler = null;
 71  0
   private WorkingContext workingContext = null;
 72  0
   private CommandResponse commandResponse = null;
 73  
 
 74  
   /**
 75  
    * Constructs a <code>CommandContext</code>, in which commands are run.
 76  
    */
 77  0
   public CommandContext(WorkingContext workingContext) {
 78  0
     this.workingContext = workingContext;
 79  0
   }
 80  
 
 81  
   public WorkingContext getWorkingContext() {
 82  0
     return workingContext;
 83  
   }
 84  
 
 85  
   /**
 86  
    * Initializes the context to run commands. This method should only be called once on a <code>CommandContext</code>.
 87  
    *
 88  
    * @param handler        The {@link CommandResponseHandler} object that will be passed to all commands run through this
 89  
    *                       context.
 90  
    * @param updateStores   If this paramter is true, the CommandContext will update the local manifest and location store
 91  
    *                       with the latest manifests and locations.
 92  
    */
 93  
   public synchronized void init(CommandResponseHandler handler, boolean updateStores) throws CommandException {
 94  
 
 95  0
     if (handler == null) {
 96  0
       throw new IllegalArgumentException("CommandResponseHandler may not be null.");
 97  
     }
 98  
 
 99  
     // Initialize a command response object.
 100  
     //
 101  0
     commandResponse = new CommandResponse();
 102  0
     commandResponse.addCommandResponseListener(handler);
 103  0
     setHandler(handler);
 104  
 
 105  
     // Use a command to initialize this further.
 106  
     //
 107  0
     Command command = new KarmaInitializationCommand(updateStores);
 108  
 
 109  0
     command.setContext(this);
 110  0
     command.registerCommandResponseListener(getHandler());
 111  
 
 112  0
     CommandStartedEvent startEvent = new CommandStartedEvent(command);
 113  
     //commandResponse.addEvent(startEvent);
 114  
 
 115  
     try {
 116  0
       command.execute();
 117  0
     } catch (CommandException c) {
 118  0
       commandResponse.addEvent(new ErrorEvent(command, c.getErrorCode(), c.getMessageArguments()));
 119  
       //commandResponse.addEvent(new CommandFailedEvent(command, c));
 120  0
       throw c;
 121  0
     }
 122  
 
 123  0
     command.deregisterCommandResponseListener(handler);
 124  0
     command.cleanUp();
 125  0
   }
 126  
 
 127  
 
 128  
   private synchronized void setFileModificationTimes() {
 129  
 
 130  0
     Manifest manifest = currentManifest;
 131  
 
 132  0
     WorkingContextConfiguration config = workingContext.getConfiguration();
 133  
 
 134  0
     Long lastMod = new Long(class="keyword">new File(config.getManifestStore().getModule().getBaseDir(), manifest.getName() + ".xml").lastModified());
 135  0
     modificationMap.put(manifest, lastMod);
 136  
 
 137  
     try {
 138  0
       Collection includes = manifest.getIncludes();
 139  0
       for (Iterator i = includes.iterator(); i.hasNext();) {
 140  
 
 141  0
         manifest = (Manifest) i.next();
 142  
 
 143  0
         lastMod = new Long(class="keyword">new File(config.getManifestStore().getModule().getBaseDir(), manifest.getName() + ".xml").lastModified());
 144  0
         modificationMap.put(manifest, lastMod);
 145  
       }
 146  0
     } catch (Exception e) {
 147  0
       logger.error(e);
 148  0
       modificationMap.clear();
 149  0
     }
 150  0
   }
 151  
 
 152  
   /**
 153  
    * Implementation of the {@link ChangeListener} interface. This method reloads the
 154  
    * current manifest to allow changes to be reflected without having to restart Karma.
 155  
    */
 156  
   public synchronized void process() {
 157  
 
 158  0
     boolean reload = false;
 159  
 
 160  
     try {
 161  
 
 162  0
       Collection manifests = new ArrayList();
 163  0
       manifests.add(currentManifest);
 164  0
       manifests.addAll(currentManifest.getIncludes());
 165  
 
 166  0
       for (Iterator i = manifests.iterator(); i.hasNext();) {
 167  
 
 168  0
         Manifest m = (Manifest) i.next();
 169  0
         long lastMod = ((Long) modificationMap.get(m)).class="keyword">longValue();
 170  
 
 171  0
         WorkingContextConfiguration config = workingContext.getConfiguration();
 172  
 
 173  
         // todo omslachtig. direct via een getmanifeststore()-achtige.
 174  0
         File f = new File(config.getManifestStore().getModule().getBaseDir(), m.getName() + ".xml");
 175  0
         if (!f.exists()) {
 176  0
           currentManifest = null;
 177  0
           throw new ManifestException(ManifestException.MANIFEST_FILE_NOT_FOUND, class="keyword">new Object[] {m.getName()});
 178  
         }
 179  
 
 180  0
         if (f.lastModclass="keyword">ified() > lastMod) {
 181  0
           reload = true;
 182  0
           break;
 183  
         }
 184  
       }
 185  
 
 186  0
       if (reload) {
 187  
 
 188  
         // One of the manifests in the tree has been changed on disk, reload the full structure.
 189  
         //
 190  0
         ManifestStructure reloadedStructure =
 191  
             getWorkingContext().getManifestLoader().load(currentManifest.getName());
 192  0
         currentManifest = new ManifestFactory().create(workingContext, reloadedStructure);
 193  
 
 194  0
         setFileModificationTimes();
 195  
 
 196  0
         String message = "\nManifest " + getCurrentManifest().getName() + " has changed on disk. Reloaded automatically.\n";
 197  0
         logger.info(message);
 198  
 
 199  0
         commandResponse.addEvent(new MessageEvent(class="keyword">new SimpleMessage(message)));
 200  
 
 201  0
         return;
 202  
       }
 203  
 
 204  0
     } catch (ManifestException m) {
 205  
 
 206  
       // Catches the ManifestException in case the manifest file has disappeared as well.
 207  
       //
 208  0
       managed = false;
 209  0
       manager.suspendListener(this);
 210  
 
 211  0
       logger.error(m);
 212  
 
 213  
       // todo in karma-core-1.1 this should be improved. Right now, the probability of this process failing is remote.
 214  
       //
 215  0
       throw new KarmaRuntimeException(m.getErrorCode(), m.getMessageArguments());
 216  0
     } catch (Exception e) {
 217  
 
 218  0
       managed = false;
 219  0
       manager.suspendListener(this);
 220  
 
 221  0
       logger.error("Error while processing manifests during automatic reload; " + e.getMessage(), e);
 222  0
     }
 223  0
   }
 224  
 
 225  
   /**
 226  
    * Gets the currently active manifest.
 227  
    *
 228  
    * @return The currently active manifest, or <code>null</code> when no manifest is current.
 229  
    */
 230  
   public Manifest getCurrentManifest() {
 231  0
     return currentManifest;
 232  
   }
 233  
 
 234  
   /**
 235  
    * Changes the current manifest for this context. This method loads the manifest with the <code>manifestName</code>
 236  
    * name.
 237  
    *
 238  
    * @param manifestName
 239  
    * @throws ManifestException When the manifest could not be changed. See {@link ManifestException#MANIFEST_LOAD_ERROR}.
 240  
    */
 241  
   public void changeCurrentManifest(String manifestName) throws ManifestException, LocationException {
 242  
 
 243  0
     ManifestFactory manifestFactory = new ManifestFactory();
 244  0
     ManifestLoader loader = new ManifestLoader(workingContext);
 245  0
     Manifest newManifest = manifestFactory.create(workingContext, loader.load(manifestName));
 246  
 
 247  
     // If we are here, loading the new manifest was succesfull.
 248  
     //
 249  0
     currentManifest = newManifest;
 250  
 
 251  0
     register();
 252  0
   }
 253  
 
 254  
   /**
 255  
    * Changes the current manifest for this context. This method assumes a loaded manifest.
 256  
    *
 257  
    * @param newManifest
 258  
    */
 259  
   public void changeCurrentManifest(Manifest newManifest) {
 260  0
     currentManifest = newManifest;
 261  
 
 262  0
     if (currentManclass="keyword">ifest != null) {
 263  0
       register();
 264  
     }
 265  0
   }
 266  
 
 267  
   /**
 268  
    * Registers this <code>CommandContext</code> for automatic manifest file update changes.
 269  
    */
 270  
   synchronized void register() {
 271  
 
 272  0
     setFileModificationTimes();
 273  
 
 274  0
     if (!managed) {
 275  
 
 276  0
       manager = ListenerManager.getInstance();
 277  
       try {
 278  0
         manager.register(this);
 279  0
       } catch (ListenerManagerException e) {
 280  0
         logger.error(e);
 281  0
         throw new KarmaRuntimeException(e.getMessage());
 282  0
       }
 283  
 
 284  0
       manager.start();
 285  
 
 286  0
       managed = true;
 287  
     }
 288  0
   }
 289  
 
 290  
   /**
 291  
    * Gets all manifests.
 292  
    *
 293  
    * @return See <code>ManifestLoader.getAllManifests()</code>.
 294  
    */
 295  
   public Collection getAllManifests() {
 296  0
     return workingContext.getManifestCollector().getAllManifests();
 297  
   }
 298  
 
 299  
   /**
 300  
    * <p>Executes a command. Interface applications should use this method to actually execute a command. When a
 301  
    * <code>KarmaException</code> is thrown an interface applications should <b>*** NOT ***</b> quit program execution as
 302  
    * a result of this exception. It should be handled nicely.
 303  
    *
 304  
    * @param commandLine The command to execute. A full command line is passed as a parameter.
 305  
    * @throws CommandException A whole lot. Interface applications should <b>*** NOT ***</b> quit program execution as a
 306  
    *   result of this exception. It should be handled nicely.
 307  
    */
 308  
   public void execute(String commandLine) throws CommandException {
 309  
 
 310  0
     Command command = null;
 311  
     try {
 312  0
       command = CommandFactory.getInstance().getCommand(commandLine);
 313  0
     } catch (CommandException c) {
 314  0
       logger.error(c.getMessage());
 315  0
       commandResponse.addEvent(new ErrorEvent(c.getErrorCode(), c.getMessageArguments()));
 316  0
       throw c;
 317  0
     } catch (CommandLoadException e) {
 318  0
       logger.error(e.getMessage());
 319  0
       throw new CommandException(e, e.getErrorCode(),  e.getMessageArguments());
 320  0
     }
 321  0
     execute(command);
 322  0
   }
 323  
 
 324  
   /**
 325  
    * Exceutes <code>command</code>.
 326  
    *
 327  
    * @param command The command to execute.
 328  
    * @throws CommandException
 329  
    */
 330  
   public void execute(Command command) throws CommandException {
 331  
 
 332  0
     if (command == null) {
 333  0
       throw new IllegalArgumentException("Invalid command; command cannot be null.");
 334  
     }
 335  
 
 336  
     // Store a reference to this context in the command
 337  
     //
 338  0
     command.setContext(this);
 339  0
     command.registerCommandResponseListener(getHandler());
 340  
     // Register the response handler with this context, so commands have a reference to it.
 341  
     //
 342  
     //todo what happens when an exception occurs in the execute wrt deregister?
 343  
 
 344  0
     CommandStartedEvent startEvent = new CommandStartedEvent(command);
 345  0
     commandResponse.addEvent(startEvent);
 346  
 
 347  
     try {
 348  0
       command.execute();
 349  0
     } catch (CommandException c) {
 350  0
       logger.error(c.getMessage());
 351  0
       commandResponse.addEvent(new ErrorEvent(command, c.getErrorCode(), c.getMessageArguments()));
 352  0
       commandResponse.addEvent(new CommandFailedEvent(command, c));
 353  0
       throw c;
 354  0
     }
 355  0
     commandResponse.addEvent(new CommandFinishedEvent(command, startEvent.getTime()));
 356  
 
 357  0
     command.deregisterCommandResponseListener(getHandler());
 358  0
     command.cleanUp();
 359  0
   }
 360  
 
 361  
   private void setHandler(CommandResponseHandler handler) {
 362  0
     this.handler = handler;
 363  0
   }
 364  
 
 365  
   private CommandResponseListener getHandler() {
 366  0
     return handler;
 367  
   }
 368  
 
 369  
   /**
 370  
    * Checks if a manifest is active for this context.
 371  
    *
 372  
    * @return <code>true</code> if a manifest is active for the context, or <code>false</code> if no manifest is active.
 373  
    */
 374  
   public boolean isManifestLoaded() {
 375  0
     return currentManifest != null;
 376  
   }
 377  
 
 378  
   /**
 379  
    * <p>Some module-types (e.g. source modules) have a physical location on disk where the module can be located. This
 380  
    * method returns a valid reference to that location. When the module-root is located at
 381  
    * <code>/home/jensen/dev/modules/CORE-conversion</code>, <code>getLocalPath()</code> will return a <code>File</code>
 382  
    * handle to that directory.
 383  
    *
 384  
    * @param module The module for which the local path should be retrieved.
 385  
    *
 386  
    * @return A <code>File</code> handle to the module directory on a local disk.
 387  0
    *
 388  0
    * todo consider moving it to Module.
 389  
    */
 390  0
   public File getLocalPath(Module module) {
 391  
 
 392  0
     File localPath = new File(getBase(), module.getName());
 393  0
     logger.debug("getLocalPath() = " + localPath.getPath());
 394  
 
 395  0
     return localPath;
 396  
   }
 397  
 
 398  
   /**
 399  
    * Helper to get the module base for the current manifest.
 400  
    */
 401  
   private File getBase() {
 402  0
     return new File(workingContext.getProjectBaseDirectory(), getCurrentManifest().getName());
 403  
   }
 404  
 
 405  
   /**
 406  
    * Sets the workingContext for this command context.
 407  
    *
 408  
    * @param workingContext
 409  
    */
 410  
   public void setWorkingContext(WorkingContext workingContext) {
 411  0
     this.workingContext = workingContext;
 412  0
   }
 413  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.