1
15
16 package gate.persist;
17
18 import java.io.*;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.*;
22 import java.util.zip.GZIPInputStream;
23 import java.util.zip.GZIPOutputStream;
24
25 import gate.*;
26 import gate.corpora.SerialCorpusImpl;
27 import gate.creole.ResourceData;
28 import gate.event.DatastoreEvent;
29 import gate.event.DatastoreListener;
30 import gate.security.*;
31 import gate.security.SecurityException;
32 import gate.util.*;
33
34
37 public class SerialDataStore
38 extends AbstractFeatureBearer implements DataStore {
39
40
41 private static final boolean DEBUG = false;
42
43
44 protected String name;
45
46
52 public SerialDataStore(String storageDirUrl) throws PersistenceException {
53 setStorageUrl(storageDirUrl);
54 }
56
60 public SerialDataStore() { };
61
62
65 protected File storageDir;
66
67
68 public void setStorageDir(File storageDir) { this.storageDir = storageDir; }
69
70
71 public File getStorageDir() { return storageDir; }
72
73
74 public void setStorageUrl(String urlString) throws PersistenceException {
75 URL storageUrl = null;
76 try {
77 storageUrl = new URL(urlString);
78 } catch (java.net.MalformedURLException ex) {
79 throw new PersistenceException(
80 "The URL passed is not correct: " + urlString
81 );
82 }
83 if(! storageUrl.getProtocol().equalsIgnoreCase("file"))
84 throw new PersistenceException(
85 "A serial data store needs a file URL, not " + storageUrl
86 );
87 this.storageDir = new File(storageUrl.getFile());
88 }
90
91 public String getStorageUrl() {
92 if(storageDir == null) return null;
93
94 URL u = null;
95 try { u = storageDir.toURL(); } catch(MalformedURLException e) {
96 }
99
100 return u.toString();
101 }
103
108 public void create()
109 throws PersistenceException {
110 if(storageDir == null)
111 throw new PersistenceException("null storage directory: cannot create");
112
113 if(! storageDir.exists()) { if(! storageDir.mkdir())
115 throw new
116 PersistenceException("cannot create directory " + storageDir);
117 } else { String[] existingFiles = storageDir.list();
119 if(! (existingFiles == null || existingFiles.length == 0) )
120 throw new PersistenceException(
121 "directory "+ storageDir +" is not empty: cannot use for data store"
122 );
123 }
124
125 try {
127 File versionFile = getVersionFile();
128 OutputStreamWriter osw = new OutputStreamWriter(
129 new FileOutputStream(versionFile)
130 );
131 osw.write(versionNumber + Strings.getNl());
132 osw.close();
133 } catch(IOException e) {
134 throw new PersistenceException("couldn't write version file: " + e);
135 }
136 }
138
139 protected static String versionFileName = "__GATE_SerialDataStore__";
140
141
142 protected static String currentProtocolVersion = null;
143
144
145 protected File getVersionFile() throws IOException {
146 return new File(storageDir, versionFileName);
147 }
149
163 protected String versionNumber = "1.1";
164
165
166 protected String[] protocolVersionNumbers = {
167 "1.0",
168 "1.1"
169 };
171
172 protected boolean isValidProtocolVersion(String versionNumber) {
173 if(versionNumber == null)
174 return false;
175
176 for(int i = 0; i < protocolVersionNumbers.length; i++)
177 if(protocolVersionNumbers[i].equals(versionNumber))
178 return true;
179
180 return false;
181 }
183
185 public void delete() throws PersistenceException {
186 if(storageDir == null || ! Files.rmdir(storageDir))
187 throw new PersistenceException("couldn't delete " + storageDir);
188
189 Gate.getDataStoreRegister().remove(this);
190 }
192
194 public void delete(String lrClassName, Object lrPersistenceId)
195 throws PersistenceException {
196
197 File resourceTypeDirectory = new File(storageDir, lrClassName);
199 if(
200 (! resourceTypeDirectory.exists()) ||
201 (! resourceTypeDirectory.isDirectory())
202 ) {
203 throw new PersistenceException("Can't find " + resourceTypeDirectory);
204 }
205
206 File resourceFile = new File(resourceTypeDirectory, (String)lrPersistenceId);
208 if(! resourceFile.exists() || ! resourceFile.isFile())
209 throw new PersistenceException("Can't find file " + resourceFile);
210
211 if(! resourceFile.delete())
213 throw new PersistenceException("Can't delete file " + resourceFile);
214
215 if(resourceTypeDirectory.list().length == 0)
217 if(! resourceTypeDirectory.delete())
218 throw new PersistenceException("Can't delete " + resourceTypeDirectory);
219
220 fireResourceDeleted(
222 new DatastoreEvent(
223 this, DatastoreEvent.RESOURCE_DELETED, null, (String) lrPersistenceId
224 )
225 );
226 }
228
229 public LanguageResource adopt(LanguageResource lr,SecurityInfo secInfo)
230 throws PersistenceException,gate.security.SecurityException {
231
232
234 DataStore currentDS = lr.getDataStore();
236 if(currentDS == null) { LanguageResource res = lr;
238
239 if (lr instanceof Corpus) {
240 FeatureMap features1 = Factory.newFeatureMap();
241 features1.put("transientSource", lr);
242 try {
243 res = (LanguageResource)
246 Factory.createResource("gate.corpora.SerialCorpusImpl", features1);
247 } catch (gate.creole.ResourceInstantiationException ex) {
252 throw new GateRuntimeException(ex.getMessage());
253 }
254
255 }
256
257 res.setDataStore(this);
258
259 fireResourceAdopted(
261 new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, lr, null)
262 );
263 return res;
264 } else if(currentDS.equals(this)) return lr;
266 else { throw new PersistenceException(
268 "Can't adopt a resource which is already in a different datastore"
269 );
270 }
271
272
273 }
275
276 public void open() throws PersistenceException {
277 if(storageDir == null)
278 throw new PersistenceException("Can't open: storage dir is null");
279
280 if(! storageDir.canRead()) {
282 throw new PersistenceException("Can't read " + storageDir);
283 }
284
285 try {
290 FileReader fis = new FileReader(getVersionFile());
291 BufferedReader isr = new BufferedReader(fis);
292 currentProtocolVersion = isr.readLine();
293 if(DEBUG) Out.prln("opening SDS version " + currentProtocolVersion);
294 isr.close();
295 } catch(IOException e) {
296 throw new PersistenceException(
297 "Invalid storage directory: " + e
298 );
299 }
300 if(! isValidProtocolVersion(currentProtocolVersion))
301 throw new PersistenceException(
302 "Invalid protocol version number: " + currentProtocolVersion
303 );
304
305 }
307
308 public void close() throws PersistenceException {
309 Gate.getDataStoreRegister().remove(this);
310 }
312
315 public void sync(LanguageResource lr) throws PersistenceException {
316
318 if(lr.getDataStore() == null || ! lr.getDataStore().equals(this))
320 throw new PersistenceException(
321 "LR " + lr.getName() + " has not been adopted by this DataStore"
322 );
323
324 ResourceData lrData =
326 (ResourceData) Gate.getCreoleRegister().get(lr.getClass().getName());
327
328 File resourceTypeDirectory = new File(storageDir, lrData.getClassName());
330 if(
331 (! resourceTypeDirectory.exists()) ||
332 (! resourceTypeDirectory.isDirectory())
333 ) {
334 if(! resourceTypeDirectory.mkdir())
335 throw new PersistenceException("Can't write " + resourceTypeDirectory);
336 }
337
338 String lrName = null;
340 Object lrPersistenceId = null;
341 lrName = lr.getName();
342 lrPersistenceId = lr.getLRPersistenceId();
343
344 if(lrName == null)
345 lrName = lrData.getName();
346 if(lrPersistenceId == null) {
347 lrPersistenceId = constructPersistenceId(lrName);
348 lr.setLRPersistenceId(lrPersistenceId);
349 }
350
351 if (lr instanceof Corpus) {
353 if (! (lr instanceof SerialCorpusImpl))
355 throw new PersistenceException("Can't save a corpus which " +
356 "is not of type SerialCorpusImpl!");
357 SerialCorpusImpl corpus = (SerialCorpusImpl) lr;
358 for (int i = 0; i < corpus.size(); i++) {
362 if ( (!corpus.isDocumentLoaded(i)) && corpus.isPersistentDocument(i))
364 continue;
365 if (DEBUG)
366 Out.prln("Saving document at position " + i);
367 if (DEBUG)
368 Out.prln("Document in memory " + corpus.isDocumentLoaded(i));
369 if (DEBUG)
370 Out.prln("is persistent? "+ corpus.isPersistentDocument(i));
371 if (DEBUG)
372 Out.prln("Document name at position" + corpus.getDocumentName(i));
373 Document doc = (Document) corpus.get(i);
374 try {
375 if (doc.getLRPersistenceId() == null) {
377 if (DEBUG) Out.prln("Document adopted" + doc.getName());
378 doc = (Document) this.adopt(doc, null);
379 this.sync(doc);
380 if (DEBUG) Out.prln("Document sync-ed");
381 corpus.setDocumentPersistentID(i, doc.getLRPersistenceId());
382 if (DEBUG) Out.prln("new document ID " + doc.getLRPersistenceId());
383 } else this.sync(doc);
385 } catch (Exception ex) {
386 throw new PersistenceException("Error while saving corpus: "
387 + corpus
388 + "because of an error storing document "
389 + ex.getMessage());
390 }
391 } }
393
394 File resourceFile = new File(resourceTypeDirectory, (String) lrPersistenceId);
396
397 try {
399 OutputStream os = new FileOutputStream(resourceFile);
400
401 if(! currentProtocolVersion.equals("1.0"))
403 os = new GZIPOutputStream(os);
404
405 ObjectOutputStream oos = new ObjectOutputStream(os);
406 oos.writeObject(lr);
407 oos.close();
408 } catch(IOException e) {
409 throw new PersistenceException("Couldn't write to storage file: " + e);
410 }
411
412 fireResourceWritten(
414 new DatastoreEvent(
415 this, DatastoreEvent.RESOURCE_WRITTEN, lr, (String) lrPersistenceId
416 )
417 );
418 }
420
421 protected String constructPersistenceId(String lrName) {
422 return lrName + "___" + new Date().getTime() + "___" + random();
423 }
425
430 public LanguageResource getLr(String lrClassName, Object lrPersistenceId)
431 throws PersistenceException,SecurityException {
432
433 File resourceTypeDirectory = new File(storageDir, lrClassName);
435 if(
436 (! resourceTypeDirectory.exists()) ||
437 (! resourceTypeDirectory.isDirectory())
438 ) {
439 throw new PersistenceException("Can't find " + resourceTypeDirectory);
440 }
441
442 File resourceFile = new File(resourceTypeDirectory, (String)lrPersistenceId);
444 if(! resourceFile.exists() || ! resourceFile.isFile())
445 throw new PersistenceException("Can't find file " + resourceFile);
446
447 LanguageResource lr = null;
449 try {
450 InputStream is = new FileInputStream(resourceFile);
451
452 if(! currentProtocolVersion.equals("1.0"))
454 is = new GZIPInputStream(is);
455
456 ObjectInputStream ois = new ObjectInputStream(is);
457 lr = (LanguageResource) ois.readObject();
458 ois.close();
459 } catch(IOException e) {
460 throw
461 new PersistenceException("Couldn't read file "+resourceFile+": "+e);
462 } catch(ClassNotFoundException ee) {
463 throw
464 new PersistenceException("Couldn't find class "+lrClassName+": "+ee);
465 }
466
467 lr.setDataStore(this);
470 lr.setLRPersistenceId(lrPersistenceId);
471
472 if (DEBUG) Out.prln("LR read in memory: " + lr);
473
474 return lr;
475 }
477
478 public List getLrTypes() throws PersistenceException {
479 if(storageDir == null || ! storageDir.exists())
480 throw new PersistenceException("Can't read storage directory");
481
482 String[] fileArray = storageDir.list();
484 List lrTypes = new ArrayList();
485 for(int i=0; i<fileArray.length; i++)
486 if(! fileArray[i].equals(versionFileName))
487 lrTypes.add(fileArray[i]);
488
489 return lrTypes;
490 }
492
493 public List getLrIds(String lrType) throws PersistenceException {
494 File resourceTypeDir = new File(storageDir, lrType);
496 if(! resourceTypeDir.exists())
497 return Arrays.asList(new String[0]);
498
499 return Arrays.asList(resourceTypeDir.list());
500 }
502
503 public List getLrNames(String lrType) throws PersistenceException {
504 String[] lrFileNames = (String[]) getLrIds(lrType).toArray();
506 ArrayList lrNames = new ArrayList();
507
508 for(int i = 0; i<lrFileNames.length; i++) {
510 String name = getLrName(lrFileNames[i]);
511 lrNames.add(name);
512 }
513
514 return lrNames;
515 }
517
518 public String getLrName(Object lrId) {
519 int secondSeparator = ((String) lrId).lastIndexOf("___");
520 lrId = ((String) lrId).substring(0, secondSeparator);
521 int firstSeparator = ((String) lrId).lastIndexOf("___");
522
523 return ((String) lrId).substring(0, firstSeparator);
524 }
526
530 public void setAutoSaving(boolean autoSaving)
531 throws UnsupportedOperationException {
532 throw new UnsupportedOperationException(
533 "SerialDataStore has no auto-save capability"
534 );
535 }
537
538 public boolean isAutoSaving() { return autoSaving; }
539
540
541 protected boolean autoSaving = false;
542
543
544 protected static int random() {
545 return randomiser.nextInt(9999);
546 }
548
549 protected static Random randomiser = new Random();
550 private transient Vector datastoreListeners;
551
552
553 public String toString() {
554 String nl = Strings.getNl();
555 StringBuffer s = new StringBuffer("SerialDataStore: ");
556 s.append("autoSaving: " + autoSaving);
557 s.append("; storageDir: " + storageDir);
558 s.append(nl);
559
560 return s.toString();
561 }
563
564 public int hashCode(){
565 return getClass().hashCode() ^ storageDir.hashCode();
566 }
568
569 public boolean equals(Object other) {
570
571
572 if (! (other instanceof SerialDataStore))
573 return false;
574
575 if (! ((SerialDataStore)other).storageDir.equals(storageDir))
576 return false;
577
578 if (((SerialDataStore)other).name == name)
581 return true;
582 else
583 return ((SerialDataStore)other).name.equals(name);
584 }
586 public synchronized void removeDatastoreListener(DatastoreListener l) {
587 if (datastoreListeners != null && datastoreListeners.contains(l)) {
588 Vector v = (Vector) datastoreListeners.clone();
589 v.removeElement(l);
590 datastoreListeners = v;
591 }
592 }
593 public synchronized void addDatastoreListener(DatastoreListener l) {
594 Vector v = datastoreListeners == null ? new Vector(2) : (Vector) datastoreListeners.clone();
595 if (!v.contains(l)) {
596 v.addElement(l);
597 datastoreListeners = v;
598 }
599 }
600 protected void fireResourceAdopted(DatastoreEvent e) {
601 if (datastoreListeners != null) {
602 Vector listeners = datastoreListeners;
603 int count = listeners.size();
604 for (int i = 0; i < count; i++) {
605 ((DatastoreListener) listeners.elementAt(i)).resourceAdopted(e);
606 }
607 }
608 }
609 protected void fireResourceDeleted(DatastoreEvent e) {
610 if (datastoreListeners != null) {
611 Vector listeners = datastoreListeners;
612 int count = listeners.size();
613 for (int i = 0; i < count; i++) {
614 ((DatastoreListener) listeners.elementAt(i)).resourceDeleted(e);
615 }
616 }
617 }
618 protected void fireResourceWritten(DatastoreEvent e) {
619 if (datastoreListeners != null) {
620 Vector listeners = datastoreListeners;
621 int count = listeners.size();
622 for (int i = 0; i < count; i++) {
623 ((DatastoreListener) listeners.elementAt(i)).resourceWritten(e);
624 }
625 }
626 }
627
628
632 public String getIconName(){
633 return "ds.gif";
634 }
635
636
639 public String getComment(){
640 return "GATE serial datastore";
641 }
642
643
647 public boolean canReadLR(Object lrID)
648 throws PersistenceException, gate.security.SecurityException{
649
650 return true;
651 }
652
656 public boolean canWriteLR(Object lrID)
657 throws PersistenceException, gate.security.SecurityException{
658
659 return true;
660 }
661
662
663 public void setName(String name){
664 this.name = name;
665 }
666
667
668 public String getName(){
669 return name;
670 }
671
672
673
674
675 public SecurityInfo getSecurityInfo(LanguageResource lr)
676 throws PersistenceException {
677
678 throw new UnsupportedOperationException("security information is not supported "+
679 "for DatabaseDataStore");
680 }
681
682
683 public void setSecurityInfo(LanguageResource lr,SecurityInfo si)
684 throws PersistenceException, gate.security.SecurityException {
685
686 throw new UnsupportedOperationException("security information is not supported "+
687 "for DatabaseDataStore");
688
689 }
690
691
692
693 public void setSession(Session s)
694 throws gate.security.SecurityException {
695
696 }
698
699
700
701
702 public Session getSession(Session s)
703 throws gate.security.SecurityException {
704
705 return null;
706 }
707
708
712 public boolean lockLr(LanguageResource lr)
713 throws PersistenceException,SecurityException {
714 return true;
715 }
716
717
720 public void unlockLr(LanguageResource lr)
721 throws PersistenceException,SecurityException {
722 return;
723 }
724
725
726 public List findLrIds(List constraints) throws PersistenceException {
727 throw new UnsupportedOperationException(
728 "Serial DataStore does not support document retrieval.");
729 }
730
731
735 public List findLrIds(List constraints, String lrType) throws PersistenceException {
736 throw new UnsupportedOperationException(
737 "Serial DataStore does not support document retrieval.");
738 }
739
740 }