Gate.java
0001 /*
0002  *  Gate.java
0003  *
0004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Hamish Cunningham, 31/07/98
0013  *
0014  *  $Id: Gate.java 13569 2011-03-28 14:15:16Z ian_roberts $
0015  */
0016 
0017 package gate;
0018 
0019 import gate.config.ConfigDataProcessor;
0020 import gate.creole.CreoleRegisterImpl;
0021 import gate.creole.ResourceData;
0022 import gate.creole.metadata.CreoleResource;
0023 import gate.event.CreoleListener;
0024 import gate.util.Benchmark;
0025 import gate.util.Files;
0026 import gate.util.GateClassLoader;
0027 import gate.util.GateException;
0028 import gate.util.GateRuntimeException;
0029 import gate.util.LuckyException;
0030 import gate.util.OptionsMap;
0031 import gate.util.Strings;
0032 import gate.util.asm.AnnotationVisitor;
0033 import gate.util.asm.ClassReader;
0034 import gate.util.asm.ClassVisitor;
0035 import gate.util.asm.Opcodes;
0036 import gate.util.asm.Type;
0037 import gate.util.asm.commons.EmptyVisitor;
0038 
0039 import java.io.File;
0040 import java.io.FileInputStream;
0041 import java.io.FileOutputStream;
0042 import java.io.IOException;
0043 import java.io.InputStream;
0044 import java.io.OutputStreamWriter;
0045 import java.net.HttpURLConnection;
0046 import java.net.InetAddress;
0047 import java.net.MalformedURLException;
0048 import java.net.URI;
0049 import java.net.URL;
0050 import java.net.URLClassLoader;
0051 import java.net.URLConnection;
0052 import java.net.UnknownHostException;
0053 import java.util.ArrayList;
0054 import java.util.Collections;
0055 import java.util.EventListener;
0056 import java.util.HashMap;
0057 import java.util.HashSet;
0058 import java.util.Iterator;
0059 import java.util.LinkedHashMap;
0060 import java.util.List;
0061 import java.util.Map;
0062 import java.util.Set;
0063 import java.util.StringTokenizer;
0064 import java.util.jar.JarEntry;
0065 import java.util.jar.JarInputStream;
0066 
0067 import org.apache.log4j.Logger;
0068 import org.jdom.Attribute;
0069 import org.jdom.Element;
0070 import org.jdom.JDOMException;
0071 import org.jdom.input.SAXBuilder;
0072 
0073 /**
0074  * The class is responsible for initialising the GATE libraries, and providing
0075  * access to singleton utility objects, such as the GATE class loader, CREOLE
0076  * register and so on.
0077  */
0078 public class Gate implements GateConstants {
0079 
0080   /** A logger to use instead of sending messages to Out or Err **/
0081   protected static final Logger log = Logger.getLogger(Gate.class);
0082 
0083   /**
0084    * The default StringBuffer size, it seems that we need longer string than the
0085    * StringBuffer class default because of the high number of buffer expansions
0086    */
0087   public static final int STRINGBUFFER_SIZE = 1024;
0088 
0089   /**
0090    * The default size to be used for Hashtable, HashMap and HashSet. The defualt
0091    * is 11 and it leads to big memory usage. Having a default load factor of
0092    * 0.75, table of size 4 can take 3 elements before being re-hashed - a values
0093    * that seems to be optimal for most of the cases.
0094    */
0095   public static final int HASH_STH_SIZE = 4;
0096 
0097   /**
0098    * The database schema owner (GATEADMIN is default) this one should not be
0099    * hardcoded but set in the XML initialization files
0100    *
0101    */
0102   public static final String DB_OWNER = "gateadmin";
0103 
0104   /** The list of builtin URLs to search for CREOLE resources. */
0105   private static String builtinCreoleDirectoryUrls[] {
0106   // "http://derwent.dcs.shef.ac.uk/gate.ac.uk/creole/"
0107 
0108     // this has been moved to initCreoleRegister and made relative to
0109     // the base URL returned by getUrl()
0110     // "http://gate.ac.uk/creole/"
0111     };
0112 
0113   /** The GATE URI used to interpret custom GATE tags */
0114   public static final String URI = "http://www.gate.ac.uk";
0115 
0116   /** Minimum version of JDK we support */
0117   protected static final String MIN_JDK_VERSION = "1.5";
0118 
0119   /**
0120    * Feature name that should be used to set if the benchmarking logging should
0121    * be enabled or disabled.
0122    */
0123   protected static final String ENABLE_BENCHMARKING_FEATURE_NAME =
0124     "gate.enable.benchmark";
0125 
0126   /** Is true if GATE is to be run in a sandbox **/
0127   private static boolean sandboxed = false;
0128 
0129   /**
0130    * Find out if GATE is to be run in a sandbox or not. If true then
0131    * GATE will not attempt to load any local configuration information during
0132    * Initialisation making it possible to use GATE from within unsigned
0133    * applets and web start applications.
0134    @return true if GATE is to be run in a sandbox, false otherwise
0135    */
0136   public static boolean isSandboxed() {
0137     return sandboxed;
0138   }
0139 
0140   /**
0141    * Method to tell GATE if it is being run inside a JVM sandbox. If true then
0142    * GATE will not attempt to load any local configuration information during
0143    * Initialisation making it possible to use GATE from within unsigned
0144    * applets and web start applications.
0145    @param sandboxed true if GATE is to be run in a sandbox, false otherwise
0146    */
0147   public static void runInSandbox(boolean sandboxed) {
0148     if (initFinished)
0149       throw new IllegalStateException("Sandbox status cannot be changed after GATE has been initialised!");
0150 
0151     Gate.sandboxed = sandboxed;
0152   }
0153 
0154   /** Get the minimum supported version of the JDK */
0155   public static String getMinJdkVersion() {
0156     return MIN_JDK_VERSION;
0157   }
0158 
0159   /**
0160    * Initialisation - must be called by all clients before using any other parts
0161    * of the library. Also initialises the CREOLE register and reads config data (<TT>gate.xml</TT>
0162    * files).
0163    *
0164    @see #initCreoleRegister
0165    */
0166   public static void init() throws GateException {
0167     // init local paths
0168     if (!sandboxedinitLocalPaths();
0169 
0170     // check if benchmarking should be enabled or disabled
0171     if(System.getProperty(ENABLE_BENCHMARKING_FEATURE_NAME!= null
0172       && System.getProperty(ENABLE_BENCHMARKING_FEATURE_NAME).equalsIgnoreCase(
0173         "true")) {
0174       Benchmark.setBenchmarkingEnabled(true);
0175     }
0176 
0177     // builtin creole dir
0178     if(builtinCreoleDir == null) {
0179       String builtinCreoleDirPropertyValue =
0180         System.getProperty(BUILTIN_CREOLE_DIR_PROPERTY_NAME);
0181       if(builtinCreoleDirPropertyValue == null) {
0182         // default to /gate/resources/creole in gate.jar
0183         builtinCreoleDir = Files.getGateResource("/creole/");
0184       }
0185       else {
0186         String builtinCreoleDirPath = builtinCreoleDirPropertyValue;
0187         // add a slash onto the end of the path if it doesn't have one already -
0188         // a creole directory URL should always end with a forward slash
0189         if(!builtinCreoleDirPath.endsWith("/")) {
0190           builtinCreoleDirPath += "/";
0191         }
0192         try {
0193           builtinCreoleDir = new URL(builtinCreoleDirPath);
0194         }
0195         catch(MalformedURLException mue) {
0196           // couldn't parse as a File either, so throw an exception
0197           throw new GateRuntimeException(BUILTIN_CREOLE_DIR_PROPERTY_NAME
0198             " value \"" + builtinCreoleDirPropertyValue + "\" could"
0199             " not be parsed as either a URL or a file path.");
0200         }
0201         log.info("Using " + builtinCreoleDir + " as built-in CREOLE"
0202           " directory URL");
0203       }
0204     }
0205 
0206     // register the URL handler for the "gate://" URLs
0207     System.setProperty("java.protocol.handler.pkgs", System
0208       .getProperty("java.protocol.handler.pkgs")
0209       "|" "gate.util.protocols");
0210 
0211     // initialise the symbols generator
0212     lastSym = 0;
0213 
0214     // create class loader and creole register if they're null
0215     if(classLoader == null)
0216       classLoader = new GateClassLoader(Gate.class.getClassLoader());
0217     if(creoleRegister == nullcreoleRegister = new CreoleRegisterImpl();
0218     if(knownPlugins == nullknownPlugins = new ArrayList<URL>();
0219     if(autoloadPlugins == nullautoloadPlugins = new ArrayList<URL>();
0220     if(pluginData == nullpluginData = new HashMap<URL, DirectoryInfo>();
0221     // init the creole register
0222     initCreoleRegister();
0223     // init the data store register
0224     initDataStoreRegister();
0225 
0226     // read gate.xml files; this must come before creole register
0227     // initialisation in order for the CREOLE-DIR elements to have and effect
0228     if (!sandboxedinitConfigData();
0229 
0230     if (!sandboxedinitCreoleRepositories();
0231     // the creoleRegister acts as a proxy for datastore related events
0232     dataStoreRegister.addCreoleListener(creoleRegister);
0233 
0234     // some of the events are actually fired by the {@link gate.Factory}
0235     Factory.addCreoleListener(creoleRegister);
0236 
0237     // check we have a useable JDK
0238     if(System.getProperty("java.version").compareTo(MIN_JDK_VERSION0) { throw new GateException(
0239       "GATE requires JDK " + MIN_JDK_VERSION + " or newer")}
0240 
0241     // register Lucene as a IR search engine
0242     try {
0243       registerIREngine("gate.creole.ir.lucene.LuceneIREngine");
0244     }
0245     catch(ClassNotFoundException cnfe) {
0246       throw new GateRuntimeException(cnfe);
0247     }
0248 
0249     initFinished = true;
0250   // init()
0251 
0252   /** Have we successfully run {@link #init()} before? */
0253   public static boolean isInitialised() { return initFinished; }
0254 
0255   /** Records initialisation status. */
0256   protected static boolean initFinished = false;
0257 
0258   /**
0259    * Initialises the paths to local files of interest like the GATE home, the
0260    * installed plugins home and site and user configuration files.
0261    */
0262   protected static void initLocalPaths() {
0263     // GATE Home
0264     if(gateHome == null) {
0265       String gateHomeStr = System.getProperty(GATE_HOME_PROPERTY_NAME);
0266       if(gateHomeStr != null && gateHomeStr.length() 0) {
0267         gateHome = new File(gateHomeStr);
0268       }
0269       // if failed, try to guess
0270       if(gateHome == null || !gateHome.exists()) {
0271         log.warn("GATE home system property (\"" + GATE_HOME_PROPERTY_NAME
0272           "\") not set.\nAttempting to guess...");
0273         URL gateURL =
0274           Thread.currentThread().getContextClassLoader().getResource(
0275             "gate/Gate.class");
0276         try {
0277           if(gateURL.getProtocol().equals("jar")) {
0278             // running from gate.jar
0279             String gateURLStr = gateURL.getFile();
0280             File gateJarFile =
0281               new File(
0282                 new URI(gateURLStr.substring(0, gateURLStr.indexOf('!'))));
0283             gateHome = gateJarFile.getParentFile().getParentFile();
0284           }
0285           else if(gateURL.getProtocol().equals("file")) {
0286             // running from classes directory
0287             File gateClassFile = Files.fileFromURL(gateURL);
0288             gateHome =
0289               gateClassFile.getParentFile().getParentFile().getParentFile();
0290           }
0291           log.warn("Using \"" + gateHome.getCanonicalPath()
0292             "\" as GATE Home.\nIf this is not correct please set it manually"
0293             " using the -D" + GATE_HOME_PROPERTY_NAME
0294             " option in your start-up script");
0295         }
0296         catch(Throwable thr) {
0297           throw new GateRuntimeException(
0298             "Cannot guess GATE Home. Pease set it manually!", thr);
0299         }
0300       }
0301     }
0302     log.info("Using " + gateHome.toString() " as GATE home");
0303 
0304     // Plugins home
0305     if(pluginsHome == null) {
0306       String pluginsHomeStr = System.getProperty(PLUGINS_HOME_PROPERTY_NAME);
0307       if(pluginsHomeStr != null && pluginsHomeStr.length() 0) {
0308         File homeFile = new File(pluginsHomeStr);
0309         if(homeFile.exists() && homeFile.isDirectory()) {
0310           pluginsHome = homeFile;
0311         }
0312       }
0313       // if not set, use the GATE Home as a base directory
0314       if(pluginsHome == null) {
0315         File homeFile = new File(gateHome, PLUGINS);
0316         if(homeFile.exists() && homeFile.isDirectory()) {
0317           pluginsHome = homeFile;
0318         }
0319       }
0320       // if still not set, throw exception
0321       if(pluginsHome == null) { throw new GateRuntimeException(
0322         "Could not infer installed plug-ins home!\n"
0323           "Please set it manually using the -D" + PLUGINS_HOME_PROPERTY_NAME
0324           " option in your start-up script.")}
0325     }
0326     log.info("Using " + pluginsHome.toString()
0327       " as installed plug-ins directory.");
0328 
0329     // site config
0330     if(siteConfigFile == null) {
0331       String siteConfigStr = System.getProperty(SITE_CONFIG_PROPERTY_NAME);
0332       if(siteConfigStr != null && siteConfigStr.length() 0) {
0333         File configFile = new File(siteConfigStr);
0334         if(configFile.exists()) siteConfigFile = configFile;
0335       }
0336       // if not set, use GATE home as base directory
0337       if(siteConfigFile == null) {
0338         File configFile = new File(gateHome, GATE_DOT_XML);
0339         if(configFile.exists()) siteConfigFile = configFile;
0340       }
0341       // if still not set, throw exception
0342       if(siteConfigFile == null) { throw new GateRuntimeException(
0343         "Could not locate the site configuration file!\n"
0344           "Please create it at "
0345           new File(gateHome, GATE_DOT_XML).toString()
0346           " or point to an existing one using the -D"
0347           + SITE_CONFIG_PROPERTY_NAME + " option in your start-up script!")}
0348     }
0349     log.info("Using " + siteConfigFile.toString()
0350       " as site configuration file.");
0351 
0352     // user config
0353     if(userConfigFile == null) {
0354       String userConfigStr = System.getProperty(USER_CONFIG_PROPERTY_NAME);
0355       if(userConfigStr != null && userConfigStr.length() 0) {
0356         userConfigFile = new File(userConfigStr);
0357       else {
0358         userConfigFile = new File(getDefaultUserConfigFileName());
0359       }
0360     }
0361     log.info("Using " + userConfigFile + " as user configuration file");
0362 
0363     // user session
0364     if(userSessionFile == null) {
0365       String userSessionStr =
0366         System.getProperty(GATE_USER_SESSION_PROPERTY_NAME);
0367       if(userSessionStr != null && userSessionStr.length() 0) {
0368         userSessionFile = new File(userSessionStr);
0369       }
0370       else {
0371         userSessionFile = new File(getDefaultUserSessionFileName());
0372       }
0373     }// if(userSessionFile == null)
0374     log.info("Using " + userSessionFile + " as user session file");
0375   }
0376 
0377   /**
0378    * Loads the CREOLE repositories (aka plugins) that the user has selected for
0379    * automatic loading. Loads the information about known plugins in memory.
0380    */
0381   protected static void initCreoleRepositories() {
0382     // the logic is:
0383     // get the list of know plugins from gate.xml
0384     // add all the installed plugins
0385     // get the list of loadable plugins
0386     // load loadable plugins
0387 
0388     // process the known plugins list
0389     String knownPluginsPath =
0390       (String)getUserConfig().get(KNOWN_PLUGIN_PATH_KEY);
0391     if(knownPluginsPath != null && knownPluginsPath.length() 0) {
0392       StringTokenizer strTok =
0393         new StringTokenizer(knownPluginsPath, ";"false);
0394       while(strTok.hasMoreTokens()) {
0395         String aKnownPluginPath = strTok.nextToken();
0396         try {
0397           URL aPluginURL = new URL(aKnownPluginPath);
0398           addKnownPlugin(aPluginURL);
0399         }
0400         catch(MalformedURLException mue) {
0401           log.error("Plugin error: " + aKnownPluginPath + " is an invalid URL!");
0402         }
0403       }
0404     }
0405     // add all the installed plugins
0406     // pluginsHome is now set by initLocalPaths
0407     // File pluginsHome = new File(System.getProperty(GATE_HOME_PROPERTY_NAME),
0408     // "plugins");
0409     File[] dirs = pluginsHome.listFiles();
0410     for(int i = 0; i < dirs.length; i++) {
0411       File creoleFile = new File(dirs[i]"creole.xml");
0412       if(creoleFile.exists()) {
0413         try {
0414           URL pluginURL = dirs[i].toURI().toURL();
0415           addKnownPlugin(pluginURL);
0416         }
0417         catch(MalformedURLException mue) {
0418           // this should never happen
0419           throw new GateRuntimeException(mue);
0420         }
0421       }
0422     }
0423 
0424     // process the autoload plugins
0425     String pluginPath = getUserConfig().getString(AUTOLOAD_PLUGIN_PATH_KEY);
0426     // can be overridden by system property
0427     String prop = System.getProperty(AUTOLOAD_PLUGIN_PATH_PROPERTY_NAME);
0428     if(prop != null && prop.length() 0pluginPath = prop;
0429 
0430     if(pluginPath == null || pluginPath.length() == 0) {
0431       // no plugin to load stop here
0432       return;
0433     }
0434 
0435     // load all loadable plugins
0436     StringTokenizer strTok = new StringTokenizer(pluginPath, ";"false);
0437     while(strTok.hasMoreTokens()) {
0438       String aDir = strTok.nextToken();
0439       try {
0440         URL aPluginURL = new URL(aDir);
0441         addAutoloadPlugin(aPluginURL);
0442       }
0443       catch(MalformedURLException mue) {
0444         log.error("Cannot load " + aDir + " CREOLE repository.",mue);
0445       }
0446       try {
0447         Iterator<URL> loadPluginsIter = getAutoloadPlugins().iterator();
0448         while(loadPluginsIter.hasNext()) {
0449           getCreoleRegister().registerDirectories(loadPluginsIter.next());
0450         }
0451       }
0452       catch(GateException ge) {
0453         log.error("Cannot load " + aDir + " CREOLE repository.",ge);
0454       }
0455     }
0456   }
0457 
0458   /** Initialise the CREOLE register. */
0459   public static void initCreoleRegister() throws GateException {
0460 
0461     // register the builtin CREOLE directories
0462     for(int i = 0; i < builtinCreoleDirectoryUrls.length; i++)
0463       try {
0464         creoleRegister.addDirectory(new URL(builtinCreoleDirectoryUrls[i]));
0465       }
0466       catch(MalformedURLException e) {
0467         throw new GateException(e);
0468       }
0469 
0470     /*
0471      * We'll have to think about this. Right now it points to the creole inside
0472      * the jar/classpath so it's the same as registerBuiltins
0473      */
0474     // // add the GATE base URL creole directory
0475     // creoleRegister.addDirectory(Gate.getUrl("creole/"));
0476     // creoleRegister.registerDirectories();
0477     // register the resources that are actually in gate.jar
0478     creoleRegister.registerBuiltins();
0479   // initCreoleRegister
0480 
0481   /** Initialise the DataStore register. */
0482   public static void initDataStoreRegister() {
0483     dataStoreRegister = new DataStoreRegister();
0484   // initDataStoreRegister()
0485 
0486   /**
0487    * Reads config data (<TT>gate.xml</TT> files). There are three sorts of
0488    * these files:
0489    <UL>
0490    <LI> The builtin file from GATE's resources - this is read first.
0491    <LI> A site-wide init file given as a command-line argument or as a
0492    <TT>gate.config</TT> property - this is read second.
0493    <LI> The user's file from their home directory - this is read last.
0494    </UL>
0495    * Settings from files read after some settings have already been made will
0496    * simply overwrite the previous settings.
0497    */
0498   public static void initConfigData() throws GateException {
0499     ConfigDataProcessor configProcessor = new ConfigDataProcessor();
0500     // parse the site configuration file
0501     URL configURL;
0502     try {
0503       configURL = siteConfigFile.toURI().toURL();
0504     }
0505     catch(MalformedURLException mue) {
0506       // this should never happen
0507       throw new GateRuntimeException(mue);
0508     }
0509     try {
0510       InputStream configStream = new FileInputStream(siteConfigFile);
0511       configProcessor.parseConfigFile(configStream, configURL);
0512     }
0513     catch(IOException e) {
0514       throw new GateException("Couldn't open site configuration file: "
0515         + configURL + " " + e);
0516     }
0517 
0518     // parse the user configuration data if present
0519     if(userConfigFile != null && userConfigFile.exists()) {
0520       try {
0521         configURL = userConfigFile.toURI().toURL();
0522       }
0523       catch(MalformedURLException mue) {
0524         // this should never happen
0525         throw new GateRuntimeException(mue);
0526       }
0527       try {
0528         InputStream configStream = new FileInputStream(userConfigFile);
0529         configProcessor.parseConfigFile(configStream, configURL);
0530       }
0531       catch(IOException e) {
0532         throw new GateException("Couldn't open user configuration file: "
0533           + configURL + " " + e);
0534       }
0535     }
0536 
0537     // remember the init-time config options
0538     originalUserConfig.putAll(userConfig);
0539 
0540     log.debug("user config loaded; DBCONFIG="
0541         + DataStoreRegister.getConfigData());
0542   // initConfigData()
0543 
0544   /**
0545    * Attempts to guess the Unicode font for the platform.
0546    */
0547   public static String guessUnicodeFont() {
0548     // guess the Unicode font for the platform
0549     String[] fontNames =
0550       java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
0551         .getAvailableFontFamilyNames();
0552     String unicodeFontName = null;
0553     for(int i = 0; i < fontNames.length; i++) {
0554       if(fontNames[i].equalsIgnoreCase("Arial Unicode MS")) {
0555         unicodeFontName = fontNames[i];
0556         break;
0557       }
0558       if(fontNames[i].toLowerCase().indexOf("unicode"!= -1) {
0559         unicodeFontName = fontNames[i];
0560       }
0561     }// for(int i = 0; i < fontNames.length; i++)
0562     return unicodeFontName;
0563   }
0564 
0565   /**
0566    * Get a URL that points to either an HTTP server or a file system that
0567    * contains GATE files (such as test cases). The following locations are tried
0568    * in sequence:
0569    <UL>
0570    <LI> <TT>http://derwent.dcs.shef.ac.uk/gate.ac.uk/</TT>, a
0571    * Sheffield-internal development server (the gate.ac.uk affix is a copy of
0572    * the file system present on GATE's main public server - see next item);
0573    <LI> <TT>http://gate.ac.uk/</TT>, GATE's main public server;
0574    <LI> <TT>http://localhost/gate.ac.uk/</TT>, a Web server running on the
0575    * local machine;
0576    <LI> the local file system where the binaries for the current invocation of
0577    * GATE are stored.
0578    </UL>
0579    * In each case we assume that a Web server will be running on port 80, and
0580    * that if we can open a socket to that port then the server is running. (This
0581    * is a bit of a strong assumption, but this URL is used largely by the test
0582    * suite, so we're not betting anything too critical on it.)
0583    <P>
0584    * Note that the value returned will only be calculated when the existing
0585    * value recorded by this class is null (which will be the case when neither
0586    * setUrlBase nor getUrlBase have been called, or if setUrlBase(null) has been
0587    * called).
0588    */
0589   public static URL getUrl() throws GateException {
0590     if(urlBase != nullreturn urlBase;
0591 
0592     try {
0593 
0594       // if we're assuming a net connection, try network servers
0595       if(isNetConnected()) {
0596         if(
0597         // tryNetServer("gate-internal.dcs.shef.ac.uk", 80, "/") ||
0598         // tryNetServer("derwent.dcs.shef.ac.uk", 80, "/gate.ac.uk/") ||
0599         tryNetServer("gate.ac.uk"80"/")) {
0600           log.debug("getUrl() returned " + urlBase);
0601           return urlBase;
0602         }
0603       // if isNetConnected() ...
0604 
0605       // no network servers; try for a local host web server.
0606       // we use InetAddress to get host name instead of using "localhost" coz
0607       // badly configured Windoze IP sometimes doesn't resolve the latter
0608       if(isLocalWebServer()
0609         && tryNetServer(InetAddress.getLocalHost().getHostName()80,
0610           "/gate.ac.uk/")) {
0611         log.debug("getUrlBase() returned " + urlBase);
0612         return urlBase;
0613       }
0614 
0615       // try the local file system
0616       tryFileSystem();
0617 
0618     }
0619     catch(MalformedURLException e) {
0620       throw new GateException("Bad URL, getUrlBase(): " + urlBase + ": " + e);
0621     }
0622     catch(UnknownHostException e) {
0623       throw new GateException("No host, getUrlBase(): " + urlBase + ": " + e);
0624     }
0625 
0626     // return value will be based on the file system, or null
0627     log.debug("getUrlBase() returned " + urlBase);
0628     return urlBase;
0629   // getUrl()
0630 
0631   /**
0632    * Get a URL that points to either an HTTP server or a file system that
0633    * contains GATE files (such as test cases). Calls <TT>getUrl()</TT> then
0634    * adds the <TT>path</TT> parameter to the result.
0635    *
0636    @param path
0637    *          a path to add to the base URL.
0638    @see #getUrl()
0639    */
0640   public static URL getUrl(String paththrows GateException {
0641     getUrl();
0642     if(urlBase == nullreturn null;
0643 
0644     URL newUrl = null;
0645     try {
0646       newUrl = new URL(urlBase, path);
0647     }
0648     catch(MalformedURLException e) {
0649       throw new GateException("Bad URL, getUrl( " + path + "): " + e);
0650     }
0651 
0652     log.debug("getUrl(" + path + ") returned " + newUrl);
0653     return newUrl;
0654   // getUrl(path)
0655 
0656   /**
0657    * Flag controlling whether we should try to access the net, e.g. when setting
0658    * up a base URL.
0659    */
0660   private static boolean netConnected = false;
0661 
0662   private static int lastSym;
0663 
0664   /**
0665    * A list of names of classes that implement {@link gate.creole.ir.IREngine}
0666    * that will be used as information retrieval engines.
0667    */
0668   private static Set<String> registeredIREngines = new HashSet<String>();
0669 
0670   /**
0671    * Registers a new IR engine. The class named should implement
0672    * {@link gate.creole.ir.IREngine} and be accessible via the GATE class
0673    * loader.
0674    *
0675    @param className
0676    *          the fully qualified name of the class to be registered
0677    @throws GateException
0678    *           if the class does not implement the
0679    *           {@link gate.creole.ir.IREngine} interface.
0680    @throws ClassNotFoundException
0681    *           if the named class cannot be found.
0682    */
0683   public static void registerIREngine(String classNamethrows GateException,
0684     ClassNotFoundException {
0685     Class aClass = Class.forName(className, true, Gate.getClassLoader());
0686     if(gate.creole.ir.IREngine.class.isAssignableFrom(aClass)) {
0687       registeredIREngines.add(className);
0688     }
0689     else {
0690       throw new GateException(className + " does not implement the "
0691         + gate.creole.ir.IREngine.class.getName() " interface!");
0692     }
0693   }
0694 
0695   /**
0696    * Unregisters a previously registered IR engine.
0697    *
0698    @param className
0699    *          the name of the class to be removed from the list of registered IR
0700    *          engines.
0701    @return true if the class was found and removed.
0702    */
0703   public static boolean unregisterIREngine(String className) {
0704     return registeredIREngines.remove(className);
0705   }
0706 
0707   /**
0708    * Gets the set of registered IR engines.
0709    *
0710    @return an unmodifiable {@link java.util.Set} value.
0711    */
0712   public static Set<String> getRegisteredIREngines() {
0713     return Collections.unmodifiableSet(registeredIREngines);
0714   }
0715 
0716   /**
0717    * Gets the GATE home location.
0718    *
0719    @return a File value.
0720    */
0721   public static File getGateHome() {
0722     return gateHome;
0723   }
0724 
0725   /** Should we assume we're connected to the net? */
0726   public static boolean isNetConnected() {
0727     return netConnected;
0728   }
0729 
0730   /**
0731    * Tell GATE whether to assume we're connected to the net. Has to be called
0732    <B>before</B> {@link #init()}.
0733    */
0734   public static void setNetConnected(boolean b) {
0735     netConnected = b;
0736   }
0737 
0738   /**
0739    * Flag controlling whether we should try to access a web server on localhost,
0740    * e.g. when setting up a base URL. Has to be called <B>before</B>
0741    * {@link #init()}.
0742    */
0743   private static boolean localWebServer = false;
0744 
0745   /** Should we assume there's a local web server? */
0746   public static boolean isLocalWebServer() {
0747     return localWebServer;
0748   }
0749 
0750   /** Tell GATE whether to assume there's a local web server. */
0751   public static void setLocalWebServer(boolean b) {
0752     localWebServer = b;
0753   }
0754 
0755   /**
0756    * Try to contact a network server. When sucessfull sets urlBase to an HTTP
0757    * URL for the server.
0758    *
0759    @param hostName
0760    *          the name of the host to try and connect to
0761    @param serverPort
0762    *          the port to try and connect to
0763    @param path
0764    *          a path to append to the URL when we make a successfull connection.
0765    *          E.g. for host xyz, port 80, path /thing, the resultant URL would
0766    *          be <TT>http://xyz:80/thing</TT>.
0767    */
0768   public static boolean tryNetServer(String hostName, int serverPort,
0769     String paththrows MalformedURLException {
0770 
0771     log.debug("tryNetServer(hostName=" + hostName + ", serverPort="
0772         + serverPort + ", path=" + path + ")");
0773 
0774     // is the host listening at the port?
0775     try {
0776       URL url = new URL("http://" + hostName + ":" + serverPort + "/");
0777       URLConnection uConn = url.openConnection();
0778       HttpURLConnection huConn = null;
0779       if(uConn instanceof HttpURLConnectionhuConn = (HttpURLConnection)uConn;
0780       if(huConn.getResponseCode() == -1return false;
0781     }
0782     catch(IOException e) {
0783       return false;
0784     }
0785 
0786     // if(socket != null) {
0787     urlBase = new URL("http", hostName, serverPort, path);
0788     return true;
0789     // }
0790 
0791     // return false;
0792   // tryNetServer()
0793 
0794   /** Try to find GATE files in the local file system */
0795   protected static boolean tryFileSystem() throws MalformedURLException {
0796     String urlBaseName = locateGateFiles();
0797     log.debug("tryFileSystem: " + urlBaseName);
0798 
0799     urlBase = new URL(urlBaseName + "gate/resources/gate.ac.uk/");
0800     return urlBase == null;
0801   // tryFileSystem()
0802 
0803   /**
0804    * Find the location of the GATE binaries (and resources) in the local file
0805    * system.
0806    */
0807   public static String locateGateFiles() {
0808     String aGateResourceName = "gate/resources/creole/creole.xml";
0809     URL resourcesUrl = Gate.getClassLoader().getResource(aGateResourceName);
0810 
0811     StringBuffer basePath = new StringBuffer(resourcesUrl.toExternalForm());
0812     String urlBaseName =
0813       basePath.substring(0, basePath.length() - aGateResourceName.length());
0814 
0815     return urlBaseName;
0816   // locateGateFiles
0817 
0818   /**
0819    * Checks whether a particular class is a Gate defined type
0820    */
0821   public static boolean isGateType(String classname) {
0822     boolean res = getCreoleRegister().containsKey(classname);
0823     if(!res) {
0824       try {
0825         Class aClass = Class.forName(classname, true, Gate.getClassLoader());
0826         res =
0827           Resource.class.isAssignableFrom(aClass);
0828       }
0829       catch(ClassNotFoundException cnfe) {
0830         return false;
0831       }
0832     }
0833     return res;
0834   }
0835 
0836   /** Returns the value for the HIDDEN attribute of a feature map */
0837   static public boolean getHiddenAttribute(FeatureMap fm) {
0838     if(fm == nullreturn false;
0839     Object value = fm.get(HIDDEN_FEATURE_KEY);
0840     return value != null && value instanceof String
0841       && ((String)value).equals("true");
0842   }
0843 
0844   /** Sets the value for the HIDDEN attribute of a feature map */
0845   static public void setHiddenAttribute(FeatureMap fm, boolean hidden) {
0846     if(hidden) {
0847       fm.put(HIDDEN_FEATURE_KEY, "true");
0848     }
0849     else {
0850       fm.remove(HIDDEN_FEATURE_KEY);
0851     }
0852   }
0853 
0854   /**
0855    * Registers a {@link gate.event.CreoleListener} with the Gate system
0856    */
0857   public static synchronized void addCreoleListener(CreoleListener l) {
0858     creoleRegister.addCreoleListener(l);
0859   // addCreoleListener
0860 
0861   /** Set the URL base for GATE files, e.g. <TT>http://gate.ac.uk/</TT>. */
0862   public static void setUrlBase(URL urlBase) {
0863     Gate.urlBase = urlBase;
0864   }
0865 
0866   /** The URL base for GATE files, e.g. <TT>http://gate.ac.uk/</TT>. */
0867   private static URL urlBase = null;
0868 
0869   /**
0870    * Class loader used e.g. for loading CREOLE modules, of compiling JAPE rule
0871    * RHSs.
0872    */
0873   private static GateClassLoader classLoader = null;
0874 
0875   /** Get the GATE class loader. */
0876   public static GateClassLoader getClassLoader() {
0877     return classLoader;
0878   }
0879 
0880   /** The CREOLE register. */
0881   private static CreoleRegister creoleRegister = null;
0882 
0883   /** Get the CREOLE register. */
0884   public static CreoleRegister getCreoleRegister() {
0885     return creoleRegister;
0886   }
0887 
0888   /** The DataStore register */
0889   private static DataStoreRegister dataStoreRegister = null;
0890 
0891   /**
0892    * The current executable under execution.
0893    */
0894   private static gate.Executable currentExecutable;
0895 
0896   /** Get the DataStore register. */
0897   public static DataStoreRegister getDataStoreRegister() {
0898     return dataStoreRegister;
0899   // getDataStoreRegister
0900 
0901   /**
0902    * Sets the {@link Executable} currently under execution. At a given time
0903    * there can be only one executable set. After the executable has finished its
0904    * execution this value should be set back to null. An attempt to set the
0905    * executable while this value is not null will result in the method call
0906    * waiting until the old executable is set to null.
0907    */
0908   public synchronized static void setExecutable(gate.Executable executable) {
0909     if(executable == null)
0910       currentExecutable = executable;
0911     else {
0912       while(getExecutable() != null) {
0913         try {
0914           Thread.sleep(200);
0915         }
0916         catch(InterruptedException ie) {
0917           throw new LuckyException(ie.toString());
0918         }
0919       }
0920       currentExecutable = executable;
0921     }
0922   // setExecutable
0923 
0924   /**
0925    * Returns the curently set executable.
0926    *
0927    @see #setExecutable(gate.Executable)
0928    */
0929   public synchronized static gate.Executable getExecutable() {
0930     return currentExecutable;
0931   // getExecutable
0932 
0933   /**
0934    * Returns a new unique string
0935    */
0936   public synchronized static String genSym() {
0937     StringBuffer buff =
0938       new StringBuffer(Integer.toHexString(lastSym++).toUpperCase());
0939     for(int i = buff.length(); i <= 4; i++)
0940       buff.insert(0'0');
0941     return buff.toString();
0942   // genSym
0943 
0944   /** GATE development environment configuration data (stored in gate.xml). */
0945   private static OptionsMap userConfig = new OptionsMap();
0946 
0947   /**
0948    * This map stores the init-time config data in case we need it later. GATE
0949    * development environment configuration data (stored in gate.xml).
0950    */
0951   private static OptionsMap originalUserConfig = new OptionsMap();
0952 
0953   /** Name of the XML element for GATE development environment config data. */
0954   private static String userConfigElement = "GATECONFIG";
0955 
0956   /**
0957    * Gate the name of the XML element for GATE development environment config
0958    * data.
0959    */
0960   public static String getUserConfigElement() {
0961     return userConfigElement;
0962   }
0963 
0964   /**
0965    * Get the site config file (generally set during command-line processing or
0966    * as a <TT>gate.config</TT> property). If the config is null, this method
0967    * checks the <TT>gate.config</TT> property and uses it if non-null.
0968    */
0969   public static File getSiteConfigFile() {
0970     if(siteConfigFile == null) {
0971       String gateConfigProperty = System.getProperty(GATE_CONFIG_PROPERTY);
0972       if(gateConfigProperty != null)
0973         siteConfigFile = new File(gateConfigProperty);
0974     }
0975     return siteConfigFile;
0976   // getSiteConfigFile
0977 
0978   /** Set the site config file (e.g. during command-line processing). */
0979   public static void setSiteConfigFile(File siteConfigFile) {
0980     Gate.siteConfigFile = siteConfigFile;
0981   // setSiteConfigFile
0982 
0983   /** Shorthand for local newline */
0984   private static String nl = Strings.getNl();
0985 
0986   /** An empty config data file. */
0987   private static String emptyConfigFile =
0988     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + nl + "<!-- " + GATE_DOT_XML
0989       ": GATE configuration data -->" + nl + "<GATE>" + nl + "" + nl
0990       "<!-- NOTE: the next element may be overwritten by the GUI!!! -->" + nl
0991       "<" + userConfigElement + "/>" + nl + "" + nl + "</GATE>" + nl;
0992 
0993   /**
0994    * Get an empty config file. <B>NOTE:</B> this method is intended only for
0995    * use by the test suite.
0996    */
0997   public static String getEmptyConfigFile() {
0998     return emptyConfigFile;
0999   }
1000 
1001   /**
1002    * Get the GATE development environment configuration data (initialised from
1003    <TT>gate.xml</TT>).
1004    */
1005   public static OptionsMap getUserConfig() {
1006     return userConfig;
1007   }
1008 
1009   /**
1010    * Get the original, initialisation-time, GATE development environment
1011    * configuration data (initialised from <TT>gate.xml</TT>).
1012    */
1013   public static OptionsMap getOriginalUserConfig() {
1014     return originalUserConfig;
1015   // getOriginalUserConfig
1016 
1017   /**
1018    * Update the GATE development environment configuration data in the user's
1019    <TT>gate.xml</TT> file (create one if it doesn't exist).
1020    */
1021   public static void writeUserConfig() throws GateException {
1022 
1023     //if we are running in a sandbox then don't try and write anything
1024     if (sandboxedreturn;
1025 
1026     String pluginsHomeStr;
1027     try {
1028       pluginsHomeStr = pluginsHome.getCanonicalPath();
1029     }
1030     catch(IOException ioe) {
1031       throw new GateRuntimeException(
1032         "Problem while locating the plug-ins home!", ioe);
1033     }
1034     // update the values for knownPluginPath
1035     String knownPluginPath = "";
1036     Iterator<URL> pluginIter = getKnownPlugins().iterator();
1037     while(pluginIter.hasNext()) {
1038       URL aPluginURL = pluginIter.next();
1039       // do not save installed plug-ins - they get loaded automatically
1040       if(aPluginURL.getProtocol().equals("file")) {
1041         File pluginDirectory = Files.fileFromURL(aPluginURL);
1042         try {
1043           if(pluginDirectory.getCanonicalPath().startsWith(pluginsHomeStr))
1044             continue;
1045         }
1046         catch(IOException ioe) {
1047           throw new GateRuntimeException("Problem while locating the plug-in"
1048             + aPluginURL.toString(), ioe);
1049         }
1050       }
1051       if(knownPluginPath.length() 0knownPluginPath += ";";
1052       knownPluginPath += aPluginURL.toExternalForm();
1053     }
1054     getUserConfig().put(KNOWN_PLUGIN_PATH_KEY, knownPluginPath);
1055 
1056     // update the autoload plugin list
1057     String loadPluginPath = "";
1058     pluginIter = getAutoloadPlugins().iterator();
1059     while(pluginIter.hasNext()) {
1060       URL aPluginURL = pluginIter.next();
1061       if(loadPluginPath.length() 0loadPluginPath += ";";
1062       loadPluginPath += aPluginURL.toExternalForm();
1063     }
1064     getUserConfig().put(AUTOLOAD_PLUGIN_PATH_KEY, loadPluginPath);
1065 
1066     // the user's config file
1067     // String configFileName = getUserConfigFileName();
1068     // File configFile = new File(configFileName);
1069     File configFile = getUserConfigFile();
1070 
1071     // create if not there, then update
1072     try {
1073       // if the file doesn't exist, create one with an empty GATECONFIG
1074       if(!configFile.exists()) {
1075         FileOutputStream fos = new FileOutputStream(configFile);
1076         OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
1077         writer.write(emptyConfigFile);
1078         writer.close();
1079       }
1080 
1081       // update the config element of the file
1082       Files.updateXmlElement(configFile, userConfigElement, userConfig);
1083 
1084     }
1085     catch(IOException e) {
1086       throw new GateException("problem writing user " + GATE_DOT_XML + ": "
1087         + nl + e.toString());
1088     }
1089   // writeUserConfig
1090 
1091   /**
1092    * Get the name of the user's <TT>gate.xml</TT> config file (this doesn't
1093    * guarantee that file exists!).
1094    *
1095    @deprecated Use {@link #getUserConfigFile} instead.
1096    */
1097   public static String getUserConfigFileName() {
1098     return getDefaultUserConfigFileName();
1099   // getUserConfigFileName
1100 
1101   /**
1102    * Get the default path to the user's config file, which is used unless an
1103    * alternative name has been specified via system properties or
1104    * {@link #setUserConfigFile}.
1105    *
1106    @return the default user config file path.
1107    */
1108   public static String getDefaultUserConfigFileName() {
1109     String filePrefix = "";
1110     if(runningOnUnix()) filePrefix = ".";
1111 
1112     String userConfigName =
1113       System.getProperty("user.home"+ Strings.getFileSep() + filePrefix
1114         + GATE_DOT_XML;
1115     return userConfigName;
1116   // getDefaultUserConfigFileName
1117 
1118   /**
1119    * Get the default path to the user's session file, which is used unless an
1120    * alternative name has been specified via system properties or
1121    * {@link #setUserSessionFile(File)}
1122    *
1123    @return the default user session file path.
1124    */
1125   public static String getDefaultUserSessionFileName() {
1126     String filePrefix = "";
1127     if(runningOnUnix()) filePrefix = ".";
1128 
1129     String userSessionName =
1130       System.getProperty("user.home"+ Strings.getFileSep() + filePrefix
1131         + GATE_DOT_SER;
1132 
1133     return userSessionName;
1134   // getUserSessionFileName
1135 
1136   /**
1137    * This method tries to guess if we are on a UNIX system. It does this by
1138    * checking the value of <TT>System.getProperty("file.separator")</TT>; if
1139    * this is "/" it concludes we are on UNIX. <B>This is obviously not a very
1140    * good idea in the general case, so nothing much should be made to depend on
1141    * this method (e.g. just naming of config file <TT>.gate.xml</TT> as
1142    * opposed to <TT>gate.xml</TT>)</B>.
1143    */
1144   public static boolean runningOnUnix() {
1145     return Strings.getFileSep().equals("/");
1146   // runningOnUnix
1147 
1148   /**
1149    * This method tries to guess if we are on a Mac OS X system.  It does this
1150    * by checking the value of <TT>System.getProperty("os.name")</TT>.  Note
1151    * that if this method returns true, {@link #runningOnUnix()} will also
1152    * return true (i.e. Mac is considered a Unix platform) but the reverse is
1153    * not necessarily the case.
1154    */
1155   public static boolean runningOnMac() {
1156     return System.getProperty("os.name").toLowerCase().startsWith("mac os x");
1157   }
1158 
1159   /**
1160    * Returns the list of CREOLE directories the system knows about (either
1161    * pre-installed plugins in the plugins directory or CREOLE directories that
1162    * have previously been loaded manually).
1163    *
1164    @return a {@link List} of {@link URL}s.
1165    */
1166   public static List<URL> getKnownPlugins() {
1167     return knownPlugins;
1168   }
1169 
1170   /**
1171    * Adds the plugin to the list of known plugins.
1172    *
1173    @param pluginURL
1174    *          the URL for the new plugin.
1175    */
1176   public static void addKnownPlugin(URL pluginURL) {
1177     pluginURL = normaliseCreoleUrl(pluginURL);
1178     if(knownPlugins.contains(pluginURL)) return;
1179     knownPlugins.add(pluginURL);
1180   }
1181 
1182   /**
1183    * Makes sure the provided URL ends with "/" (CREOLE URLs always point to
1184    * directories so thry should always end with a slash.
1185    *
1186    @param url
1187    *          the URL to be normalised
1188    @return the (maybe) corrected URL.
1189    */
1190   private static URL normaliseCreoleUrl(URL url) {
1191     // CREOLE URLs are directory URLs so they should end with "/"
1192     String urlName = url.toExternalForm();
1193     String separator = "/";
1194     if(urlName.endsWith(separator)) {
1195       return url;
1196     }
1197     else {
1198       urlName += separator;
1199       try {
1200         return new URL(urlName);
1201       }
1202       catch(MalformedURLException mue) {
1203         throw new GateRuntimeException(mue);
1204       }
1205     }
1206   }
1207 
1208   /**
1209    * Returns the list of CREOLE directories the system loads automatically at
1210    * start-up.
1211    *
1212    @return a {@link List} of {@link URL}s.
1213    */
1214   public static List<URL> getAutoloadPlugins() {
1215     return autoloadPlugins;
1216   }
1217 
1218   /**
1219    * Adds a new directory to the list of plugins that are loaded automatically
1220    * at start-up.
1221    *
1222    @param pluginUrl
1223    *          the URL for the new plugin.
1224    */
1225   public static void addAutoloadPlugin(URL pluginUrl) {
1226     pluginUrl = normaliseCreoleUrl(pluginUrl);
1227     if(autoloadPlugins.contains(pluginUrl)) return;
1228     // make sure it's known
1229     addKnownPlugin(pluginUrl);
1230     // add it to autoload list
1231     autoloadPlugins.add(pluginUrl);
1232   }
1233 
1234   /**
1235    * Gets the information about a known directory.
1236    *
1237    @param directory
1238    *          the URL for the directory in question.
1239    @return a {@link DirectoryInfo} value.
1240    */
1241   public static DirectoryInfo getDirectoryInfo(URL directory) {
1242     directory = normaliseCreoleUrl(directory);
1243     if(!knownPlugins.contains(directory)) return null;
1244     DirectoryInfo dInfo = pluginData.get(directory);
1245     if(dInfo == null) {
1246       dInfo = new DirectoryInfo(directory);
1247       pluginData.put(directory, dInfo);
1248     }
1249     return dInfo;
1250   }
1251 
1252   /**
1253    * Tells the system to &quot;forget&quot; about one previously known
1254    * directory. If the specified directory was loaded, it will be unloaded as
1255    * well - i.e. all the metadata relating to resources defined by this
1256    * directory will be removed from memory.
1257    *
1258    @param pluginURL
1259    */
1260   public static void removeKnownPlugin(URL pluginURL) {
1261     pluginURL = normaliseCreoleUrl(pluginURL);
1262     knownPlugins.remove(pluginURL);
1263     autoloadPlugins.remove(pluginURL);
1264     creoleRegister.removeDirectory(pluginURL);
1265     pluginData.remove(pluginURL);
1266   }
1267 
1268   /**
1269    * Tells the system to remove a plugin URL from the list of plugins that are
1270    * loaded automatically at system start-up. This will be reflected in the
1271    * user's configuration data file.
1272    *
1273    @param pluginURL
1274    *          the URL to be removed.
1275    */
1276   public static void removeAutoloadPlugin(URL pluginURL) {
1277     pluginURL = normaliseCreoleUrl(pluginURL);
1278     autoloadPlugins.remove(pluginURL);
1279   }
1280 
1281   /**
1282    * Stores information about the contents of a CREOLE directory.
1283    */
1284   public static class DirectoryInfo {
1285     public DirectoryInfo(URL url) {
1286       this.url = normaliseCreoleUrl(url);
1287       valid = true;
1288       resourceInfoList = new ArrayList<ResourceInfo>();
1289       // this may invalidate it if something goes wrong
1290       parseCreole();
1291     }
1292 
1293     /**
1294      * Performs a shallow parse of the creole.xml file to get the information
1295      * about the resources contained.
1296      */
1297     protected void parseCreole() {
1298       SAXBuilder builder = new SAXBuilder(false);
1299       try {
1300         URL creoleFileURL = new URL(url, "creole.xml");
1301         org.jdom.Document creoleDoc = builder.build(creoleFileURL);
1302 
1303         final Map<String, ResourceInfo> resInfos = new LinkedHashMap<String, ResourceInfo>();
1304         List<Element> jobsList = new ArrayList<Element>();
1305         List<String> jarsToScan = new ArrayList<String>();
1306         List<String> allJars = new ArrayList<String>();
1307         jobsList.add(creoleDoc.getRootElement());
1308         while(!jobsList.isEmpty()) {
1309           Element currentElem = jobsList.remove(0);
1310           if(currentElem.getName().equalsIgnoreCase("JAR")) {
1311             List<Attribute> attrs = currentElem.getAttributes();
1312             Iterator<Attribute> attrsIt = attrs.iterator();
1313             while(attrsIt.hasNext()) {
1314               Attribute attr = attrsIt.next();
1315               if(attr.getName().equalsIgnoreCase("SCAN"&& attr.getBooleanValue()) {
1316                 jarsToScan.add(currentElem.getTextTrim());
1317                 break;
1318               }
1319             }
1320             allJars.add(currentElem.getTextTrim());
1321           }
1322           else if(currentElem.getName().equalsIgnoreCase("RESOURCE")) {
1323             // we don't go deeper than resources so no recursion here
1324             String resName = currentElem.getChildTextTrim("NAME");
1325             String resClass = currentElem.getChildTextTrim("CLASS");
1326             String resComment = currentElem.getChildTextTrim("COMMENT");
1327             if(!resInfos.containsKey(resClass)) {
1328               // create the handler
1329               ResourceInfo rHandler =
1330                 new ResourceInfo(resName, resClass, resComment);
1331               resInfos.put(resClass, rHandler);
1332             }
1333           }
1334           else {
1335             // this is some higher level element -> simulate recursion
1336             // we want Depth-first-search so we need to add at the beginning
1337             List<Element> newJobsList = new ArrayList<Element>(currentElem.getChildren());
1338             newJobsList.addAll(jobsList);
1339             jobsList = newJobsList;
1340           }
1341         }
1342 
1343         // now process the jar files with SCAN="true", looking for any extra
1344         // CreoleResource annotated classes.
1345         for(String jarFile : jarsToScan) {
1346           URL jarUrl = new URL(url, jarFile);
1347           scanJar(jarUrl, resInfos);
1348         }
1349 
1350         // see whether any of the ResourceInfo objects are still incomplete
1351         // (don't have a name)
1352         List<ResourceInfo> incompleteResInfos = new ArrayList<ResourceInfo>();
1353         for(ResourceInfo ri : resInfos.values()) {
1354           if(ri.getResourceName() == null) {
1355             incompleteResInfos.add(ri);
1356           }
1357         }
1358 
1359         if(!incompleteResInfos.isEmpty()) {
1360           fillInResInfos(incompleteResInfos, allJars);
1361         }
1362 
1363         // if any of the resource infos still don't have a name, take it from
1364         // the class name.
1365         for(ResourceInfo ri : incompleteResInfos) {
1366           if(ri.getResourceName() == null) {
1367             ri.resourceName = ri.resourceClassName.substring(
1368                     ri.resourceClassName.lastIndexOf('.'1);
1369           }
1370         }
1371 
1372         // finally, we have the complete list of ResourceInfos
1373         resourceInfoList.addAll(resInfos.values());
1374       }
1375       catch(IOException ioe) {
1376         valid = false;
1377         log.error("Problem while parsing plugin " + url.toExternalForm() "!\n"
1378           + ioe.toString() "\nPlugin not available!");
1379       }
1380       catch(JDOMException jde) {
1381         valid = false;
1382         log.error("Problem while parsing plugin " + url.toExternalForm() "!\n"
1383           + jde.toString() "\nPlugin not available!");
1384       }
1385     }
1386 
1387     protected void scanJar(URL jarUrl, Map<String, ResourceInfo> resInfosthrows IOException {
1388       JarInputStream jarInput = new JarInputStream(jarUrl.openStream()false);
1389       JarEntry entry = null;
1390       while((entry = jarInput.getNextJarEntry()) != null) {
1391         String entryName = entry.getName();
1392         if(entryName != null && entryName.endsWith(".class")) {
1393           final String className = entryName.substring(0,
1394                    entryName.length() 6).replace('/''.');
1395           if(!resInfos.containsKey(className)) {
1396             ClassReader classReader = new ClassReader(jarInput);
1397             ResourceInfo resInfo = new ResourceInfo(null, className, null);
1398             ResourceInfoVisitor visitor = new ResourceInfoVisitor(resInfo);
1399 
1400             classReader.accept(visitor, ClassReader.SKIP_CODE |
1401                 ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
1402             if(visitor.isCreoleResource()) {
1403               resInfos.put(className, resInfo);
1404             }
1405           }
1406         }
1407       }
1408 
1409       jarInput.close();
1410     }
1411 
1412     protected void fillInResInfos(List<ResourceInfo> incompleteResInfos,
1413               List<String> allJarsthrows IOException {
1414       // now create a temporary class loader with all the JARs (scanned or
1415       // not), so we can look up all the referenced classes in the normal
1416       // way and read their CreoleResource annotations (if any).
1417       URL[] jarUrls = new URL[allJars.size()];
1418       for(int i = 0; i < jarUrls.length; i++) {
1419         jarUrls[inew URL(url, allJars.get(i));
1420       }
1421       ClassLoader tempClassLoader = new URLClassLoader(jarUrls,
1422                 Gate.class.getClassLoader());
1423       for(ResourceInfo ri : incompleteResInfos) {
1424         String classFile = ri.getResourceClassName().replace('.''/')
1425                 ".class";
1426         InputStream classStream = tempClassLoader.getResourceAsStream(classFile);
1427         if(classStream != null) {
1428           ClassReader classReader = new ClassReader(classStream);
1429           ClassVisitor visitor = new ResourceInfoVisitor(ri);
1430           classReader.accept(visitor, ClassReader.SKIP_CODE |
1431               ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
1432           classStream.close();
1433         }
1434       }
1435     }
1436 
1437     /**
1438      @return Returns the resourceInfoList.
1439      */
1440     public List<ResourceInfo> getResourceInfoList() {
1441       return resourceInfoList;
1442     }
1443 
1444     /**
1445      @return Returns the url.
1446      */
1447     public URL getUrl() {
1448       return url;
1449     }
1450 
1451     /**
1452      @return Returns the valid.
1453      */
1454     public boolean isValid() {
1455       return valid;
1456     }
1457 
1458     /**
1459      * The URL for the CREOLE directory.
1460      */
1461     protected URL url;
1462 
1463     /**
1464      * Is the directory valid (i.e. is the location reachable and the creole.xml
1465      * file parsable).
1466      */
1467     protected boolean valid;
1468 
1469     /**
1470      * The list of {@link Gate.ResourceInfo} objects.
1471      */
1472     protected List<ResourceInfo> resourceInfoList;
1473   }
1474 
1475   /**
1476    * Stores information about a resource defined by a CREOLE directory. The
1477    * resource might not have been loaded in the system so not all information
1478    * normally provided by the {@link ResourceData} class is available. This is
1479    * what makes this class different from {@link ResourceData}.
1480    */
1481   public static class ResourceInfo {
1482     public ResourceInfo(String name, String className, String comment) {
1483       this.resourceClassName = className;
1484       this.resourceName = name;
1485       this.resourceComment = comment;
1486     }
1487 
1488     /**
1489      @return Returns the resourceClassName.
1490      */
1491     public String getResourceClassName() {
1492       return resourceClassName;
1493     }
1494 
1495     /**
1496      @return Returns the resourceComment.
1497      */
1498     public String getResourceComment() {
1499       return resourceComment;
1500     }
1501 
1502     /**
1503      @return Returns the resourceName.
1504      */
1505     public String getResourceName() {
1506       return resourceName;
1507     }
1508 
1509     /**
1510      * The class for the resource.
1511      */
1512     protected String resourceClassName;
1513 
1514     /**
1515      * The resource name.
1516      */
1517     protected String resourceName;
1518 
1519     /**
1520      * The comment for the resource.
1521      */
1522     protected String resourceComment;
1523   }
1524 
1525   /**
1526    * ClassVisitor that uses information from a CreoleResource annotation on the
1527    * visited class (if such exists) to fill in the name and comment in the
1528    * corresponding ResourceInfo.
1529    */
1530   private static class ResourceInfoVisitor extends EmptyVisitor {
1531     private ResourceInfo resInfo;
1532 
1533     private boolean foundCreoleResource = false;
1534 
1535     private boolean isAbstract = false;
1536 
1537     public ResourceInfoVisitor(ResourceInfo resInfo) {
1538       this.resInfo = resInfo;
1539     }
1540 
1541     public boolean isCreoleResource() {
1542       return foundCreoleResource && !isAbstract;
1543     }
1544 
1545     /**
1546      * Type descriptor for the CreoleResource annotation type.
1547      */
1548     private static final String CREOLE_RESOURCE_DESC =
1549             Type.getDescriptor(CreoleResource.class);
1550 
1551     /**
1552      * Visit the class header, checking whether this is an abstract class or
1553      * interface and setting the isAbstract flag appropriately.
1554      */
1555     public void visit(int version, int access, String name, String signature,
1556             String superName, String[] interfaces) {
1557       isAbstract = ((access &
1558             (Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)) != 0);
1559     }
1560 
1561     /**
1562      * Visit an annotation on the class.
1563      */
1564     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
1565       // we've found a CreoleResource annotation on this class
1566       if(desc.equals(CREOLE_RESOURCE_DESC)) {
1567         foundCreoleResource = true;
1568         return new EmptyVisitor() {
1569           public void visit(String name, Object value) {
1570             if(name.equals("name"&& resInfo.resourceName == null) {
1571               resInfo.resourceName = (String)value;
1572             }
1573             else if(name.equals("comment"&& resInfo.resourceComment == null) {
1574               resInfo.resourceComment = (String)value;
1575             }
1576           }
1577 
1578           public AnnotationVisitor visitAnnotation(String name,
1579                     String desc) {
1580             // don't want to recurse into AutoInstance annotations
1581             return new EmptyVisitor();
1582           }
1583         };
1584       }
1585       else {
1586         return super.visitAnnotation(desc, visible);
1587       }
1588     }
1589   }
1590 
1591   /**
1592    * The top level directory of the GATE installation.
1593    */
1594   protected static File gateHome;
1595 
1596   /** Site config file */
1597   private static File siteConfigFile;
1598 
1599   /** User config file */
1600   private static File userConfigFile;
1601 
1602   /**
1603    * The top level directory for GATE installed plugins.
1604    */
1605   protected static File pluginsHome;
1606 
1607   /**
1608    * The "builtin" creole directory URL, where the creole.xml that defines
1609    * things like DocumentImpl can be found.
1610    */
1611   protected static URL builtinCreoleDir;
1612 
1613   /**
1614    * The user session file to use.
1615    */
1616   protected static File userSessionFile;
1617 
1618   /**
1619    * Set the location of the GATE home directory.
1620    *
1621    @throws IllegalStateException
1622    *           if the value has already been set.
1623    */
1624   public static void setGateHome(File gateHome) {
1625     if(Gate.gateHome != null) { throw new IllegalStateException(
1626       "gateHome has already been set")}
1627     Gate.gateHome = gateHome;
1628   }
1629 
1630   /**
1631    * Set the location of the plugins directory.
1632    *
1633    @throws IllegalStateException
1634    *           if the value has already been set.
1635    */
1636   public static void setPluginsHome(File pluginsHome) {
1637     if(Gate.pluginsHome != null) { throw new IllegalStateException(
1638       "pluginsHome has already been set")}
1639     Gate.pluginsHome = pluginsHome;
1640   }
1641 
1642   /**
1643    * Get the location of the plugins directory.
1644    *
1645    @return the plugins drectory, or null if this has not yet been set (i.e.
1646    *         <code>Gate.init()</code> has not yet been called).
1647    */
1648   public static File getPluginsHome() {
1649     return pluginsHome;
1650   }
1651 
1652   /**
1653    * Set the location of the user's config file.
1654    *
1655    @throws IllegalStateException
1656    *           if the value has already been set.
1657    */
1658   public static void setUserConfigFile(File userConfigFile) {
1659     if(Gate.userConfigFile != null) { throw new IllegalStateException(
1660       "userConfigFile has already been set")}
1661     Gate.userConfigFile = userConfigFile;
1662   }
1663 
1664   /**
1665    * Get the location of the user's config file.
1666    *
1667    @return the user config file, or null if this has not yet been set (i.e.
1668    *         <code>Gate.init()</code> has not yet been called).
1669    */
1670   public static File getUserConfigFile() {
1671     return userConfigFile;
1672   }
1673 
1674   /**
1675    * Set the URL to the "builtin" creole directory. The URL must point to a
1676    * directory, and must end with a forward slash.
1677    *
1678    @throws IllegalStateException
1679    *           if the value has already been set.
1680    */
1681   public static void setBuiltinCreoleDir(URL builtinCreoleDir) {
1682     if(Gate.builtinCreoleDir != null) { throw new IllegalStateException(
1683       "builtinCreoleDir has already been set")}
1684     Gate.builtinCreoleDir = builtinCreoleDir;
1685   }
1686 
1687   /**
1688    * Get the URL to the "builtin" creole directory, i.e. the directory that
1689    * contains the creole.xml file that defines things like DocumentImpl, the
1690    * Controllers, etc.
1691    */
1692   public static URL getBuiltinCreoleDir() {
1693     return builtinCreoleDir;
1694   }
1695 
1696   /**
1697    * Set the user session file. This can only done prior to calling Gate.init()
1698    * which will set the file to either the OS-specific default or whatever has
1699    * been set by the property gate.user.session
1700    *
1701    @throws IllegalStateException
1702    *           if the value has already been set.
1703    */
1704   public static void setUserSessionFile(File newUserSessionFile) {
1705     if(Gate.userSessionFile != null) { throw new IllegalStateException(
1706       "userSessionFile has already been set")}
1707     Gate.userSessionFile = newUserSessionFile;
1708   }
1709 
1710   /**
1711    * Get the user session file.
1712    *
1713    @return the file corresponding to the user session file or null, if not yet
1714    *         set.
1715    */
1716   public static File getUserSessionFile() {
1717     return userSessionFile;
1718   }
1719 
1720   /**
1721    * The list of plugins (aka CREOLE directories) the system knows about. This
1722    * list contains URL objects.
1723    */
1724   protected static List<URL> knownPlugins;
1725 
1726   /**
1727    * The list of plugins (aka CREOLE directories) the system loads automatically
1728    * at start-up. This list contains URL objects.
1729    */
1730   protected static List<URL> autoloadPlugins;
1731 
1732   /**
1733    * Map from URL of directory to {@link DirectoryInfo}.
1734    */
1735   protected static Map<URL, DirectoryInfo> pluginData;
1736 
1737   /** Flag for SLUG GUI start instead of standart GATE GUI. */
1738   private static boolean slugGui = false;
1739 
1740   /** Should we start SLUG GUI. */
1741   public static boolean isSlugGui() {
1742     return slugGui;
1743   }
1744 
1745   /** Tell GATE whether to start SLUG GUI. */
1746   public static void setSlugGui(boolean b) {
1747     slugGui = b;
1748   }
1749 
1750   /**
1751    * Flag for whether to use native serialization or xml serialization when
1752    * saving applications.
1753    */
1754   private static boolean useXMLSerialization = true;
1755 
1756   /**
1757    * Tell GATE whether to use XML serialization for applications.
1758    */
1759   public static void setUseXMLSerialization(boolean useXMLSerialization) {
1760     Gate.useXMLSerialization = useXMLSerialization;
1761   }
1762 
1763   /**
1764    * Should we use XML serialization for applications.
1765    */
1766   public static boolean getUseXMLSerialization() {
1767     return useXMLSerialization;
1768   }
1769 
1770   /**
1771    * Returns the listeners map, a map that holds all the listeners that
1772    * are singletons (e.g. the status listener that updates the status
1773    * bar on the main frame or the progress listener that updates the
1774    * progress bar on the main frame). The keys used are the class names
1775    * of the listener interface and the values are the actual listeners
1776    * (e.g "gate.event.StatusListener" -> this). The returned map is the
1777    * actual data member used to store the listeners so any changes in
1778    * this map will be visible to everyone.
1779    @return the listeners map
1780    */
1781   public static java.util.Map<String, EventListener> getListeners() {
1782     return listeners;
1783   }
1784 
1785   /**
1786    * A Map which holds listeners that are singletons (e.g. the status
1787    * listener that updates the status bar on the main frame or the
1788    * progress listener that updates the progress bar on the main frame).
1789    * The keys used are the class names of the listener interface and the
1790    * values are the actual listeners (e.g "gate.event.StatusListener" ->
1791    * this).
1792    *
1793    * Deprecated. Call getListeners instead.
1794    */
1795   @Deprecated
1796   public static java.util.Map<String, EventListener> listeners =
1797     new HashMap<String, EventListener>();
1798 
1799 // class Gate