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 (!sandboxed) initLocalPaths();
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 == null) creoleRegister = new CreoleRegisterImpl();
0218 if(knownPlugins == null) knownPlugins = new ArrayList<URL>();
0219 if(autoloadPlugins == null) autoloadPlugins = new ArrayList<URL>();
0220 if(pluginData == null) pluginData = 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 (!sandboxed) initConfigData();
0229
0230 if (!sandboxed) initCreoleRepositories();
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_VERSION) < 0) { 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() > 0) pluginPath = 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 != null) return 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 path) throws GateException {
0641 getUrl();
0642 if(urlBase == null) return 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 className) throws 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 path) throws 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 HttpURLConnection) huConn = (HttpURLConnection)uConn;
0780 if(huConn.getResponseCode() == -1) return 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 == null) return 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 (sandboxed) return;
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() > 0) knownPluginPath += ";";
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() > 0) loadPluginPath += ";";
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 "forget" 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> resInfos) throws 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> allJars) throws 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[i] = new 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
|