/**
   * 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;
    }
  }
  /**
   * 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];
    }
  }