1
14 package gate.util.persistence;
15
16 import java.io.*;
17 import java.net.MalformedURLException;
18 import java.net.URL;
19 import java.text.NumberFormat;
20 import java.util.*;
21
22 import gate.*;
23 import gate.creole.*;
24 import gate.event.ProgressListener;
25 import gate.event.StatusListener;
26 import gate.gui.MainFrame;
27 import gate.persist.PersistenceException;
28 import gate.util.*;
29
30
36 public class PersistenceManager {
37
38 private static final boolean DEBUG = false;
39
40
46 static protected class ObjectHolder{
47 ObjectHolder(Object target){
48 this.target = target;
49 }
50
51 public int hashCode(){
52 return System.identityHashCode(target);
53 }
54
55 public boolean equals(Object obj){
56 if(obj instanceof ObjectHolder)
57 return ((ObjectHolder)obj).target == this.target;
58 else return false;
59 }
60
61 public Object getTarget(){
62 return target;
63 }
64
65 private Object target;
66 }
68
79 public static class SlashDevSlashNull implements Persistence{
80
83 public void extractDataFromSource(Object source)throws PersistenceException{
84 }
85
86
89 public Object createObject()throws PersistenceException,
90 ResourceInstantiationException{
91 return null;
92 }
93 static final long serialVersionUID = -8665414981783519937L;
94 }
95
96
102 public static class URLHolder implements Persistence{
103
107 public void extractDataFromSource(Object source)throws PersistenceException{
108 try{
109 URL url = (URL)source;
110 if(url.getProtocol().equals("file")){
111 try{
112 urlString = relativePathMarker +
113 getRelativePath(persistenceFile.toURL(), url);
114 }catch(MalformedURLException mue){
115 urlString = ((URL)source).toExternalForm();
116 }
117 }else{
118 urlString = ((URL)source).toExternalForm();
119 }
120 }catch(ClassCastException cce){
121 throw new PersistenceException(cce);
122 }
123 }
124
125
129 public Object createObject()throws PersistenceException{
130 try{
131 if(urlString.startsWith(relativePathMarker)){
132 URL context = persistenceURL;
133 return new URL(context,
134 urlString.substring(relativePathMarker.length()));
135 }else return new URL(urlString);
136 }catch(MalformedURLException mue){
137 throw new PersistenceException(mue);
138 }
139 }
140 String urlString;
141
145 private static final String relativePathMarker = "$relpath$";
146 static final long serialVersionUID = 7943459208429026229L;
147 }
148
149 public static class ClassComparator implements Comparator{
150
161 public int compare(Object o1, Object o2){
162 Class c1 = (Class)o1;
163 Class c2 = (Class)o2;
164
165 if(c1.equals(c2)) return 0;
166 if(c1.isAssignableFrom(c2)) return 1;
167 if(c2.isAssignableFrom(c1)) return -1;
168 throw new NotComparableException();
169 }
170 }
171
172
176 public static class NotComparableException extends RuntimeException{
177 public NotComparableException(String message){
178 super(message);
179 }
180 public NotComparableException(){
181 }
182 }
183
184
192 static Serializable getPersistentRepresentation(Object target)
193 throws PersistenceException{
194 if(target == null) return null;
195 Persistence res = (Persistence)existingPersitentReplacements.
197 get(new ObjectHolder(target));
198 if(res != null) return res;
199
200 Class type = target.getClass();
201 Class newType = getMostSpecificPersistentType(type);
202 if(newType == null){
203 if(target instanceof Serializable) return (Serializable)target;
205 else throw new PersistenceException(
206 "Could not find a serialisable replacement for " + type);
207 }
208
209 try{
211 res = (Persistence)newType.newInstance();
212 }catch(Exception e){
213 throw new PersistenceException(e);
214 }
215 if(target instanceof NameBearer){
216 StatusListener sListener = (StatusListener)MainFrame.getListeners().
217 get("gate.event.StatusListener");
218 if(sListener != null){
219 sListener.statusChanged("Storing " + ((NameBearer)target).getName());
220 }
221 }
222 res.extractDataFromSource(target);
223 existingPersitentReplacements.put(new ObjectHolder(target), res);
224 return res;
225 }
226
227
228 static Object getTransientRepresentation(Object target)
229 throws PersistenceException,
230 ResourceInstantiationException{
231
232 if(target == null || target instanceof SlashDevSlashNull) return null;
233 if(target instanceof Persistence){
234 Object resultKey = new ObjectHolder(target);
235 Object result = existingTransientValues.get(resultKey);
237 if(result != null) return result;
238
239 result = ((Persistence)target).createObject();
241 existingTransientValues.put(resultKey, result);
242 return result;
243 }else return target;
244 }
245
246
247
263 protected static Class getMostSpecificPersistentType(Class type){
264 List expansionSet = new ArrayList();
268 expansionSet.add(type);
269
270
274 List userInterfaces = new ArrayList();
277 List gateInterfaces = new ArrayList();
278 List javaInterfaces = new ArrayList();
279 while(!expansionSet.isEmpty()){
280 Iterator typesIter = expansionSet.iterator();
282 while(typesIter.hasNext()){
283 Class result = (Class)persistentReplacementTypes.get(typesIter.next());
284 if(result != null){
285 return result;
286 }
287 }
288
292 if(type != null) type = type.getSuperclass();
294
295
296 userInterfaces.clear();
297 gateInterfaces.clear();
298 javaInterfaces.clear();
299
300 typesIter = expansionSet.iterator();
301 while(typesIter.hasNext()){
302 Class aType = (Class)typesIter.next();
303 Class[] interfaces = aType.getInterfaces();
304 for(int i = 0; i < interfaces.length; i++){
306 Class anIterf = interfaces[i];
307 String interfType = anIterf.getName();
308 if(interfType.startsWith("java")){
309 javaInterfaces.add(anIterf);
310 }else if(interfType.startsWith("gate")){
311 gateInterfaces.add(anIterf);
312 }else userInterfaces.add(anIterf);
313 }
314 }
315
316 expansionSet.clear();
317 if(type != null) expansionSet.add(type);
318 expansionSet.addAll(userInterfaces);
319 expansionSet.addAll(gateInterfaces);
320 expansionSet.addAll(javaInterfaces);
321 }
322 return null;
324
325 }
367
368
376 public static String getRelativePath(URL context, URL target){
377 if(context.getProtocol().equals("file") &&
378 target.getProtocol().equals("file")){
379
380 try{
382 context = new File(context.getPath()).toURL();
383 }catch(MalformedURLException mue){
384 throw new GateRuntimeException("Could not normalise the file URL:\n"+
385 context + "\nThe problem was:\n" +mue);
386 }
387 try{
388 target = new File(target.getPath()).toURL();
389 }catch(MalformedURLException mue){
390 throw new GateRuntimeException("Could not normalise the file URL:\n"+
391 target + "\nThe problem was:\n" +mue);
392 }
393 List targetPathComponents = new ArrayList();
394 File aFile = new File(target.getPath()).getParentFile();
395 while(aFile != null){
396 targetPathComponents.add(0, aFile);
397 aFile = aFile.getParentFile();
398 }
399 List contextPathComponents = new ArrayList();
400 aFile = new File(context.getPath()).getParentFile();
401 while(aFile != null){
402 contextPathComponents.add(0, aFile);
403 aFile = aFile.getParentFile();
404 }
405 int commonPathElements = 0;
408 while(commonPathElements < targetPathComponents.size() &&
409 commonPathElements < contextPathComponents.size() &&
410 targetPathComponents.get(commonPathElements).
411 equals(contextPathComponents.get(commonPathElements)))
412 commonPathElements++;
413 String relativePath = "";
415 for(int i = commonPathElements;
416 i < contextPathComponents.size(); i++){
417 if(relativePath.length() == 0) relativePath += "..";
418 else relativePath += "/..";
419 }
420 for(int i = commonPathElements; i < targetPathComponents.size(); i++){
421 String aDirName = ((File)targetPathComponents.get(i)).getName();
422 if(aDirName.length() == 0){
423 aDirName = ((File)targetPathComponents.get(i)).getAbsolutePath();
424 if(aDirName.endsWith(File.separator)){
425 aDirName = aDirName.substring(0, aDirName.length() -
426 File.separator.length());
427 }
428 }
429 if(relativePath.length() == 0){
431 relativePath += aDirName;
432 }else{
433 relativePath += "/" + aDirName;
434 }
435 }
436 if(relativePath.length() == 0){
438 relativePath += new File(target.getPath()).getName();
439 }else{
440 relativePath += "/" + new File(target.getPath()).getName();
441 }
442
443 return relativePath;
444 }else{
445 throw new GateRuntimeException("Both the target and the context URLs " +
446 "need to be \"file:\" URLs!");
447 }
448 }
449
450 public static void saveObjectToFile(Object obj, File file)
451 throws PersistenceException, IOException {
452 ProgressListener pListener = (ProgressListener)MainFrame.getListeners()
453 .get("gate.event.ProgressListener");
454 StatusListener sListener = (gate.event.StatusListener)
455 MainFrame.getListeners().
456 get("gate.event.StatusListener");
457 long startTime = System.currentTimeMillis();
458 if(pListener != null) pListener.progressChanged(0);
459 ObjectOutputStream oos = null;
462 com.thoughtworks.xstream.XStream xstream = null;
463 FileWriter fileWriter = null;
464 persistenceFile = file;
465 try{
466 existingPersitentReplacements.clear();
468 existingPersitentReplacements.clear();
469
470 if (Gate.getUseXMLSerialization()) {
471 xstream = new com.thoughtworks.xstream.XStream();
474 fileWriter = new FileWriter(file);
475 } else {
476 oos = new ObjectOutputStream(new FileOutputStream(file));
477 }
478
479 List urlList = new ArrayList(Gate.getCreoleRegister().getDirectories());
481 Object persistentList = getPersistentRepresentation(urlList);
482
483 Object persistentObject = getPersistentRepresentation(obj);
484
485 if (Gate.getUseXMLSerialization()) {
486 GateApplication gateApplication = new GateApplication();
489 gateApplication.urlList = persistentList;
490 gateApplication.application = persistentObject;
491
492 xstream.toXML(gateApplication, fileWriter);
494 } else {
495 oos.writeObject(persistentList);
497
498 oos.writeObject(persistentObject);
500 }
501
502 }finally{
503 persistenceFile = null;
504 if(oos != null){
505 oos.flush();
506 oos.close();
507 }
508 if (fileWriter != null) {
509 fileWriter.flush();
511 fileWriter.close();
512 }
513 long endTime = System.currentTimeMillis();
514 if(sListener != null) sListener.statusChanged(
515 "Storing completed in " +
516 NumberFormat.getInstance().format(
517 (double)(endTime - startTime) / 1000) + " seconds");
518 if(pListener != null) pListener.processFinished();
519 }
520 }
521
522 public static Object loadObjectFromFile(File file)
523 throws PersistenceException, IOException,
524 ResourceInstantiationException {
525 return loadObjectFromUrl(file.toURL());
526 }
527
528 public static Object loadObjectFromUrl(URL url)
529 throws PersistenceException, IOException,
530 ResourceInstantiationException {
531 exceptionOccured = false;
532 ProgressListener pListener = (ProgressListener)MainFrame.getListeners().
533 get("gate.event.ProgressListener");
534 StatusListener sListener = (gate.event.StatusListener)
535 MainFrame.getListeners()
536 .get("gate.event.StatusListener");
537 if(pListener != null) pListener.progressChanged(0);
538 long startTime = System.currentTimeMillis();
539 persistenceURL = url;
540 boolean xmlStream = isXmlApplicationFile(url);
543 ObjectInputStream ois = null;
544 java.io.Reader reader = null;
545 com.thoughtworks.xstream.XStream xstream = null;
546 if (xmlStream) {
549 reader = new java.io.InputStreamReader(url.openStream());
550 xstream = new com.thoughtworks.xstream.XStream();
551 } else {
552 ois = new ObjectInputStream(url.openStream());
553 }
554 Object res = null;
555 try{
556 Iterator urlIter;
557 GateApplication gateApplication = null;
560 if (xmlStream) {
561 if (DEBUG)
562 System.out.println("About to load application");
563 gateApplication = (GateApplication)xstream.fromXML(reader);
565 reader.close();
566 if (DEBUG)
567 System.out.println("About to extract url list");
568 urlIter =
570 ((Collection)getTransientRepresentation(gateApplication.urlList))
571 .iterator();
572 if (DEBUG)
573 System.out.println("URL list loaded");
574 } else {
575 urlIter = ((Collection)
578
579 getTransientRepresentation(ois.readObject())).
580 iterator();
581 }
582 while(urlIter.hasNext()){
583 URL anUrl = (URL)urlIter.next();
584 try{
585 Gate.getCreoleRegister().registerDirectories(anUrl);
586 }catch(GateException ge){
587 Err.prln("Could not reload creole directory " +
588 anUrl.toExternalForm());
589 }
590 }
591
592 if (xmlStream) {
594 if (DEBUG)
595 System.out.println("About to load application itself");
596 res = gateApplication.application;
599 if (DEBUG)
600 System.out.println("Application loaded");
601 } else {
602 res = ois.readObject();
604 ois.close();
605 }
606
607 existingTransientValues.clear();
609 res = getTransientRepresentation(res);
610 existingTransientValues.clear();
611 long endTime = System.currentTimeMillis();
612 if(sListener != null) sListener.statusChanged(
613 "Loading completed in " +
614 NumberFormat.getInstance().format(
615 (double)(endTime - startTime) / 1000) + " seconds");
616 if(pListener != null) pListener.processFinished();
617 if(exceptionOccured){
618 throw new PersistenceException("There were errors!\n" +
619 "See messages for details...");
620 }
621 return res;
622 }catch(ResourceInstantiationException rie){
623 if(sListener != null) sListener.statusChanged("Loading failed!");
624 if(pListener != null) pListener.processFinished();
625 throw rie;
626 }catch(Exception ex){
627 if(sListener != null) sListener.statusChanged("Loading failed!");
628 if(pListener != null) pListener.processFinished();
629 throw new PersistenceException(ex);
630 }finally{
631 persistenceURL = null;
632 }
633 }
634
635
643 private static boolean isXmlApplicationFile(URL url)
644 throws java.io.IOException {
645 if (DEBUG) {
646 System.out.println("Checking whether file is xml");
647 }
648 java.io.BufferedReader fileReader =
649 new java.io.BufferedReader(
650 new java.io.InputStreamReader(url.openStream()));
651 String firstLine = fileReader.readLine();
652 fileReader.close();
653
654 if (DEBUG) {
655 System.out.println("isXMLApplicationFile = " +
656 (firstLine.length() >= STARTOFXMLAPPLICATIONFILES.length()
657 && firstLine.substring(0, STARTOFXMLAPPLICATIONFILES.length())
658 .equals(STARTOFXMLAPPLICATIONFILES)));
659 }
660
661 return firstLine.length() >= STARTOFXMLAPPLICATIONFILES.length()
662 && firstLine.substring(0, STARTOFXMLAPPLICATIONFILES.length())
663 .equals(STARTOFXMLAPPLICATIONFILES);
664 }
665
666 private static final String STARTOFXMLAPPLICATIONFILES =
667 "<gate.util.persistence.GateApplication>";
668
669
679 public static Class registerPersitentEquivalent(Class transientType,
680 Class persistentType)
681 throws PersistenceException{
682 if(!Persistence.class.isAssignableFrom(persistentType)){
683 throw new PersistenceException(
684 "Persistent equivalent types have to implement " +
685 Persistence.class.getName() + "!\n" +
686 persistentType.getName() + " does not!");
687 }
688 return (Class)persistentReplacementTypes.put(transientType, persistentType);
689 }
690
691
692
696 private static Map persistentReplacementTypes;
697
698
704 private static Map existingPersitentReplacements;
705
706
713 private static Map existingTransientValues;
714
715 private static ClassComparator classComparator = new ClassComparator();
716
717
721 static boolean exceptionOccured = false;
722
723
727 static File persistenceFile;
728
729
733 static URL persistenceURL;
734
735 static{
736 persistentReplacementTypes = new HashMap();
737 try{
738 registerPersitentEquivalent(VisualResource.class,
740 SlashDevSlashNull.class);
741
742 registerPersitentEquivalent(URL.class, URLHolder.class);
743
744 registerPersitentEquivalent(Map.class, MapPersistence.class);
745 registerPersitentEquivalent(Collection.class,
746 CollectionPersistence.class);
747
748 registerPersitentEquivalent(ProcessingResource.class,
749 PRPersistence.class);
750
751 registerPersitentEquivalent(DataStore.class,
752 DSPersistence.class);
753
754 registerPersitentEquivalent(LanguageResource.class,
755 LRPersistence.class);
756
757 registerPersitentEquivalent(Corpus.class,
758 CorpusPersistence.class);
759
760 registerPersitentEquivalent(Controller.class,
761 ControllerPersistence.class);
762
763 registerPersitentEquivalent(ConditionalController.class,
764 ConditionalControllerPersistence.class);
765
766 registerPersitentEquivalent(LanguageAnalyser.class,
767 LanguageAnalyserPersistence.class);
768
769 registerPersitentEquivalent(SerialAnalyserController.class,
770 SerialAnalyserControllerPersistence.class);
771
772 registerPersitentEquivalent(gate.persist.JDBCDataStore.class,
773 JDBCDSPersistence.class);
774 registerPersitentEquivalent(gate.creole.AnalyserRunningStrategy.class,
775 AnalyserRunningStrategyPersistence.class);
776 }catch(PersistenceException pe){
777 pe.printStackTrace();
779 }
780 existingPersitentReplacements = new HashMap();
781 existingTransientValues = new HashMap();
782 }
783 }
784