/**
   * Add a <code>Package</code> to the network. Iterates through the <code>Package</code> adding
   * Each individual <code>Rule</code> to the network. Before update network each referenced <code>
   * WorkingMemory</code> is locked.
   *
   * @param pkg The package to add.
   * @throws PackageIntegrationException
   * @throws RuleIntegrationException if an error prevents complete construction of the network for
   *     the <code>Rule</code>.
   * @throws FactException
   * @throws InvalidPatternException
   */
  public void addPackage(final Package newPkg) throws PackageIntegrationException {
    newPkg.checkValidity();
    final Package pkg = (Package) this.pkgs.get(newPkg.getName());

    // Iterate each workingMemory and lock it
    // This is so we don't update the Rete network during propagation
    for (final Iterator it = this.workingMemories.keySet().iterator(); it.hasNext(); ) {
      final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) it.next();
      workingMemory.getLock().lock();
    }

    if (pkg != null) {
      mergePackage(pkg, newPkg);
    } else {
      this.pkgs.put(newPkg.getName(), newPkg);
    }

    final Map newGlobals = newPkg.getGlobals();

    // Check that the global data is valid, we cannot change the type
    // of an already declared global variable
    for (final Iterator it = newGlobals.keySet().iterator(); it.hasNext(); ) {
      final String identifier = (String) it.next();
      final Class type = (Class) newGlobals.get(identifier);
      if (this.globals.containsKey(identifier) && !this.globals.get(identifier).equals(type)) {
        throw new PackageIntegrationException(pkg);
      }
    }
    this.globals.putAll(newGlobals);

    final Rule[] rules = newPkg.getRules();

    for (int i = 0; i < rules.length; ++i) {
      addRule(rules[i]);
    }

    this.packageClassLoader.addClassLoader(newPkg.getPackageCompilationData().getClassLoader());

    // Iterate each workingMemory and attempt to fire any rules, that were activated as a result
    // of the new rule addition. Unlock after fireAllRules();
    for (final Iterator it = this.workingMemories.keySet().iterator(); it.hasNext(); ) {
      final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) it.next();

      workingMemory.fireAllRules();
      workingMemory.getLock().unlock();
    }
  }
  public void removeRule(final String packageName, final String ruleName) {
    final Package pkg = (Package) this.pkgs.get(packageName);
    final Rule rule = pkg.getRule(ruleName);
    // Iterate each workingMemory and lock it
    // This is so we don't update the Rete network during propagation
    for (final Iterator it = this.workingMemories.keySet().iterator(); it.hasNext(); ) {
      final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) it.next();
      workingMemory.getLock().lock();
    }
    removeRule(rule);
    pkg.removeRule(rule);

    // Iterate and unlock
    for (final Iterator it = this.workingMemories.keySet().iterator(); it.hasNext(); ) {
      final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) it.next();
      workingMemory.getLock().unlock();
    }
  }
  public void removePackage(final String packageName) {
    final Package pkg = (Package) this.pkgs.get(packageName);
    // Iterate each workingMemory and lock it
    // This is so we don't update the Rete network during propagation
    for (final Iterator it = this.workingMemories.keySet().iterator(); it.hasNext(); ) {
      final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) it.next();
      workingMemory.getLock().lock();
    }

    final Rule[] rules = pkg.getRules();

    for (int i = 0; i < rules.length; ++i) {
      removeRule(rules[i]);
    }

    this.packageClassLoader.removeClassLoader(pkg.getPackageCompilationData().getClassLoader());

    pkg.clear();

    // getting the list of referenced globals
    final Set referencedGlobals = new HashSet();
    for (final Iterator it = this.pkgs.values().iterator(); it.hasNext(); ) {
      final org.drools.rule.Package pkgref = (org.drools.rule.Package) it.next();
      if (pkgref != pkg) {
        referencedGlobals.addAll(pkgref.getGlobals().keySet());
      }
    }
    // removing globals declared inside the package that are not shared
    for (final Iterator it = pkg.getGlobals().keySet().iterator(); it.hasNext(); ) {
      final String globalName = (String) it.next();
      if (!referencedGlobals.contains(globalName)) {
        this.globals.remove(globalName);
      }
    }
    // removing the package itself from the list
    this.pkgs.remove(pkg.getName());

    // Iterate and unlock
    for (final Iterator it = this.workingMemories.keySet().iterator(); it.hasNext(); ) {
      final AbstractWorkingMemory workingMemory = (AbstractWorkingMemory) it.next();
      workingMemory.getLock().unlock();
    }
  }