/**
   * Constructor. Initialises the servlet, reading the ontology model from static files and creating
   * a cleaner thread that removes obsolete sessions.
   */
  public LiberServletImpl() {
    System.out.println("Starting up");
    try {
      ontology = new OntologyReader(); // reads PolicyGrid ontologies
      //	ontology = new GeographyOntologyReader(); //reads geography ontology

      clean =
          new TimerTask() {
            public void run() { // 	Removes obsolete sessions from the map, running every 10 min.
              long time = new GregorianCalendar().getTimeInMillis();
              synchronized (userMap) {
                Iterator it = userMap.keySet().iterator();
                List<String> obsolete = new ArrayList<String>();
                while (it.hasNext()) {
                  String key = (String) it.next();
                  long timeDif = time - userMap.get(key).getLastUpdated();
                  if ((timeDif < 0)
                      || (timeDif > 3600000)) // if an hour has lapsed, or the next day has started
                  obsolete.add(key); // (let's just hope no-one starts using the tool at midnight)
                }
                for (int i = 0; i < obsolete.size(); i++) // then remove that session
                userMap.remove(obsolete.get(i));
              }
            }
          };
      Timer timer = new Timer();
      timer.schedule(clean, 0, 600000);

      Runtime r = Runtime.getRuntime();
      r.gc(); // FORCES GARBAGE COLLECTION
      r.runFinalization();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  /**
   * Creates a new session for the editing tool.
   *
   * @param user user id
   * @param project Project ID
   * @param resource Resource ID
   * @return String[] with user id and name
   */
  public String[] newSession(String user, String project, String resource) {
    try {
      LiberSession session = new LiberSession(ontology, user, project, resource, LiberSession.EDIT);
      userMap.put(user, session);

      Runtime r = Runtime.getRuntime();
      r.gc(); // FORCES GARBAGE COLLECTION
      r.runFinalization();
      return session.getUserDetails();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
  /**
   * Called when the users asks to show or hide all known information about a certain object from
   * the database.
   *
   * @param user User ID
   * @param anchor Unique ID of Anchor
   * @param show If true, show more information, if false, hide information
   * @param type Session type (edit, query, browse)
   * @param key browsing session id (null if session type is EDIT)
   * @return AnchorInfo[] with serialisable version of feedback text, empty if the user's session is
   *     expired or null if an error occurred
   */
  public AnchorInfo[] changeTextContent(
      String user, String anchor, boolean show, int type, String key) {
    if (!userMap.containsKey(user)) return new AnchorInfo[0];

    LiberSession session = userMap.get(user);

    Runtime r = Runtime.getRuntime();
    r.gc(); // FORCES GARBAGE COLLECTION
    r.runFinalization();

    synchronized (session) {
      session.setLastUpdated();
      return session.changeTextContent(anchor, show, type, key);
    }
  }
  /**
   * Ends this particular session. If it's a browsing session, the browsing generator is removed;
   * otherwise, the entire user's session can be removed.
   *
   * @param user user id
   * @param type type of session
   * @param key browsing session id (null if session type is EDIT)
   */
  public void endSession(String user, int type, String key) {
    if (!userMap.containsKey(user)) return;

    if (type == LiberSession.BROWSE) {
      LiberSession session = userMap.get(user);
      synchronized (session) {
        session.removeBrowsingGenerator(key);
      }
    } else {
      if (type == LiberSession.QUERY) userMap.get(user).printQueryTimes();
      userMap.remove(user);
    }

    Runtime r = Runtime.getRuntime();
    r.gc(); // FORCES GARBAGE COLLECTION
    r.runFinalization();
  }
  /**
   * Initialises a new editing session.
   *
   * @param user user id
   * @param data Data provided about resource
   * @return AnchorInfo[] containing the feedback text.
   */
  public AnchorInfo[] initSession(String user, InstanceData data) {
    try {
      LiberSession session = userMap.get(user);

      Runtime r = Runtime.getRuntime();
      r.gc(); // FORCES GARBAGE COLLECTION
      r.runFinalization();

      synchronized (session) {
        session.setLastUpdated();
        return session.init(data); // initialise the session and return the text
      }
    } catch (Exception e) {
      e.printStackTrace();
      return new AnchorInfo[0];
    }
  }
  /**
   * Initialises a new query session, and returns the class hierarchy. This hierarchy is special
   * because it includes the number of instances of each type.
   *
   * @param user user id
   * @param roots root classes of the hierarchy (null to get complete hierarchy)
   * @return Hierarchy[] containing class hierarchy
   */
  public Hierarchy[] initSessionAndGetClassHierarchy(String user, String[] roots) {
    try {
      if (userMap.containsKey(user)) userMap.get(user).setLastUpdated();
      else userMap.put(user, new LiberSession(ontology, user, null, null, LiberSession.QUERY));

      LiberSession session = userMap.get(user);

      Runtime r = Runtime.getRuntime();
      r.gc(); // FORCES GARBAGE COLLECTION
      r.runFinalization();

      synchronized (session) {
        return session.getCountedClassHierarchy(roots);
      }
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
 public static void fullGC(boolean verbose) {
   if (verbose)
     System.out.print(
         new Date().toString()
             + ' '
             + String.valueOf((RUNTIME.totalMemory() - RUNTIME.freeMemory()) / 1024L)
             + "Kb used");
   long isFree = RUNTIME.freeMemory();
   long wasFree;
   do {
     wasFree = isFree;
     RUNTIME.runFinalization();
     RUNTIME.gc();
     isFree = RUNTIME.freeMemory();
   } while (isFree > wasFree);
   if (verbose)
     System.out.println(
         " --> "
             + String.valueOf((RUNTIME.totalMemory() - RUNTIME.freeMemory()) / 1024L)
             + "Kb used");
 }
  /**
   * Initialises a new browsing session, creating a new session for this user.
   *
   * @param user user id
   * @param resource Resource id
   * @param type Type of session (edit, query, browse)
   * @return AnchorInfo[] containing the feedback text.
   */
  public AnchorInfo[] initSession(String user, String resource, int type) {
    try {
      if (type == LiberSession.BROWSE) // make a new user session if this is the browse tab
      userMap.put(
            user, new LiberSession(ontology, user, null, resource.replaceAll("\"", ""), type));
      else if (!userMap.containsKey(user)) return null;

      LiberSession session = userMap.get(user);
      Runtime r = Runtime.getRuntime();
      r.gc(); // FORCES GARBAGE COLLECTION
      r.runFinalization();

      synchronized (session) {
        session.setLastUpdated();
        return session.init(
            resource.replaceAll("\"", "")); // initialise the session and return the text
      }
    } catch (Exception e) {
      e.printStackTrace();
      return new AnchorInfo[0];
    }
  }
  /**
   * Performs intraprocedural BLOAT on a program's live methods.
   *
   * @param liveMethods Should be alphabetized. This way we can commit a class once we've BLOATed
   *     all of its methods.
   */
  private static void intraBloat(final Collection liveMethods, final BloatContext context) {

    ClassEditor prevClass = null;
    final Iterator iter = liveMethods.iterator();
    for (int count = 0; iter.hasNext(); count++) {
      MethodEditor live = null;
      ClassEditor ce = null; // Hack to make sure commit happens
      try {
        live = context.editMethod((MemberRef) iter.next());
        ce = context.editClass(live.declaringClass().classInfo());

      } catch (final NoSuchMethodException ex3) {
        BloatBenchmark.err.println("** Could not find method " + ex3.getMessage());
        System.exit(1);
      }

      /* So we can skip classes or packages */
      final String name = ce.type().className();
      final String qual = ce.type().qualifier() + "/*";
      boolean skip = false;
      for (int i = 0; i < BloatBenchmark.SKIP.size(); i++) {
        final String pkg = (String) BloatBenchmark.SKIP.get(i);

        if (name.equals(pkg) || qual.equals(pkg)) {
          skip = true;
          break;
        }
      }

      if (context.ignoreMethod(live.memberRef()) || skip) {
        // Don't display ignored methods, it's misleading.
        context.release(live.methodInfo());
        continue;
      }

      final Runtime runtime = Runtime.getRuntime();
      runtime.gc();

      final Date start = new Date();
      BloatBenchmark.tr(
          "  " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type());
      BloatBenchmark.tr("    Start: " + start);

      try {
        EDU.purdue.cs.bloat.optimize.Main.TRACE = BloatBenchmark.TRACE;
        if (!BloatBenchmark.VERIFY) {
          EDU.purdue.cs.bloat.optimize.Main.VERIFY = false;
        }
        EDU.purdue.cs.bloat.optimize.Main.bloatMethod(live, context);

      } catch (final Exception oops) {
        BloatBenchmark.err.println("******************************************");
        BloatBenchmark.err.println(
            "Exception while BLOATing "
                + live.declaringClass().name()
                + "."
                + live.name()
                + live.type());
        BloatBenchmark.err.println(oops.getMessage());
        oops.printStackTrace(System.err);
        BloatBenchmark.err.println("******************************************");
      }

      // Commit here in an attempt to conserve memory
      context.commit(live.methodInfo());
      context.release(live.methodInfo());

      if (prevClass == null) {
        prevClass = ce;

      } else if (!prevClass.equals(ce)) {
        // We've finished BLOATed the methods for prevClass, commit
        // prevClass and move on
        BloatBenchmark.tr(prevClass.type() + " != " + ce.type());
        context.commit(prevClass.classInfo());
        context.release(prevClass.classInfo());
        // context.commitDirty();
        // tr(context.toString());
        prevClass = ce;

      } else {
        context.release(ce.classInfo());
      }

      final Date end = new Date();
      BloatBenchmark.tr("    Ellapsed time: " + (end.getTime() - start.getTime()) + " ms");
    }

    context.commitDirty();
  }