| Gate.java |
1 /*
2 * Gate.java
3 *
4 * Copyright (c) 1998-2004, The University of Sheffield.
5 *
6 * This file is part of GATE (see http://gate.ac.uk/), and is free
7 * software, licenced under the GNU Library General Public License,
8 * Version 2, June 1991 (in the distribution as file licence.html,
9 * and also available at http://gate.ac.uk/gate/licence.html).
10 *
11 * Hamish Cunningham, 31/07/98
12 *
13 * $Id: Gate.java,v 1.68 2004/08/04 16:58:42 valyt Exp $
14 */
15
16 package gate;
17
18 import java.io.*;
19 import java.net.*;
20 import java.util.*;
21 import org.jdom.Element;
22 import org.jdom.JDOMException;
23 import org.jdom.input.SAXBuilder;
24
25 import gate.config.ConfigDataProcessor;
26 import gate.creole.CreoleRegisterImpl;
27 import gate.creole.ResourceData;
28 import gate.event.CreoleListener;
29 import gate.util.*;
30
31 /** The class is responsible for initialising the GATE libraries, and
32 * providing access to singleton utility objects, such as the GATE class
33 * loader, CREOLE register and so on.
34 */
35 public class Gate implements GateConstants
36 {
37 /** Debug flag */
38 private static final boolean DEBUG = false;
39
40 /**
41 * The default StringBuffer size, it seems that we need longer string
42 * than the StringBuffer class default because of the high number of
43 * buffer expansions
44 * */
45 public static final int STRINGBUFFER_SIZE = 1024;
46
47 /**
48 * The default size to be used for Hashtable, HashMap and HashSet.
49 * The defualt is 11 and it leads to big memory usage. Having a default
50 * load factor of 0.75, table of size 4 can take 3 elements before being
51 * re-hashed - a values that seems to be optimal for most of the cases.
52 * */
53 public static final int HASH_STH_SIZE = 4;
54
55
56 /**
57 * The database schema owner (GATEADMIN is default)
58 * this one should not be hardcoded but set in the
59 * XML initialization files
60 *
61 * */
62 public static final String DB_OWNER = "gateadmin";
63
64
65 /** The list of builtin URLs to search for CREOLE resources. */
66 private static String builtinCreoleDirectoryUrls[] = {
67 // "http://derwent.dcs.shef.ac.uk/gate.ac.uk/creole/"
68
69 // this has been moved to initCreoleRegister and made relative to
70 // the base URL returned by getUrl()
71 // "http://gate.ac.uk/creole/"
72 };
73
74
75 /** The GATE URI used to interpret custom GATE tags*/
76 public static final String URI = "http://www.gate.ac.uk";
77
78 /** Minimum version of JDK we support */
79 protected static final String MIN_JDK_VERSION = "1.4";
80
81 /** Get the minimum supported version of the JDK */
82 public static String getMinJdkVersion() { return MIN_JDK_VERSION; }
83
84 /** Initialisation - must be called by all clients before using
85 * any other parts of the library. Also initialises the CREOLE
86 * register and reads config data (<TT>gate.xml</TT> files).
87 * @see #initCreoleRegister
88 */
89 public static void init() throws GateException {
90
91 // register the URL handler for the "gate://" URLs
92 System.setProperty(
93 "java.protocol.handler.pkgs",
94 System.getProperty("java.protocol.handler.pkgs")
95 + "|" + "gate.util.protocols"
96 );
97
98 //System.setProperty("javax.xml.parsers.SAXParserFactory",
99 // "org.apache.xerces.jaxp.SAXParserFactoryImpl");
100
101 //initialise the symbols generator
102 lastSym = 0;
103
104 // create class loader and creole register if they're null
105 if(classLoader == null)
106 classLoader = new GateClassLoader(Gate.class.getClassLoader());
107 if(creoleRegister == null)
108 creoleRegister = new CreoleRegisterImpl();
109 if(knownPlugins == null) knownPlugins = new ArrayList();
110 if(autoloadPlugins == null) autoloadPlugins = new ArrayList();
111 if(pluginData == null) pluginData = new HashMap();
112 // init the creole register
113 initCreoleRegister();
114 // init the data store register
115 initDataStoreRegister();
116 // read gate.xml files; this must come before creole register
117 // initialisation in order for the CREOLE-DIR elements to have and effect
118 initConfigData();
119
120 initCreoleRepositories();
121 // the creoleRegister acts as a proxy for datastore related events
122 dataStoreRegister.addCreoleListener(creoleRegister);
123
124 // some of the events are actually fired by the {@link gate.Factory}
125 Factory.addCreoleListener(creoleRegister);
126
127 // check we have a useable JDK
128 if(System.getProperty("java.version").compareTo(MIN_JDK_VERSION) < 0) {
129 throw new GateException(
130 "GATE requires JDK " + MIN_JDK_VERSION + " or newer"
131 );
132 }
133
134 //register Lucene as a IR search engine
135 try{
136 registerIREngine("gate.creole.ir.lucene.LuceneIREngine");
137 }catch(ClassNotFoundException cnfe){
138 throw new GateRuntimeException(cnfe);
139 }
140 } // init()
141
142 /**
143 * Loads the CREOLE repositories (aka plugins) that the user has selected for
144 * automatic loading.
145 * Loads the information about known plugins in memory.
146 */
147 protected static void initCreoleRepositories(){
148 //the logic is:
149 //get the list of know plugins from gate.xml
150 //add all the installed plugins
151 //get the list of loadable plugins
152 //or use ANNIE if value not set
153 //load loadable plugins
154
155 //process the known plugins list
156 String knownPluginsPath = (String)getUserConfig().get(KNOWN_PLUGIN_PATH_KEY);
157 if(knownPluginsPath != null && knownPluginsPath.length() > 0){
158 StringTokenizer strTok = new StringTokenizer(knownPluginsPath, ";", false);
159 while(strTok.hasMoreTokens()){
160 String aKnownPluginPath = strTok.nextToken();
161 try{
162 URL aPluginURL = new URL(aKnownPluginPath);
163 addKnownPlugin(aPluginURL);
164 }catch(MalformedURLException mue){
165 Err.prln("Plugin error: " + aKnownPluginPath + " is an invalid URL!");
166 }
167 }
168 }
169 //add all the installed plugins
170 File pluginsHome = new File(System.getProperty(GATE_HOME_SYSPROP_KEY),
171 "plugins");
172 File[] dirs = pluginsHome.listFiles();
173 for(int i = 0; i < dirs.length; i++){
174 File creoleFile = new File(dirs[i], "creole.xml");
175 if(creoleFile.exists()){
176 try{
177 URL pluginURL = dirs[i].toURL();
178 if(!knownPlugins.contains(pluginURL))
179 knownPlugins.add(pluginURL);
180 }catch(MalformedURLException mue){
181 //this shoulod never happen
182 throw new GateRuntimeException(mue);
183 }
184 }
185 }
186 //we now have a full list of known plugins
187 //get the information about the plugins
188 Iterator pluginIter = knownPlugins.iterator();
189 while(pluginIter.hasNext()){
190 URL aPluginURL = (URL)pluginIter.next();
191 DirectoryInfo dInfo = new DirectoryInfo(aPluginURL);
192 pluginData.put(aPluginURL, dInfo);
193 }
194
195 //process the autoload plugins
196 String pluginPath = getUserConfig().getString(LOAD_PLUGIN_PATH_KEY);
197 //can be overridden by system property
198 String prop = System.getProperty(LOAD_PLUGIN_PATH_SYSPROP_KEY);
199 if(prop != null && prop.length() > 0) pluginPath = prop;
200
201 if(pluginPath == null || pluginPath.length() == 0){
202 //value not set -> use the default
203 try{
204 pluginPath = new File(pluginsHome, "ANNIE/").toURL().toString();
205 getUserConfig().put(LOAD_PLUGIN_PATH_KEY, pluginPath);
206 }catch(MalformedURLException mue){
207 throw new GateRuntimeException(mue);
208 }
209 }
210
211 //load all loadable plugins
212 StringTokenizer strTok = new StringTokenizer(pluginPath, ";", false);
213 while(strTok.hasMoreTokens()){
214 String aDir = strTok.nextToken();
215 try{
216 URL aPluginURL = new URL(aDir);
217 if(!autoloadPlugins.contains(aPluginURL)){
218 autoloadPlugins.add(aPluginURL);
219 }
220 getCreoleRegister().registerDirectories(aPluginURL);
221 }catch(MalformedURLException mue){
222 System.err.println("Cannot load " + aDir + " CREOLE repository.");
223 mue.printStackTrace();
224 }catch(GateException ge){
225 System.err.println("Cannot load " + aDir + " CREOLE repository.");
226 ge.printStackTrace();
227 }
228 }
229 }
230
231 /** Initialise the CREOLE register. */
232 public static void initCreoleRegister() throws GateException {
233
234 // register the builtin CREOLE directories
235 for(int i=0; i<builtinCreoleDirectoryUrls.length; i++)
236 try {
237 creoleRegister.addDirectory(
238 new URL(builtinCreoleDirectoryUrls[i])
239 );
240 } catch(MalformedURLException e) {
241 throw new GateException(e);
242 }
243
244 /*
245 We'll have to think about this. Right now it points to the creole inside the
246 jar/classpath so it's the same as registerBuiltins
247 */
248 // // add the GATE base URL creole directory
249 // creoleRegister.addDirectory(Gate.getUrl("creole/"));
250 // creoleRegister.registerDirectories();
251
252 // register the resources that are actually in gate.jar
253 creoleRegister.registerBuiltins();
254 } // initCreoleRegister
255
256 /** Initialise the DataStore register. */
257 public static void initDataStoreRegister() {
258 dataStoreRegister = new DataStoreRegister();
259 } // initDataStoreRegister()
260
261 /**
262 * Reads config data (<TT>gate.xml</TT> files). There are three
263 * sorts of these files:
264 * <UL>
265 * <LI>
266 * The builtin file from GATE's resources - this is read first.
267 * <LI>
268 * A site-wide init file given as a command-line argument or as a
269 * <TT>gate.config</TT> property - this is read second.
270 * <LI>
271 * The user's file from their home directory - this is read last.
272 * </UL>
273 * Settings from files read after some settings have already been
274 * made will simply overwrite the previous settings.
275 */
276 public static void initConfigData() throws GateException {
277 ConfigDataProcessor configProcessor = new ConfigDataProcessor();
278
279 // url of the builtin config data (for error messages)
280 URL configUrl =
281 Gate.getClassLoader().getResource("gate/resources/" + GATE_DOT_XML);
282
283 // open a stream to the builtin config data file and parse it
284 InputStream configStream = null;
285 try {
286 configStream = Files.getGateResourceAsStream(GATE_DOT_XML);
287 } catch(IOException e) {
288 throw new GateException(
289 "Couldn't open builtin config data file: " + configUrl + " " + e
290 );
291 }
292 configProcessor.parseConfigFile(configStream, configUrl);
293
294 // parse any command-line initialisation file
295 File siteConfigFile = Gate.getSiteConfigFile();
296 if(siteConfigFile != null) {
297 try {
298 configUrl = siteConfigFile.toURL();
299 configStream = new FileInputStream(Gate.getSiteConfigFile());
300 } catch(IOException e) {
301 throw new GateException(
302 "Couldn't open site config data file: " + configUrl + " " + e
303 );
304 }
305 configProcessor.parseConfigFile(configStream, configUrl);
306 }
307
308 // parse the user's config file (if it exists)
309 String userConfigName = getUserConfigFileName();
310 File userConfigFile = null;
311 URL userConfigUrl = null;
312 if(DEBUG) { Out.prln("loading user config from " + userConfigName); }
313 configStream = null;
314 boolean userConfigExists = true;
315 try {
316 userConfigFile = new File(userConfigName);
317 configStream = new FileInputStream(userConfigFile);
318 userConfigUrl = userConfigFile.toURL();
319 } catch(IOException e) {
320 userConfigExists = false;
321 }
322 if(userConfigExists)
323 configProcessor.parseConfigFile(configStream, userConfigUrl);
324
325 // remember the init-time config options
326 originalUserConfig.putAll(userConfig);
327
328 if(DEBUG) {
329 Out.prln(
330 "user config loaded; DBCONFIG=" + DataStoreRegister.getConfigData()
331 );
332 }
333
334
335 //get the CREOLE repositories to load if set trough a sys prop
336 String creolepath = System.getProperty("creole.path");
337 if(creolepath != null && creolepath.length() > 0){
338 StringTokenizer strTok = new StringTokenizer(creolepath,
339 Strings.getPathSep(), false);
340 while(strTok.hasMoreTokens()){
341 String aPath = strTok.nextToken();
342 //this is an URL
343 try{
344 URL creoleURL = new File(aPath).toURL();
345 Gate.getCreoleRegister().registerDirectories(creoleURL);
346 }catch(MalformedURLException mue){
347 throw new GateRuntimeException(mue);
348 }
349 }
350 }
351
352 } // initConfigData()
353
354 /**
355 * Attempts to guess the Unicode font for the platform.
356 */
357 public static String guessUnicodeFont(){
358 //guess the Unicode font for the platform
359 String[] fontNames = java.awt.GraphicsEnvironment.
360 getLocalGraphicsEnvironment().
361 getAvailableFontFamilyNames();
362 String unicodeFontName = null;
363 for(int i = 0; i < fontNames.length; i++){
364 if(fontNames[i].equalsIgnoreCase("Arial Unicode MS")){
365 unicodeFontName = fontNames[i];
366 break;
367 }
368 if(fontNames[i].toLowerCase().indexOf("unicode") != -1){
369 unicodeFontName = fontNames[i];
370 }
371 }//for(int i = 0; i < fontNames.length; i++)
372 return unicodeFontName;
373 }
374
375 /** Get a URL that points to either an HTTP server or a file system
376 * that contains GATE files (such as test cases). The following locations
377 * are tried in sequence:
378 * <UL>
379 * <LI>
380 * <TT>http://derwent.dcs.shef.ac.uk/gate.ac.uk/</TT>, a Sheffield-internal
381 * development server (the gate.ac.uk affix is a copy of the file system
382 * present on GATE's main public server - see next item);
383 * <LI>
384 * <TT>http://gate.ac.uk/</TT>, GATE's main public server;
385 * <LI>
386 * <TT>http://localhost/gate.ac.uk/</TT>, a Web server running on the
387 * local machine;
388 * <LI>
389 * the local file system where the binaries for the
390 * current invocation of GATE are stored.
391 * </UL>
392 * In each case we assume that a Web server will be running on port 80,
393 * and that if we can open a socket to that port then the server is
394 * running. (This is a bit of a strong assumption, but this URL is used
395 * largely by the test suite, so we're not betting anything too critical
396 * on it.)
397 * <P>
398 * Note that the value returned will only be calculated when the existing
399 * value recorded by this class is null (which will be the case when
400 * neither setUrlBase nor getUrlBase have been called, or if
401 * setUrlBase(null) has been called).
402 */
403 public static URL getUrl() throws GateException {
404 if(urlBase != null) return urlBase;
405
406 try {
407
408 // if we're assuming a net connection, try network servers
409 if(isNetConnected()) {
410 if(
411 tryNetServer("gate-internal.dcs.shef.ac.uk", 80, "/") ||
412 // tryNetServer("derwent.dcs.shef.ac.uk", 80, "/gate.ac.uk/") ||
413 tryNetServer("gate.ac.uk", 80, "/")
414 ) {
415 if(DEBUG) Out.prln("getUrl() returned " + urlBase);
416 return urlBase;
417 }
418 } // if isNetConnected() ...
419
420 // no network servers; try for a local host web server.
421 // we use InetAddress to get host name instead of using "localhost" coz
422 // badly configured Windoze IP sometimes doesn't resolve the latter
423 if(
424 isLocalWebServer() &&
425 tryNetServer(
426 InetAddress.getLocalHost().getHostName(), 80, "/gate.ac.uk/"
427 )
428 ) {
429 if(DEBUG) Out.prln("getUrlBase() returned " + urlBase);
430 return urlBase;
431 }
432
433 // try the local file system
434 tryFileSystem();
435
436 } catch(MalformedURLException e) {
437 throw new GateException("Bad URL, getUrlBase(): " + urlBase + ": " + e);
438 } catch(UnknownHostException e) {
439 throw new GateException("No host, getUrlBase(): " + urlBase + ": " + e);
440 }
441
442 // return value will be based on the file system, or null
443 if(DEBUG) Out.prln("getUrlBase() returned " + urlBase);
444 return urlBase;
445 } // getUrl()
446
447 /** Get a URL that points to either an HTTP server or a file system
448 * that contains GATE files (such as test cases).
449 * Calls <TT>getUrl()</TT> then adds the <TT>path</TT> parameter to
450 * the result.
451 * @param path a path to add to the base URL.
452 * @see #getUrl()
453 */
454 public static URL getUrl(String path) throws GateException {
455 getUrl();
456 if(urlBase == null)
457 return null;
458
459 URL newUrl = null;
460 try {
461 newUrl = new URL(urlBase, path);
462 } catch(MalformedURLException e) {
463 throw new GateException("Bad URL, getUrl( " + path + "): " + e);
464 }
465
466 if(DEBUG) Out.prln("getUrl(" + path + ") returned " + newUrl);
467 return newUrl;
468 } // getUrl(path)
469
470 /** Flag controlling whether we should try to access the net, e.g. when
471 * setting up a base URL.
472 */
473 private static boolean netConnected = true;
474
475 private static int lastSym;
476
477 /**
478 * A list of names of classes that implement {@link gate.creole.ir.IREngine}
479 * that will be used as information retrieval engines.
480 */
481 private static Set registeredIREngines = new HashSet();
482
483 /**
484 * Registers a new IR engine. The class named should implement
485 * {@link gate.creole.ir.IREngine}.
486 * @param className the fully qualified name of the class to be registered
487 * @throws GateException if the class does not implement the
488 * {@link gate.creole.ir.IREngine} interface.
489 * @throws ClassNotFoundException if the named class cannot be found.
490 */
491 public static void registerIREngine(String className)
492 throws GateException, ClassNotFoundException{
493 Class aClass = Class.forName(className);
494 if(gate.creole.ir.IREngine.class.isAssignableFrom(aClass)){
495 registeredIREngines.add(className);
496 }else{
497 throw new GateException(className + " does not implement the " +
498 gate.creole.ir.IREngine.class.getName() +
499 " interface!");
500 }
501 }
502
503 /**
504 * Unregisters a previously registered IR engine.
505 * @param className the name of the class to be removed from the list of
506 * registered IR engines.
507 * @return true if the class was found and removed.
508 */
509 public static boolean unregisterIREngine(String className){
510 return registeredIREngines.remove(className);
511 }
512
513 /**
514 * Gets the set of registered IR engines.
515 * @return an unmodifiable {@link java.util.Set} value.
516 */
517 public static Set getRegisteredIREngines(){
518 return Collections.unmodifiableSet(registeredIREngines);
519 }
520
521 /** Should we assume we're connected to the net? */
522 public static boolean isNetConnected() { return netConnected; }
523
524 /**
525 * Tell GATE whether to assume we're connected to the net. Has to be
526 * called <B>before</B> {@link #init()}.
527 */
528 public static void setNetConnected(boolean b) { netConnected = b; }
529
530 /**
531 * Flag controlling whether we should try to access a web server on
532 * localhost, e.g. when setting up a base URL. Has to be
533 * called <B>before</B> {@link #init()}.
534 */
535 private static boolean localWebServer = true;
536
537 /** Should we assume there's a local web server? */
538 public static boolean isLocalWebServer() { return localWebServer; }
539
540 /** Tell GATE whether to assume there's a local web server. */
541 public static void setLocalWebServer(boolean b) { localWebServer = b; }
542
543 /** Try to contact a network server. When sucessfull sets urlBase to an HTTP
544 * URL for the server.
545 * @param hostName the name of the host to try and connect to
546 * @param serverPort the port to try and connect to
547 * @param path a path to append to the URL when we make a successfull
548 * connection. E.g. for host xyz, port 80, path /thing, the resultant URL
549 * would be <TT>http://xyz:80/thing</TT>.
550 */
551 public static boolean tryNetServer(
552 String hostName, int serverPort, String path
553 ) throws MalformedURLException {
554 Socket socket = null;
555 if(DEBUG)
556 Out.prln(
557 "tryNetServer(hostName=" + hostName + ", serverPort=" + serverPort +
558 ", path=" + path +")"
559 );
560
561 // is the host listening at the port?
562 try{
563 URL url = new URL("http://" + hostName + ":" + serverPort + "/");
564 URLConnection uConn = url.openConnection();
565 HttpURLConnection huConn = null;
566 if(uConn instanceof HttpURLConnection)
567 huConn = (HttpURLConnection)uConn;
568 if(huConn.getResponseCode() == -1) return false;
569 } catch (IOException e){
570 return false;
571 }
572
573 // if(socket != null) {
574 urlBase = new URL("http", hostName, serverPort, path);
575 return true;
576 // }
577
578 // return false;
579 } // tryNetServer()
580
581 /** Try to find GATE files in the local file system */
582 protected static boolean tryFileSystem() throws MalformedURLException {
583 String urlBaseName = locateGateFiles();
584 if(DEBUG) Out.prln("tryFileSystem: " + urlBaseName);
585
586 urlBase = new URL(urlBaseName + "gate/resources/gate.ac.uk/");
587 return urlBase == null;
588 } // tryFileSystem()
589
590 /**
591 * Find the location of the GATE binaries (and resources) in the
592 * local file system.
593 */
594 public static String locateGateFiles() {
595 String aGateResourceName = "gate/resources/creole/creole.xml";
596 URL resourcesUrl = Gate.getClassLoader().getResource(aGateResourceName);
597
598 StringBuffer basePath = new StringBuffer(resourcesUrl.toExternalForm());
599 String urlBaseName =
600 basePath.substring(0, basePath.length() - aGateResourceName.length());
601
602 return urlBaseName;
603 } // locateGateFiles
604
605 /**
606 * Checks whether a particular class is a Gate defined type
607 */
608 public static boolean isGateType(String classname){
609 boolean res = getCreoleRegister().containsKey(classname);
610 if(!res){
611 try{
612 Class aClass = Class.forName(classname);
613 res = Resource.class.isAssignableFrom(aClass) ||
614 Controller.class.isAssignableFrom(aClass) ||
615 DataStore.class.isAssignableFrom(aClass);
616 }catch(ClassNotFoundException cnfe){
617 return false;
618 }
619 }
620 return res;
621 }
622
623 /** Returns the value for the HIDDEN attribute of a feature map */
624 static public boolean getHiddenAttribute(FeatureMap fm){
625 if(fm == null) return false;
626 Object value = fm.get("gate.HIDDEN");
627 return value != null &&
628 value instanceof String &&
629 ((String)value).equals("true");
630 }
631
632 /** Sets the value for the HIDDEN attribute of a feature map */
633 static public void setHiddenAttribute(FeatureMap fm, boolean hidden){
634 if(hidden){
635 fm.put("gate.HIDDEN", "true");
636 }else{
637 fm.remove("gate.HIDDEN");
638 }
639 }
640
641
642 /** Registers a {@link gate.event.CreoleListener} with the Gate system
643 */
644 public static synchronized void addCreoleListener(CreoleListener l){
645 creoleRegister.addCreoleListener(l);
646 } // addCreoleListener
647
648 /** Set the URL base for GATE files, e.g. <TT>http://gate.ac.uk/</TT>. */
649 public static void setUrlBase(URL urlBase) { Gate.urlBase = urlBase; }
650
651 /** The URL base for GATE files, e.g. <TT>http://gate.ac.uk/</TT>. */
652 private static URL urlBase = null;
653
654 /** Class loader used e.g. for loading CREOLE modules, of compiling
655 * JAPE rule RHSs.
656 */
657 private static GateClassLoader classLoader = null;
658
659 /** Get the GATE class loader. */
660 public static GateClassLoader getClassLoader() { return classLoader; }
661
662 /** The CREOLE register. */
663 private static CreoleRegister creoleRegister = null;
664
665 /** Get the CREOLE register. */
666 public static CreoleRegister getCreoleRegister() { return creoleRegister; }
667
668 /** The DataStore register */
669 private static DataStoreRegister dataStoreRegister = null;
670
671 /**
672 * The current executable under execution.
673 */
674 private static gate.Executable currentExecutable;
675
676 /** Get the DataStore register. */
677 public static DataStoreRegister getDataStoreRegister() {
678 return dataStoreRegister;
679 } // getDataStoreRegister
680
681 /**
682 * Sets the {@link Executable} currently under execution.
683 * At a given time there can be only one executable set. After the executable
684 * has finished its execution this value should be set back to null.
685 * An attempt to set the executable while this value is not null will result
686 * in the method call waiting until the old executable is set to null.
687 */
688 public synchronized static void setExecutable(gate.Executable executable) {
689 if(executable == null) currentExecutable = executable;
690 else{
691 while(getExecutable() != null){
692 try{
693 Thread.sleep(200);
694 }catch(InterruptedException ie){
695 throw new LuckyException(ie.toString());
696 }
697 }
698 currentExecutable = executable;
699 }
700 } // setExecutable
701
702 /**
703 * Returns the curently set executable.
704 * @see #setExecutable(gate.Executable)
705 */
706 public synchronized static gate.Executable getExecutable() {
707 return currentExecutable;
708 } // getExecutable
709
710
711 /**
712 * Returns a new unique string
713 */
714 public synchronized static String genSym() {
715 StringBuffer buff = new StringBuffer(Integer.toHexString(lastSym++).
716 toUpperCase());
717 for(int i = buff.length(); i <= 4; i++) buff.insert(0, '0');
718 return buff.toString();
719 } // genSym
720
721 /** GATE development environment configuration data (stored in gate.xml). */
722 private static OptionsMap userConfig = new OptionsMap();
723
724 /**
725 * This map stores the init-time config data in case we need it later.
726 * GATE development environment configuration data (stored in gate.xml).
727 */
728 private static OptionsMap originalUserConfig = new OptionsMap();
729
730 /** Name of the XML element for GATE development environment config data. */
731 private static String userConfigElement = "GATECONFIG";
732
733 /**
734 * Gate the name of the XML element for GATE development environment
735 * config data.
736 */
737 public static String getUserConfigElement() { return userConfigElement; }
738
739 /**
740 * Get the site config file (generally set during command-line processing
741 * or as a <TT>gate.config</TT> property).
742 * If the config is null, this method checks the <TT>gate.config</TT>
743 * property and uses it if non-null.
744 */
745 public static File getSiteConfigFile() {
746 if(siteConfigFile == null) {
747 String gateConfigProperty = System.getProperty(GATE_CONFIG_PROPERTY);
748 if(gateConfigProperty != null)
749 siteConfigFile = new File(gateConfigProperty);
750 }
751 return siteConfigFile;
752 } // getSiteConfigFile
753
754 /** Set the site config file (e.g. during command-line processing). */
755 public static void setSiteConfigFile(File siteConfigFile) {
756 Gate.siteConfigFile = siteConfigFile;
757 } // setSiteConfigFile
758
759 /** Site config file */
760 private static File siteConfigFile;
761
762 /** Shorthand for local newline */
763 private static String nl = Strings.getNl();
764
765 /** An empty config data file. */
766 private static String emptyConfigFile =
767 "<?xml version=\"1.0\"?>" + nl +
768 "<!-- " + GATE_DOT_XML + ": GATE configuration data -->" + nl +
769 "<GATE>" + nl +
770 "" + nl +
771 "<!-- NOTE: the next element may be overwritten by the GUI!!! -->" + nl +
772 "<" + userConfigElement + "/>" + nl +
773 "" + nl +
774 "</GATE>" + nl;
775
776 /**
777 * Get an empty config file. <B>NOTE:</B> this method is intended only
778 * for use by the test suite.
779 */
780 public static String getEmptyConfigFile() { return emptyConfigFile; }
781
782 /**
783 * Get the GATE development environment configuration data
784 * (initialised from <TT>gate.xml</TT>).
785 */
786 public static OptionsMap getUserConfig() { return userConfig; }
787
788 /**
789 * Get the original, initialisation-time,
790 * GATE development environment configuration data
791 * (initialised from <TT>gate.xml</TT>).
792 */
793 public static OptionsMap getOriginalUserConfig() {
794 return originalUserConfig;
795 } // getOriginalUserConfig
796
797 /**
798 * Update the GATE development environment configuration data in the
799 * user's <TT>gate.xml</TT> file (create one if it doesn't exist).
800 */
801 public static void writeUserConfig() throws GateException {
802 //update the values for knownPluginPath
803 String knownPluginPath = "";
804 Iterator pluginIter = knownPlugins.iterator();
805 while(pluginIter.hasNext()){
806 URL aPluginURL = (URL)pluginIter.next();
807 if(knownPluginPath.length() > 0) knownPluginPath += ";";
808 knownPluginPath += aPluginURL.toExternalForm();
809 }
810 getUserConfig().put(KNOWN_PLUGIN_PATH_KEY, knownPluginPath);
811
812 //update the autoload plugin list
813 String loadPluginPath = "";
814 pluginIter = autoloadPlugins.iterator();
815 while(pluginIter.hasNext()){
816 URL aPluginURL = (URL)pluginIter.next();
817 if(loadPluginPath.length() > 0) loadPluginPath += ";";
818 loadPluginPath += aPluginURL.toExternalForm();
819 }
820 getUserConfig().put(LOAD_PLUGIN_PATH_KEY, loadPluginPath);
821
822 // the user's config file
823 String configFileName = getUserConfigFileName();
824 File configFile = new File(configFileName);
825
826 // create if not there, then update
827 try {
828 // if the file doesn't exist, create one with an empty GATECONFIG
829 if(! configFile.exists()) {
830 FileWriter writer = new FileWriter(configFile);
831 writer.write(emptyConfigFile);
832 writer.close();
833 }
834
835 // update the config element of the file
836 Files.updateXmlElement(
837 new File(configFileName), userConfigElement, userConfig
838 );
839
840 } catch(IOException e) {
841 throw new GateException(
842 "problem writing user " + GATE_DOT_XML + ": " + nl + e.toString()
843 );
844 }
845 } // writeUserConfig
846
847 /**
848 * Get the name of the user's <TT>gate.xml</TT> config file (this
849 * doesn't guarantee that file exists!).
850 */
851 public static String getUserConfigFileName() {
852 String filePrefix = "";
853 if(runningOnUnix()) filePrefix = ".";
854
855 String userConfigName =
856 System.getProperty("user.home") + Strings.getFileSep() +
857 filePrefix + GATE_DOT_XML;
858 return userConfigName;
859 } // getUserConfigFileName
860
861 /**
862 * Get the name of the user's <TT>gate.ser</TT> session state file (this
863 * doesn't guarantee that file exists!).
864 */
865 public static String getUserSessionFileName() {
866 String filePrefix = "";
867 if(runningOnUnix()) filePrefix = ".";
868
869 String userSessionName =
870 System.getProperty("user.home") + Strings.getFileSep() +
871 filePrefix + GATE_DOT_SER;
872 return userSessionName;
873 } // getUserSessionFileName
874
875 /**
876 * This method tries to guess if we are on a UNIX system. It does this
877 * by checking the value of <TT>System.getProperty("file.separator")</TT>;
878 * if this is "/" it concludes we are on UNIX. <B>This is obviously not
879 * a very good idea in the general case, so nothing much should be made
880 * to depend on this method (e.g. just naming of config file
881 * <TT>.gate.xml</TT> as opposed to <TT>gate.xml</TT>)</B>.
882 */
883 public static boolean runningOnUnix() {
884 return Strings.getFileSep().equals("/");
885 } // runningOnUnix
886
887 /**
888 * Returns the list of CREOLE directories the system knows about (either
889 * pre-installed plugins in the plugins directory or CREOLE directories that
890 * have previously been loaded manually).
891 * @return a {@link List} of {@link URL}s.
892 */
893 public static List getKnownPlugins(){
894 return knownPlugins;
895 }
896
897 public static void addKnownPlugin(URL pluginURL){
898 if(knownPlugins.contains(pluginURL)) return;
899 knownPlugins.add(pluginURL);
900 }
901
902 /**
903 * Returns the list of CREOLE directories the system loads automatically at
904 * start-up.
905 * @return a {@link List} of {@link URL}s.
906 */
907 public static List getAutoloadPlugins(){
908 return autoloadPlugins;
909 }
910
911 public static void addAutoloadPlugin(URL pluginUrl){
912 if(autoloadPlugins.contains(pluginUrl))return;
913 //make sure it's known
914 addKnownPlugin(pluginUrl);
915 //add it to autoload list
916 autoloadPlugins.add(pluginUrl);
917 }
918
919 /**
920 * Gets the information about a known directory.
921 * @param directory the URL for the directory in question.
922 * @return a {@link DirectoryInfo} value.
923 */
924 public static DirectoryInfo getDirectoryInfo(URL directory){
925 if(!knownPlugins.contains(directory)) return null;
926 DirectoryInfo dInfo = (DirectoryInfo)pluginData.get(directory);
927 if(dInfo == null){
928 dInfo = new DirectoryInfo(directory);
929 pluginData.put(directory, dInfo);
930 }
931 return dInfo;
932 }
933
934 /**
935 * Tells the system to "forget" about one previously known
936 * directory. If the specified directory was loaded, it will be unloaded as
937 * well - i.e. all the metadata relating to resources defined by this
938 * directory will be removed from memory.
939 * @param directory
940 */
941 public static void removeKnownDirectory(URL directory){
942 DirectoryInfo dInfo = (DirectoryInfo)pluginData.get(directory);
943 if(dInfo != null){
944 creoleRegister.removeDirectory(directory);
945 knownPlugins.remove(directory);
946 pluginData.remove(directory);
947 }
948 }
949
950 /**
951 * Stores information about the contents of a CREOLE directory.
952 */
953 public static class DirectoryInfo{
954 public DirectoryInfo(URL url){
955 this.url = url;
956 valid = true;
957 resourceInfoList = new ArrayList();
958 //this may invalidate it if something goes wrong
959 parseCreole();
960 }
961
962 /**
963 * Performs a shallow parse of the creole.xml file to get the information
964 * about the resources contained.
965 */
966 protected void parseCreole(){
967 SAXBuilder builder = new SAXBuilder(false);
968 try{
969 if(!url.getPath().endsWith("/"))
970 url = new URL(url.getProtocol(), url.getHost(),
971 url.getPort(), url.getPath() + "/");
972 URL creoleFileURL = new URL(url, "creole.xml");
973 org.jdom.Document creoleDoc = builder.build(creoleFileURL);
974 List jobsList = new ArrayList();
975 jobsList.add(creoleDoc.getRootElement());
976 while(!jobsList.isEmpty()){
977 Element currentElem = (Element)jobsList.remove(0);
978 if(currentElem.getName().equalsIgnoreCase("RESOURCE")){
979 //we don't go deeper than resources so no recursion here
980 String resName = currentElem.getChildTextTrim("NAME");
981 String resClass = currentElem.getChildTextTrim("CLASS");
982 String resComment = currentElem.getChildTextTrim("COMMENT");
983 //create the handler
984 ResourceInfo rHandler = new ResourceInfo(resName, resClass,
985 resComment);
986 resourceInfoList.add(rHandler);
987 }else{
988 //this is some higher level element -> simulate recursion
989 //we want Depth-first-search so we need to add at the beginning
990 List newJobsList = new ArrayList(currentElem.getChildren());
991 newJobsList.addAll(jobsList);
992 jobsList = newJobsList;
993 }
994 }
995 }catch(IOException ioe){
996 valid = false;
997 ioe.printStackTrace();
998 }catch(JDOMException jde){
999 valid = false;
1000 jde.printStackTrace();
1001 }
1002 }
1003
1004 /**
1005 * @return Returns the resourceInfoList.
1006 */
1007 public List getResourceInfoList(){
1008 return resourceInfoList;
1009 }
1010 /**
1011 * @return Returns the url.
1012 */
1013 public URL getUrl(){
1014 return url;
1015 }
1016 /**
1017 * @return Returns the valid.
1018 */
1019 public boolean isValid(){
1020 return valid;
1021 }
1022 /**
1023 * The URL for the CREOLE directory.
1024 */
1025 protected URL url;
1026
1027 /**
1028 * Is the directory valid (i.e. is the location reachable and the
1029 * creole.xml file parsable).
1030 */
1031 protected boolean valid;
1032
1033 /**
1034 * The list of {@link Gate.ResourceInfo} objects.
1035 */
1036 protected List resourceInfoList;
1037 }
1038
1039 /**
1040 * Stores information about a resource defined by a CREOLE directory. The
1041 * resource might not have been loaded in the system so not all information
1042 * normally provided by the {@link ResourceData} class is available. This is
1043 * what makes this class different from {@link ResourceData}.
1044 */
1045 public static class ResourceInfo{
1046 public ResourceInfo(String name, String className, String comment){
1047 this.resourceClassName = className;
1048 this.resourceName = name;
1049 this.resourceComment = comment;
1050 }
1051
1052 /**
1053 * @return Returns the resourceClassName.
1054 */
1055 public String getResourceClassName(){
1056 return resourceClassName;
1057 }
1058 /**
1059 * @return Returns the resourceComment.
1060 */
1061 public String getResourceComment(){
1062 return resourceComment;
1063 }
1064 /**
1065 * @return Returns the resourceName.
1066 */
1067 public String getResourceName(){
1068 return resourceName;
1069 }
1070 /**
1071 * The class for the resource.
1072 */
1073 protected String resourceClassName;
1074
1075 /**
1076 * The resource name.
1077 */
1078 protected String resourceName;
1079
1080 /**
1081 * The comment for the resource.
1082 */
1083 protected String resourceComment;
1084 }
1085
1086 /**
1087 * The list of plugins (aka CREOLE directories) the system knows about.
1088 * This list contains URL objects.
1089 */
1090 protected static List knownPlugins;
1091
1092 /**
1093 * The list of plugins (aka CREOLE directories) the system loads automatically
1094 * at start-up.
1095 * This list contains URL objects.
1096 */
1097 protected static List autoloadPlugins;
1098
1099
1100 /**
1101 * Map from URL of directory to {@link DirectoryInfo}.
1102 */
1103 protected static Map pluginData;
1104
1105
1106
1107 /** Flag for SLUG GUI start instead of standart GATE GUI. */
1108 private static boolean slugGui = false;
1109
1110 /** Should we start SLUG GUI. */
1111 public static boolean isSlugGui() { return slugGui; }
1112
1113 /** Tell GATE whether to start SLUG GUI. */
1114 public static void setSlugGui(boolean b) { slugGui = b; }
1115
1116 /** Flag for Jape Debugger integration. */
1117 private static boolean enableJapeDebug = false;
1118
1119 /** Should we enable Jape Debugger. */
1120 public static boolean isEnableJapeDebug() { return enableJapeDebug; }
1121
1122 /** Tell GATE whether to enable Jape Debugger. */
1123 public static void setEnableJapeDebug(boolean b) { enableJapeDebug = b; }
1124
1125} // class Gate
1126