001: // CvsFileFrame.java
002: // $Id: CvsFileFrame.java,v 1.27 2003/03/27 16:37:07 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.frames;
007:
008: import java.io.File;
009: import java.io.IOException;
010: import java.io.PrintStream;
011:
012: import org.w3c.tools.resources.Attribute;
013: import org.w3c.tools.resources.AttributeHolder;
014: import org.w3c.tools.resources.AttributeRegistry;
015: import org.w3c.tools.resources.BooleanAttribute;
016: import org.w3c.tools.resources.DirectoryResource;
017: import org.w3c.tools.resources.FileResource;
018: import org.w3c.tools.resources.FramedResource;
019: import org.w3c.tools.resources.InvalidResourceException;
020: import org.w3c.tools.resources.MultipleLockException;
021: import org.w3c.tools.resources.ProtocolException;
022: import org.w3c.tools.resources.ReplyInterface;
023: import org.w3c.tools.resources.RequestInterface;
024: import org.w3c.tools.resources.Resource;
025: import org.w3c.tools.resources.ResourceException;
026: import org.w3c.tools.resources.ResourceFrame;
027: import org.w3c.tools.resources.ResourceReference;
028: import org.w3c.tools.resources.ServerInterface;
029:
030: import org.w3c.jigsaw.frames.HTTPFrame;
031:
032: import org.w3c.jigsaw.http.HTTPException;
033: import org.w3c.jigsaw.http.Reply;
034: import org.w3c.jigsaw.http.Request;
035:
036: import org.w3c.jigsaw.auth.AuthFilter;
037:
038: import org.w3c.www.http.HTTP;
039: import org.w3c.www.http.HttpEntityTag;
040: import org.w3c.www.http.HttpEntityMessage;
041: import org.w3c.www.http.HttpMessage;
042: import org.w3c.www.http.HttpReplyMessage;
043: import org.w3c.www.http.HttpRequestMessage;
044:
045: import org.w3c.www.mime.MimeType;
046:
047: import org.w3c.jigsaw.html.HtmlGenerator;
048:
049: import org.w3c.cvs.CVS;
050: import org.w3c.cvs.CvsAddException;
051: import org.w3c.cvs.CvsDirectory;
052: import org.w3c.cvs.CvsException;
053: import org.w3c.cvs.UpToDateCheckFailedException;
054:
055: import org.w3c.jigedit.cvs.CvsFrame;
056: import org.w3c.jigedit.cvs.CvsModule;
057:
058: /**
059: * This subclass of HTTPFrame check cvs before performing a PUT request.
060: * If a CVS directory exists<BR>
061: * <ul>
062: * If the resource file exists<BR>
063: * <ul>
064: * If resource file not up to date Fail.<BR>
065: * Else perform PUT and commit it into cvs.<BR>
066: * </ul>
067: * Else perform PUT, add and commit it into cvs.<BR>
068: * </ul>
069: * Else perform PUT.
070: * @author Benoit Mahe <bmahe@sophia.inria.fr>
071: */
072:
073: public class CvsFileFrame extends HTTPFrame {
074:
075: public static final boolean debug = true;
076:
077: /**
078: * Attribute index, tell if we must update the resource everytime it is
079: * acceded (not recommended as it generates many cvs commands)
080: */
081: private static int ATTR_AUTOUPDATE = -1;
082:
083: static {
084: Attribute a = null;
085: Class cls = null;
086:
087: try {
088: cls = Class.forName("org.w3c.jigedit.frames.CvsFileFrame");
089: } catch (Exception ex) {
090: ex.printStackTrace();
091: System.exit(1);
092: }
093: // The browsable flag:
094: a = new BooleanAttribute("autoupdate", Boolean.FALSE,
095: Attribute.EDITABLE);
096: ATTR_AUTOUPDATE = AttributeRegistry.registerAttribute(cls, a);
097: }
098:
099: protected static Reply error(Request request, int status,
100: String title, String msg) {
101: Reply error = request.makeReply(status);
102: HtmlGenerator g = CvsFrame.getHtmlGenerator(title);
103: g.append("<span class=\"title\">", title, "</span>\n");
104: g.append("<p>", msg);
105: error.setStream(g);
106: return error;
107: }
108:
109: /**
110: * tell if we must always do an update.
111: */
112: public boolean isAutoUpdatable() {
113: return getBoolean(ATTR_AUTOUPDATE, false);
114: }
115:
116: protected File resDirectory = null;
117:
118: protected synchronized File getResourceDirectory() {
119: if (resDirectory == null) {
120: ResourceReference rr = getFileResource().getParent();
121: ResourceReference rrtemp = null;
122: Resource p = null;
123: while (true) {
124: try {
125: if (rr == null)
126: return null;
127: p = rr.lock();
128: if (p instanceof DirectoryResource) {
129: resDirectory = ((DirectoryResource) p)
130: .getDirectory();
131: break;
132: }
133: rrtemp = p.getParent();
134: } catch (InvalidResourceException ex) {
135: return null;
136: } finally {
137: if (rr != null)
138: rr.unlock();
139: }
140: rr = rrtemp;
141: }
142: }
143: return resDirectory;
144: }
145:
146: protected synchronized CvsDirectory getCvsManager()
147: throws CvsException {
148: return CvsModule.getCvsManager(getResourceDirectory(),
149: getContext(), getServer().getProperties());
150: }
151:
152: protected boolean checkCvsManager() {
153: try {
154: return (getCvsManager() != null);
155: } catch (CvsException ex) {
156: return false;
157: }
158: }
159:
160: /**
161: * Get this resource Etag string, it will be computed using
162: * FileETag MTime Size directive like in Apache, this will fit some
163: * needs for our own server farm, it won't hurt anyway/
164: * @return a string or null if not applicable
165: */
166: public String computeETag() {
167: String etag_s = null;
168: if (fresource != null) {
169: long lstamp = fresource.getFileStamp();
170: long lsize = (long) fresource.getFileLength();
171: if (lstamp >= 0L) {
172: etag_s = Long.toHexString(lsize) + "-"
173: + Long.toHexString(lstamp);
174: }
175: }
176: return etag_s;
177: }
178:
179: /**
180: * @exception CvsException if the CVS process failed
181: */
182: protected void add(Request request) throws CvsException {
183: String u = (String) request.getState(AuthFilter.STATE_AUTHUSER);
184: String env[] = { "USER=" + u, "LOGNAME=" + u };
185: String names[] = null;
186: MimeType mtype;
187: mtype = request.getContentType();
188: if (mtype != null
189: && (mtype.match(MimeType.TEXT) != MimeType.MATCH_SUBTYPE)
190: && (mtype.match(MimeType.APPLICATION_XHTML_XML) != MimeType.MATCH_SPECIFIC_SUBTYPE)) {
191: names = new String[2];
192: names[0] = "-kb";
193: names[1] = getFileResource().getFile().getName();
194: } else {
195: names = new String[1];
196: names[0] = getFileResource().getFile().getName();
197: }
198: CvsDirectory cvsdir = null;
199: cvsdir = getCvsManager();
200: cvsdir.add(names, env);
201: }
202:
203: /**
204: * @exception CvsException if the CVS process failed
205: */
206: protected void commit(Request request) throws CvsException {
207: commit(request, "Changed through Jigsaw.");
208: }
209:
210: /**
211: * @exception CvsException if the CVS process failed
212: */
213: protected void commit(Request request, String msg)
214: throws CvsException {
215: String u = (String) request.getState(AuthFilter.STATE_AUTHUSER);
216: String env[] = { "USER=" + u, "LOGNAME=" + u };
217: String comment = "(" + u + ") " + msg;
218: CvsDirectory cvsdir = null;
219: cvsdir = getCvsManager();
220: cvsdir.commit(getFileResource().getFile().getName(), comment,
221: env);
222: }
223:
224: /**
225: * @exception CvsException if the CVS process failed
226: */
227: protected void update() throws CvsException {
228: CvsDirectory cvsdir = getCvsManager();
229: cvsdir.update(getFileResource().getFile().getName());
230: }
231:
232: /**
233: * @exception CvsException if the CVS process failed
234: */
235: protected int status() throws CvsException {
236: CvsDirectory cvsdir = getCvsManager();
237: return cvsdir.status(getFileResource().getFile().getName());
238: }
239:
240: protected String statusToString(int status) {
241: return CvsDirectory.statusToString(status);
242: }
243:
244: protected File getBackupFile() {
245: File file = getFileResource().getFile();
246: return new File(file.getParent(), file.getName() + ".bak");
247: }
248:
249: /**
250: * Change the content of the associated FileResource.
251: * +cvs action (commit, update)
252: * @param request The incomming request.
253: * @exception org.w3c.tools.resources.ProtocolException
254: * if a protocol error occurs
255: * @exception org.w3c.tools.resources.ResourceException
256: * if a server error occurs
257: */
258: protected Reply putFileResource(Request request)
259: throws ProtocolException, ResourceException {
260: if (debug)
261: System.out.println("Put on "
262: + getResource().getIdentifier() + "...");
263: if (getResourceDirectory() != null) {
264: if (checkCvsManager()) {
265: File resfile = getFileResource().getFile();
266: int cvs_status = -1;
267: try {
268: cvs_status = status();
269: } catch (CvsException ex) {
270: //file not on disk yet, let's try to add this file..
271: cvs_status = CVS.FILE_Q;
272: }
273: if (debug)
274: System.out
275: .println("... with a cvs status equals to : "
276: + statusToString(cvs_status));
277: if (cvs_status == CVS.FILE_C) {
278: String msg = "Conflict between working "
279: + "revision and repository revision.<br> "
280: + "Please update the file manually or use the CVS form.";
281: return error(request, HTTP.CONFLICT, "Conflict",
282: msg);
283: } else if (cvs_status == CVS.FILE_NCO) {
284: String msg = "File already in the repository "
285: + "(added independently by second party).<br>"
286: + "Please update the file manually or use the CVS form.";
287: return error(request, HTTP.CONFLICT, "Conflict",
288: msg);
289: } else if (resfile.exists()
290: && (cvs_status != CVS.FILE_Q)) {
291: File backup = getBackupFile();
292: try {
293: if (cvs_status == CVS.FILE_OK) {
294: // Write the file to web space
295: //save the current file
296: try {
297: org.w3c.util.IO.copy(resfile, backup);
298: } catch (IOException ex) {
299: //not exactly what we want but, it ~works
300: backup.delete();
301: resfile.renameTo(backup);
302: }
303: //perform the put
304: Reply r = super .putFileResource(request);
305: int rstatus = r.getStatus();
306: // if OK
307: if ((rstatus / 100) == 2) {
308: // Do a CVS commit with user name
309: commit(request);
310: }
311: //well done, remove the backup file now
312: backup.delete();
313: // update the file attrs
314: getFileResource().checkContent();
315: //return the reply.
316: if ((rstatus / 100) == 2) {
317: return createDefaultReply(request, r
318: .getStatus());
319: } else {
320: return r;
321: }
322: } else {
323: // fail
324: String msg = "File is not up to date, "
325: + "please update the file manually "
326: + "or use the CVS form.";
327: return error(request, HTTP.CONFLICT,
328: "Error", msg);
329: }
330: } catch (UpToDateCheckFailedException utd_ex) {
331: resfile.delete();
332: //restore the backup file
333: backup.renameTo(resfile);
334: //send error
335: String msg = utd_ex.getFilename()
336: + " is not up to date, "
337: + "please update the file manually "
338: + "or use the CVS form.";
339: return error(request, HTTP.CONFLICT, "Error",
340: msg);
341: } catch (CvsException ex) {
342: backup.delete();
343: // fail too
344: String msg = "CvsException : "
345: + ex.getMessage();
346: getServer().errlog(
347: getIdentifier() + " : "
348: + ex.getMessage());
349: return error(request, HTTP.SERVICE_UNAVAILABLE,
350: "Error", msg);
351: }
352: } else {
353: // Write the file to web space
354: Reply r = super .putFileResource(request);
355: try {
356: // Do a CVS add with user name
357: add(request);
358: // Do a CVS commit with user name
359: commit(request);
360: getFileResource().checkContent();
361: } catch (CvsAddException ex) {
362: String msg = "Cvs add failed : <br>'"
363: + ex.getMessage()
364: + "'<br>please update the file manually "
365: + "or use the CVS form";
366: return error(request, HTTP.SERVICE_UNAVAILABLE,
367: "Error", msg);
368: } catch (CvsException ex) {
369: ex.printStackTrace();
370: String msg = "Problem during cvs process : "
371: + ex.getMessage();
372: getServer().errlog(
373: getIdentifier() + " : " + msg);
374: return error(request,
375: HTTP.INTERNAL_SERVER_ERROR,
376: "Internal Server Error", msg);
377: }
378: if (r.getStatus() == HTTP.CREATED) {
379: Reply reply = request.makeReply(r.getStatus());
380: reply
381: .setContent("<P>Resource succesfully created");
382: if (request.hasState(STATE_CONTENT_LOCATION))
383: reply.setContentLocation(getURL(request)
384: .toExternalForm());
385: r = createDefaultReply(request, HTTP.CREATED);
386: reply.setETag(r.getETag());
387: return reply;
388: }
389: return createDefaultReply(request, r.getStatus());
390: }
391: } else {
392: return super .putFileResource(request);
393: }
394: } else {
395: String msg = "Server misconfigured : "
396: + "unable to find resource directory";
397: getServer().errlog(getIdentifier() + " : " + msg);
398: return error(request, HTTP.INTERNAL_SERVER_ERROR,
399: "Internal Server Error", msg);
400: }
401: }
402:
403: /**
404: * The DELETE method delete the file and perform a cvs remove.
405: * @param request The request to handle.
406: * @exception ProtocolException if a protocol error occurs
407: * @exception ResourceException If the resource got a fatal error.
408: */
409:
410: protected Reply deleteFileResource(Request request)
411: throws ProtocolException, ResourceException {
412: //Get the user name
413: String u = (String) request.getState(AuthFilter.STATE_AUTHUSER);
414: String env[] = { "USER=" + u, "LOGNAME=" + u };
415: //delete the file
416: fresource.getFile().delete();
417: //cvs remove
418: String names[] = new String[1];
419:
420: names[0] = fresource.getFilename();
421: if (names[0] == null)
422: names[0] = fresource.getIdentifier();
423: try {
424: if (getCvsManager().status(names[0]) != CVS.FILE_Q) {
425: getCvsManager().remove(names,
426: "Deleted via HTTP delete method.", env);
427: }
428: } catch (CvsException cvs_ex) {
429: String msg = "Cvs remove failed : " + cvs_ex.getMessage();
430: getServer().errlog(this , msg);
431: }
432: try {
433: //delete the FileResource
434: fresource.delete();
435: } catch (MultipleLockException ex) {
436: String msg = "Can't delete resource: "
437: + resource.getIdentifier()
438: + " is locked. Try again later.";
439: Reply error = error(request, HTTP.FORBIDDEN, "Error", msg);
440: throw new HTTPException(error);
441: }
442: //well done
443: return request.makeReply(HTTP.NO_CONTENT);
444: }
445:
446: /**
447: * Perform a cvs update before perform a GET,HEAD,POST request.
448: * @param req The request to handle.
449: * @exception ProtocolException If processsing the request failed.
450: * @exception ResourceException If the resource got a fatal error.
451: */
452:
453: public ReplyInterface perform(RequestInterface req)
454: throws ProtocolException, ResourceException {
455: if (!checkRequest(req))
456: return null;
457:
458: String method = ((Request) req).getMethod();
459: if ((method.equals("GET") || method.equals("HEAD")
460: || method.equals("POST") || method.equals("PUT"))
461: && isAutoUpdatable() && (fresource != null)) {
462:
463: try {
464: update();
465: } catch (CvsException ex) {
466: String msg = "cvs update \""
467: + getFileResource().getFile().getName()
468: + "\" failed.";
469: getServer().errlog(this, msg);
470: }
471: }
472: return super.perform(req);
473: }
474: }
|