001: // PutListResource.java
002: // $Id: PutListResource.java,v 1.14 2007/02/09 21:20:12 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigedit.filters;
007:
008: import java.io.BufferedReader;
009: import java.io.BufferedWriter;
010: import java.io.File;
011: import java.io.FileReader;
012: import java.io.FileWriter;
013: import java.io.IOException;
014: import java.io.PrintStream;
015: import java.io.Reader;
016: import java.io.Writer;
017:
018: import java.util.Enumeration;
019: import java.util.Hashtable;
020: import java.util.Properties;
021: import java.util.Vector;
022:
023: import java.net.URL;
024:
025: import org.w3c.util.IO;
026: import org.w3c.util.ObservableProperties;
027:
028: import org.w3c.tools.resources.Attribute;
029: import org.w3c.tools.resources.AttributeHolder;
030: import org.w3c.tools.resources.AttributeRegistry;
031: import org.w3c.tools.resources.BooleanAttribute;
032: import org.w3c.tools.resources.FileAttribute;
033: import org.w3c.tools.resources.FileResource;
034: import org.w3c.tools.resources.FramedResource;
035: import org.w3c.tools.resources.IntegerAttribute;
036: import org.w3c.tools.resources.InvalidResourceException;
037: import org.w3c.tools.resources.Resource;
038: import org.w3c.tools.resources.ResourceReference;
039: import org.w3c.tools.resources.ServerInterface;
040:
041: import org.w3c.tools.resources.serialization.Serializer;
042: import org.w3c.tools.resources.ProtocolException;
043:
044: import org.w3c.cvs.CVS;
045: import org.w3c.cvs.CvsDirectory;
046: import org.w3c.cvs.CvsException;
047: import org.w3c.cvs.UncheckedOutException;
048:
049: import org.w3c.jigsaw.auth.AuthFilter;
050:
051: import org.w3c.jigsaw.http.Request;
052:
053: import org.w3c.www.http.HttpRequestMessage;
054:
055: public class PutListResource extends FramedResource {
056:
057: protected static final boolean debug = true;
058:
059: /**
060: * status: File published
061: */
062: public static final int FILE_PB = 1;
063:
064: /**
065: * status: File unchanged
066: */
067: public static final int FILE_UC = 2;
068:
069: /**
070: * status: File merged
071: */
072: public static final int FILE_MG = 3;
073:
074: /**
075: * status: conflict
076: */
077: public static final int FILE_CF = 4;
078:
079: /**
080: * status: deleted
081: */
082: public static final int FILE_DEL = 5;
083:
084: /**
085: * Attribute index - The file used to store the modification list.
086: */
087: protected static int ATTR_FILE = -1;
088: /**
089: * Attribute index - The user's local space.
090: */
091: protected static int ATTR_SPACE = -1;
092: /**
093: * Attribute index - The web server public space.
094: */
095: protected static int ATTR_ROOT = -1;
096: /**
097: * Attribute index - The auto publish flag
098: */
099: protected static int ATTR_AUTO_PUBLISH = -1;
100: /**
101: * Attribute index - The auto delete flag
102: */
103: protected static int ATTR_AUTO_DELETE = -1;
104: /**
105: * Attribute index - The max number of published entries stored
106: */
107: protected static int ATTR_MAX_PUBLISHED = -1;
108:
109: static {
110: Class c = null;
111: Attribute a = null;
112:
113: try {
114: c = Class
115: .forName("org.w3c.jigedit.filters.PutListResource");
116: } catch (Exception ex) {
117: ex.printStackTrace();
118: System.exit(1);
119: }
120: // Register the file attribute:
121: a = new FileAttribute("file", null, Attribute.EDITABLE
122: | Attribute.MANDATORY);
123: ATTR_FILE = AttributeRegistry.registerAttribute(c, a);
124: // Register the space attribute:
125: a = new FileAttribute("space", null, Attribute.EDITABLE
126: | Attribute.MANDATORY);
127: ATTR_SPACE = AttributeRegistry.registerAttribute(c, a);
128: // Register the server root:
129: a = new FileAttribute("root", null, Attribute.EDITABLE
130: | Attribute.MANDATORY);
131: ATTR_ROOT = AttributeRegistry.registerAttribute(c, a);
132: // Register the auto publish flag
133: a = new BooleanAttribute("auto-publish", Boolean.FALSE,
134: Attribute.EDITABLE);
135: ATTR_AUTO_PUBLISH = AttributeRegistry.registerAttribute(c, a);
136: // Register the auto delete flag
137: a = new BooleanAttribute("auto-delete", Boolean.FALSE,
138: Attribute.EDITABLE);
139: ATTR_AUTO_DELETE = AttributeRegistry.registerAttribute(c, a);
140: // Register the max number of published entries stored
141: a = new IntegerAttribute("max-published", new Integer(10),
142: Attribute.EDITABLE);
143: ATTR_MAX_PUBLISHED = AttributeRegistry.registerAttribute(c, a);
144: }
145:
146: protected static Serializer serializer = null;
147:
148: static {
149: serializer = new org.w3c.tools.resources.serialization.xml.XMLSerializer();
150: }
151:
152: /**
153: * Get our auto publish flag.
154: * @return a boolean.
155: */
156: public boolean getAutoPublishFlag() {
157: return getBoolean(ATTR_AUTO_PUBLISH, false);
158: }
159:
160: /**
161: * Enable or disable the auto publish feature.
162: * @param onoff if onoff is true auto publish is enable.
163: */
164: protected void setAutoPublish(boolean onoff) {
165: setValue(ATTR_AUTO_PUBLISH, new Boolean(onoff));
166: }
167:
168: /**
169: * Get our auto delete flag.
170: * @return a boolean.
171: */
172: public boolean getAutoDeleteFlag() {
173: return getBoolean(ATTR_AUTO_DELETE, false);
174: }
175:
176: /**
177: * Enable or disable the auto delete feature.
178: * @param onoff if onoff is true auto publish is enable.
179: */
180: protected void setAutoDelete(boolean onoff) {
181: setValue(ATTR_AUTO_DELETE, new Boolean(onoff));
182: }
183:
184: /**
185: * Get the max number of published entries stored in the putlist.
186: * @return an int.
187: */
188: public int getMaxPublishedEntryStored() {
189: return getInt(ATTR_MAX_PUBLISHED, 10);
190: }
191:
192: /**
193: * Set the max number of published entries stored in the putlist.
194: * @param max This number.
195: */
196: protected void setMaxPublishedEntryStored(int max) {
197: setValue(ATTR_MAX_PUBLISHED, new Integer(max));
198: }
199:
200: /**
201: * Known entries.
202: */
203: private Hashtable entries = null;
204:
205: /**
206: * Known "todelete" entries.
207: */
208: private Hashtable dentries = null;
209:
210: /**
211: * Published
212: */
213: private Hashtable published = null;
214:
215: /**
216: * Our server context properties.
217: */
218: ObservableProperties props = null;
219:
220: /**
221: * Get the modified entries.
222: * @return an enumeration of PutedEntry
223: * @see PutedEntry
224: */
225: protected Enumeration getEntries() {
226: return entries.elements();
227: }
228:
229: /**
230: * Get the modified entries keys
231: * @return an enumeration of String
232: */
233: protected Enumeration getEntriesKeys() {
234: return entries.keys();
235: }
236:
237: /**
238: * Get the modified entry relative to the given key.
239: * @param key The key relative to the PutedEntry
240: * @return a PutedEntry
241: * @see PutedEntry
242: */
243: protected PutedEntry getEntry(String key) {
244: return (PutedEntry) entries.get(key);
245: }
246:
247: /**
248: * Add an entry into the putlist
249: * @param e the entry to add
250: */
251: protected void addEntry(PutedEntry e) {
252: entries.put(e.getKey(), e);
253: }
254:
255: /**
256: * Remove a modified entry from the putlist
257: * @param key the key of the entry to remove
258: */
259: protected void removeEntry(String key) {
260: entries.remove(key);
261: }
262:
263: /**
264: * Get the deleted entries.
265: * @return an enumeration of DeletedEntry
266: * @see DeletedEntry
267: */
268: protected Enumeration getDelEntries() {
269: return dentries.elements();
270: }
271:
272: /**
273: * Get the deleted entries keys
274: * @return an enumeration of String
275: */
276: protected Enumeration getDelEntriesKeys() {
277: return dentries.keys();
278: }
279:
280: /**
281: * Get the deleted entry relative to the given key.
282: * @param key The key relative to the DeletedEntry
283: * @return a DeletedEntry
284: * @see DeletedEntry
285: */
286: protected DeletedEntry getDelEntry(String key) {
287: return (DeletedEntry) dentries.get(key);
288: }
289:
290: /**
291: * Add an entry into the putlist
292: * @param e the entry to add
293: */
294: protected void addDelEntry(DeletedEntry e) {
295: dentries.put(e.getKey(), e);
296: }
297:
298: /**
299: * Remove a deleted entry from the putlist
300: * @param key the key of the entry to remove
301: */
302: protected void removeDelEntry(String key) {
303: dentries.remove(key);
304: }
305:
306: //---
307:
308: /**
309: * Get the published entries.
310: * @return an enumeration of PutedEntry
311: * @see PutedEntry
312: */
313: protected Enumeration getPublishedEntries() {
314: return published.elements();
315: }
316:
317: /**
318: * Remove the oldest published entry from the putlist.
319: */
320: protected void removeOldestPublishedEntry() {
321: //Could be optimized, but it is significant?
322: Enumeration penum = published.elements();
323: PutedEntry oldest = null;
324: PutedEntry current = null;
325:
326: if (!penum.hasMoreElements()) {
327: return;
328: }
329: oldest = (PutedEntry) penum.nextElement();
330: while (penum.hasMoreElements()) {
331: current = (PutedEntry) penum.nextElement();
332: if (current.getTime() < oldest.getTime())
333: oldest = current;
334: }
335: published.remove(oldest.getKey());
336: }
337:
338: /**
339: * Add a published entry into the putlist.
340: * @param e The published entry to add.
341: * @see PutedEntry
342: */
343: protected synchronized void addPubEntry(PutedEntry e) {
344: while (published.size() >= getMaxPublishedEntryStored())
345: removeOldestPublishedEntry();
346: published.put(e.getKey(), e);
347: }
348:
349: /**
350: * Remove a published entry from the putlist
351: * @param key the key of the entry to remove
352: */
353: protected synchronized void removePubEntry(String key) {
354: published.remove(key);
355: }
356:
357: /**
358: * Compute the path of the public file for the given local file.
359: * This method uses the <em>space</em> and <em>root</em> attributes
360: * to translate the path of the given file from the user's local space
361: * to the public (server) space.
362: * @return A File instance, or <strong>null</strong>.
363: */
364: protected File getServerFile(File file) {
365: String fpath = file.getAbsolutePath();
366: String fspace = getCvsSpace().getAbsolutePath();
367: if (!fpath.startsWith(fspace))
368: return null;
369: return new File(getRoot(), fpath.substring(fspace.length()));
370: }
371:
372: /**
373: * Get the file to use to store the edited list of files.
374: * @return The file.
375: */
376: public File getFile() {
377: return (File) getValue(ATTR_FILE, null);
378: }
379:
380: /**
381: * Get the file to use to store the edited list of published files.
382: * @return The file.
383: */
384: public File getPubFile() {
385: File file = getFile();
386: if (file != null)
387: return new File(file + ".pub");
388: else
389: return null;
390: }
391:
392: /**
393: * Get the file to use to store the edited list of deleted files.
394: * @return The file.
395: */
396: public File getDelFile() {
397: File file = getFile();
398: if (file != null)
399: return new File(file + ".del");
400: else
401: return null;
402: }
403:
404: /**
405: * Get the root directory of the public server to update.
406: * @return The root directory of the public server space, supposed to
407: * be controled by CVS.
408: */
409: public File getRoot() {
410: return (File) getValue(ATTR_ROOT, null);
411: }
412:
413: /**
414: * Get this user's local CVS space root directory.
415: * @return The usre's root of the CVS local space, assumed to be
416: * under CVS control.
417: */
418: public File getCvsSpace() {
419: return (File) getValue(ATTR_SPACE, null);
420: }
421:
422: protected synchronized void write(File file, Enumeration genum) {
423: if (file == null)
424: return;
425: File backup = null;
426: // Save old version if available:
427: if (file.exists()) {
428: backup = new File(file + ".bak");
429: if (backup.exists())
430: backup.delete();
431: file.renameTo(backup);
432: }
433: try {
434: Vector v = new Vector(10);
435: while (genum.hasMoreElements()) {
436: v.addElement(genum.nextElement());
437: }
438: AttributeHolder holders[] = new AttributeHolder[v.size()];
439: v.copyInto(holders);
440: Writer writer = new BufferedWriter(new FileWriter(file));
441: serializer.writeResources(holders, writer);
442: } catch (Exception ex) {
443: ex.printStackTrace();
444: if (backup != null)
445: backup.renameTo(file);
446: }
447: }
448:
449: /**
450: * Dump the current list of edited files back to disk.
451: */
452: protected synchronized void writeList() {
453: write(getFile(), entries.elements());
454: }
455:
456: /**
457: * Dump the current list of published files back to disk.
458: */
459: protected synchronized void writePubList() {
460: write(getPubFile(), published.elements());
461: }
462:
463: /**
464: * Dump the current list of [ublished files back to disk.
465: */
466: protected synchronized void writeDelList() {
467: write(getDelFile(), getDelEntries());
468: }
469:
470: protected synchronized void removeUnconfirmedDelEntries() {
471: Enumeration denum = getDelEntries();
472: while (denum.hasMoreElements()) {
473: DeletedEntry e = (DeletedEntry) denum.nextElement();
474: if (!e.isConfirmed()) {
475: removeDelEntry(e.getKey());
476: }
477: }
478: }
479:
480: /**
481: * Restore the list from the file.
482: */
483: protected synchronized void readList() {
484: File file = getFile();
485: try {
486: Reader reader = new BufferedReader(new FileReader(file));
487: AttributeHolder holders[] = serializer
488: .readAttributeHolders(reader);
489: for (int i = 0; i < holders.length; i++)
490: addEntry((PutedEntry) holders[i]);
491: } catch (Exception ex) {
492: // FIXME
493: ex.printStackTrace();
494: }
495: }
496:
497: /**
498: * Restore the published list from the file.
499: */
500: protected synchronized void readPubList() {
501: File file = getPubFile();
502: try {
503: Reader reader = new BufferedReader(new FileReader(file));
504: AttributeHolder holders[] = serializer
505: .readAttributeHolders(reader);
506: for (int i = 0; i < holders.length; i++)
507: addPubEntry((PutedEntry) holders[i]);
508: } catch (Exception ex) {
509: // FIXME
510: ex.printStackTrace();
511: }
512: }
513:
514: /**
515: * Restore the deleted list from the file.
516: */
517: protected synchronized void readDelList() {
518: File file = getDelFile();
519: try {
520: Reader reader = new BufferedReader(new FileReader(file));
521: AttributeHolder holders[] = serializer
522: .readAttributeHolders(reader);
523: for (int i = 0; i < holders.length; i++) {
524: DeletedEntry e = (DeletedEntry) holders[i];
525: e.confirm();
526: addDelEntry(e);
527: }
528: } catch (Exception ex) {
529: // FIXME
530: ex.printStackTrace();
531: }
532: }
533:
534: protected PutedEntry lookupEntry(Request request) {
535: ResourceReference rr = request.getTargetResource();
536: String k = request.getURL().toExternalForm();
537: Resource r = null;
538: if (rr != null) {
539: try {
540: r = rr.lock();
541: if (r instanceof FileResource)
542: k = ((FileResource) r).getFile().getAbsolutePath()
543: .toString();
544: } catch (InvalidResourceException ex) {
545: // continue
546: } finally {
547: rr.unlock();
548: }
549: }
550: return (PutedEntry) entries.get(k);
551: }
552:
553: protected DeletedEntry lookupDelEntry(Request request) {
554: ResourceReference rr = request.getTargetResource();
555: String k = request.getURL().toExternalForm();
556: Resource r = null;
557: if (rr != null) {
558: try {
559: r = rr.lock();
560: if (r instanceof FileResource)
561: k = ((FileResource) r).getFile().getAbsolutePath()
562: .toString();
563: } catch (InvalidResourceException ex) {
564: // continue
565: } finally {
566: rr.unlock();
567: }
568: }
569: return (DeletedEntry) dentries.get(k);
570: }
571:
572: /**
573: * Register the given request, which must has a PUT method.
574: * @param file The modified file.
575: */
576: public synchronized int registerRequest(Request request) {
577: PutedEntry e = lookupEntry(request);
578: if (e == null) {
579: e = PutedEntry.makeEntry(request);
580: addEntry(e);
581: } else {
582: e.update(request);
583: }
584: if (getAutoPublishFlag()) {
585: return publish(e);
586: } else {
587: return FILE_UC;
588: }
589: }
590:
591: public synchronized void registerDeleteRequest(Request request) {
592: DeletedEntry e = (DeletedEntry) DeletedEntry.makeEntry(request);
593: addDelEntry(e);
594: }
595:
596: public synchronized int confirmDelete(Request request) {
597: DeletedEntry e = lookupDelEntry(request);
598: if (e != null) {
599: e.confirm();
600: if (getAutoDeleteFlag())
601: return delete(e);
602: }
603: return FILE_UC;
604: }
605:
606: /**
607: * Delete the file relative to the given entry.
608: * @param entry The DeletedEntry.
609: * @return FILE_UC, FILE_DEL
610: */
611: protected int delete(DeletedEntry de) {
612: File file = new File(de.getFilename());
613: File sfile = getServerFile(file);
614: if (sfile.exists()) {
615: if (debug)
616: System.out.println("Deleting : " + sfile);
617: sfile.delete();
618: removeDelEntry(de.getKey());
619: return FILE_DEL;
620: } else {
621: if (debug)
622: System.out.println("Nothing to delete : " + sfile);
623: removeDelEntry(de.getKey());
624: return FILE_UC;
625: }
626: }
627:
628: /**
629: * Publish the file relative to the given entry.
630: * @param entry The PutedEntry.
631: * @return FILE_UC, FILE_CF, FILE_PB, FILE_MG
632: */
633: protected int publish(PutedEntry pe) {
634: File file = new File(pe.getFilename());
635: File sfile = getServerFile(file);
636: int status = FILE_UC;
637:
638: try {
639: // First step: does the private version needs commit ?
640: File d = new File(file.getParent());
641: CvsDirectory c = CvsDirectory.getManager(d, props);
642: if (c.status(file.getName()) == CVS.FILE_M) {
643: String author = pe.getAuthor();
644: String env[] = { "USER=" + author, "LOGNAME=" + author };
645: String msg = ((author != null) ? "Published by "
646: + author + " through Jigsaw"
647: : "Published through Jigsaw");
648: c.commit(file.getName(), msg, env);
649: } else if (debug) {
650: System.out.println("PutList: no commit needed on "
651: + file.getAbsolutePath() + " st="
652: + c.status(file.getName()));
653: }
654: // Second step: publish
655: File sd = new File(sfile.getParent());
656: try {
657: CvsDirectory sc = CvsDirectory.getManager(sd, props);
658: String filename = sfile.getName();
659: int cvs_status = sc.status(filename);
660: if (debug) {
661: System.out.println("publishing "
662: + CvsDirectory.statusToString(cvs_status)
663: + " file : " + filename);
664: }
665: if (cvs_status == CVS.FILE_C) {
666: //conflict! we try to merge
667: //create a backup file
668: File backup = new File(sfile.getParent(), filename
669: + ".bak");
670: try {
671: org.w3c.util.IO.copy(sfile, backup);
672: //try to merge
673: sc.update(filename);
674: cvs_status = sc.status(filename);
675: if (cvs_status == CVS.FILE_M) {
676: //merge done, so commit.
677: String author = pe.getAuthor();
678: String env[] = { "USER=" + author,
679: "LOGNAME=" + author };
680: String msg = ((author != null) ? "Merged by "
681: + author + " through Jigsaw"
682: : "Merged through Jigsaw");
683: sc.commit(filename, msg, env);
684: //done so delete backup file
685: backup.delete();
686: status = FILE_MG;
687: } else if (cvs_status == CVS.FILE_C) {
688: //merge failed
689: sfile.delete();
690: backup.renameTo(sfile);
691: status = FILE_CF;
692: }
693: } catch (IOException ex) {
694: ex.printStackTrace();
695: status = FILE_CF;
696: }
697: } else if (cvs_status != CVS.FILE_OK) {
698: sc.update(filename);
699: status = FILE_PB;
700: } else if (debug) {
701: System.out.println("PutList: no update needed on "
702: + sfile.getAbsolutePath() + " st="
703: + CvsDirectory.statusToString(cvs_status));
704: }
705: } catch (UncheckedOutException ex) {
706: // perform a get from root
707: File root = new File(getRoot().getAbsolutePath());
708: CvsDirectory sc = CvsDirectory.getManager(root, props);
709: String fpath = file.getAbsolutePath();
710: String fspace = getCvsSpace().getAbsolutePath();
711: String path = fpath.substring(fspace.length() + 1);
712: sc.get(path);
713: status = FILE_PB;
714: }
715: // Last step: remove published entries:
716: entries.remove(pe.getKey());
717: // publication time
718: pe.setValue(PutedEntry.ATTR_TIME, new Long(System
719: .currentTimeMillis()));
720: addPubEntry(pe);
721: } catch (CvsException ex) {
722: ex.printStackTrace();
723: }
724: return status;
725: }
726:
727: public synchronized void notifyUnload() {
728: writeList();
729: writePubList();
730: writeDelList();
731: super .notifyUnload();
732: }
733:
734: public void initialize(Object values[]) {
735: super .initialize(values);
736: // Prepare empty entry list:
737: File file = getFile();
738: if ((file != null) && file.exists())
739: readList();
740: File pub = getPubFile();
741: if ((pub != null) && pub.exists())
742: readPubList();
743: File del = getDelFile();
744: if ((del != null) && del.exists())
745: readDelList();
746: // Get the server properties:
747: this .props = getServer().getProperties();
748: try {
749: registerFrameIfNone("org.w3c.jigedit.filters.PutListFrame",
750: "putlist-frame");
751: } catch (Exception ex) {
752: ex.printStackTrace();
753: }
754: }
755:
756: public PutListResource() {
757: super ();
758: this .entries = new Hashtable(11);
759: this .dentries = new Hashtable(11);
760: this .published = new Hashtable(11);
761: }
762:
763: }
|