0001:/*
0002: * Copyright (c) 2000, Jacob Smullyan.
0003: *
0004: * This is part of SkunkDAV, a WebDAV client. See http://skunkdav.sourceforge.net/
0005: * for the latest version.
0006: *
0007: * SkunkDAV is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License as published
0009: * by the Free Software Foundation; either version 2, or (at your option)
0010: * any later version.
0011: *
0012: * SkunkDAV is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with SkunkDAV; see the file COPYING. If not, write to the Free
0019: * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
0020: * 02111-1307, USA.
0021:*/
0022:
0023:package org.skunk.dav.client.gui;
0024:
0025:import java.io.IOException;
0026:import java.util.Iterator;
0027:import java.util.Map;
0028:import java.util.StringTokenizer;
0029:import javax.swing.JOptionPane;
0030:import javax.swing.SwingUtilities;
0031:import javax.swing.tree.DefaultMutableTreeNode;
0032:import javax.swing.tree.DefaultTreeModel;
0033:import javax.swing.tree.MutableTreeNode;
0034:import javax.swing.tree.TreeNode;
0035:import javax.swing.tree.TreePath;
0036:import org.skunk.assert.Assertion;
0037:import org.skunk.dav.client.DAVAuthenticator;
0038:import org.skunk.dav.client.DAVConnection;
0039:import org.skunk.dav.client.DAVConnectionPool;
0040:import org.skunk.dav.client.DAVConstants;
0041:import org.skunk.dav.client.DAVException;
0042:import org.skunk.dav.client.DAVFile;
0043:import org.skunk.dav.client.DAVProperty;
0044:import org.skunk.dav.client.Depth;
0045:import org.skunk.dav.client.method.CopyMethod;
0046:import org.skunk.dav.client.method.DeleteMethod;
0047:import org.skunk.dav.client.method.GetMethod;
0048:import org.skunk.dav.client.method.LockMethod;
0049:import org.skunk.dav.client.method.MkcolMethod;
0050:import org.skunk.dav.client.method.MoveMethod;
0051:import org.skunk.dav.client.method.PropFindMethod;
0052:import org.skunk.dav.client.method.PropFindQueryType;
0053:import org.skunk.dav.client.method.PropPatchMethod;
0054:import org.skunk.dav.client.method.PutMethod;
0055:import org.skunk.dav.client.method.UnlockMethod;
0056:import org.skunk.trace.Debug;
0057:
0058:public class DAVTreeModel extends DefaultTreeModel
0059:{
0060:
0061: private static boolean DO_ALLPROP;
0062: static
0063: {
0064: /*
0065: * allProp is an experimental property, for testing.
0066: * setting it to false may cause tree model queries to be more efficient,
0067: * and enables the app to work with some recalcitrant dav server implementations
0068: * that don't like allprop. However, at present it breaks some other features.
0069: */
0070: String doAllprop=System.getProperty("doAllprop");
0071: if (doAllprop!=null && doAllprop.equalsIgnoreCase("false"))
0072: DO_ALLPROP=false;
0073: else DO_ALLPROP=true;
0074: }
0075:
0076: private boolean showNonCollections;
0077:
0078: public static final boolean usesAllprop()
0079: {
0080: return DAVTreeModel.DO_ALLPROP;
0081: }
0082:
0083:
0084: public DAVTreeModel(DefaultMutableTreeNode rootNode)
0085: {
0086: this (rootNode, false);
0087: }
0088:
0089: public DAVTreeModel(DefaultMutableTreeNode rootNode, boolean showNonCollections)
0090: {
0091: super (rootNode);
0092: this .showNonCollections=showNonCollections;
0093:
0094: Debug.trace(this , Debug.DP3, "in constructor: rootNode {0}, show non-collections: {1}",
0095: new Object[] { rootNode, new Boolean(showNonCollections)});
0096: }
0097:
0098: public boolean getShowNonCollections()
0099: {
0100: return this .showNonCollections;
0101: }
0102:
0103: public boolean isLeaf(Object node)
0104: {
0105: if (node instanceof DAVTreeNode)
0106: {
0107: return ((DAVTreeNode)node).isLeaf();
0108: }
0109: else if (node.equals(getRoot()))
0110: return false;
0111: else
0112: {
0113: return super .isLeaf(node);
0114: }
0115: }
0116:
0117: public Object getChild(Object parent, int index)
0118: {
0119: testNode(parent);
0120: return super .getChild(parent, index);
0121: }
0122:
0123: public int getChildCount(Object parent)
0124: {
0125: testNode(parent);
0126: return super .getChildCount(parent);
0127: }
0128:
0129: public int getIndexOfChild(Object parent, Object child)
0130: {
0131: Debug.trace(this , Debug.DP6,
0132: "getting index of child for parent {0} and child {1}",
0133: new Object[] {parent, child} );
0134: testNode(parent);
0135: return super .getIndexOfChild(parent, child);
0136: }
0137:
0138: /**
0139: * if the node has not been refreshed and is a collection, refresh it
0140: */
0141: public void testNode(Object node)
0142: {
0143: if (node instanceof DAVTreeNode)
0144: {
0145: DAVTreeNode davnode=(DAVTreeNode) node;
0146: if (!(davnode.isRefreshed() || davnode.isLeaf()))
0147: {
0148: refreshNode(davnode);
0149: }
0150: }
0151: }
0152:
0153: private void reloadNode(final DAVTreeNode node)
0154: {
0155: BusinessTracker.addBusy("reload");
0156: Runnable r=new Runnable() {
0157: public void run()
0158: {
0159: Debug.trace(this , Debug.DP3, "refreshing {0}",
0160: new Object[] {node});
0161: refreshNode(node); //why am I doing this in the event dispatch thread?
0162: reload(node);
0163: BusinessTracker.removeBusy("reload");
0164: }
0165: };
0166: if (SwingUtilities.isEventDispatchThread())
0167: r.run();
0168: else SwingUtilities.invokeLater(r);
0169: }
0170:
0171: public void refreshNode(DAVTreeNode node)
0172: {
0173: refreshNode(node, DO_ALLPROP);
0174: }
0175:
0176: public void refreshNode(DAVTreeNode node, boolean doAllprop)
0177: {
0178: BusinessTracker.addBusy("refresh");
0179: synchronized (node)
0180: {
0181: String resource=node.getDAVFile().getName();
0182: PropFindMethod method=(doAllprop)
0183: ? new PropFindMethod(resource)
0184: : new PropFindMethod(resource, Depth.ONE, DAVProperty.BASIC_PROPERTIES);
0185:
0186: DAVFile newFile=null;
0187: try
0188: {
0189: DAVConnection conn=node.getDAVFile().getDAVConnection();
0190: conn.execute(method);
0191: newFile=method.getDAVFile();
0192: }
0193: catch (DAVException davie)
0194: {
0195: Debug.trace(this , Debug.DP2, davie);
0196: }
0197: catch (IOException oyVeh)
0198: {
0199: Debug.trace(this , Debug.DP2, oyVeh);
0200: }
0201: if (newFile!=null)
0202: {
0203: Debug.trace(this , Debug.DP3, "refreshed file:\n"+newFile.toVerboseString());
0204: node.setDAVFile(newFile);
0205: node.setRefreshed(true);
0206: StateMonitor.setProperty(StateProperties.RELOAD,
0207: this , //reference to DAVTreeModel is application value
0208: newFile.getFullName()); //full path of resource is domain key
0209: }
0210: else
0211: {
0212: Debug.trace(this , Debug.DP2, "refresh on {0} failed", new Object[] {node});
0213: }
0214: BusinessTracker.removeBusy("refresh");
0215: }
0216: }
0217:
0218: private void addConnection(DAVTreeNode node)
0219: {
0220: BusinessTracker.addBusy("addConn");
0221: Object root=getRoot();
0222: if (root instanceof DefaultMutableTreeNode)
0223: {
0224: ((DefaultMutableTreeNode) root).add(node);
0225: reload();
0226: }
0227: else
0228: {
0229: Debug.trace(this , Debug.DP2,
0230: "Programming Error -- root node not a DefaultMutableTreeNode");
0231: }
0232: BusinessTracker.removeBusy("addConn");
0233: }
0234:
0235: public DAVTreeNode addConnectionNode(ServerData sd, boolean waitForIt, Runnable postRunner)
0236: {
0237: BusinessTracker.addBusy("addNode");
0238: String host=sd.getHost();
0239: int port=sd.getPort();
0240: String initialPath=sd.getInitialPath();
0241: Object nodeObj=getConnectionNode(host, port);
0242: if (nodeObj!=null && nodeObj instanceof DAVTreeNode)
0243: {
0244: DAVTreeNode possibleNode=(DAVTreeNode) nodeObj;
0245: String nodePath=possibleNode.getDAVFile().getName();
0246: Debug.trace(this , Debug.DP3, "possible node found, path is {0}",
0247: new Object[] {nodePath});
0248: /*
0249: four possibilities for the new initial path:
0250: 1. the same initial path. Do not connect.
0251: 2. a subpath. Do not connect.
0252: 3. a superpath. remove possibleNode and connect.
0253: 4. a parallel path. Connect, leave possibleNode alone.
0254: */
0255: if (initialPath.startsWith(nodePath)) //cases 1 and 2
0256: {
0257: if (!initialPath.equals(nodePath))
0258: {
0259: String fullPath=new StringBuffer(host)
0260: .append(':')
0261: .append(port)
0262: .append(initialPath)
0263: .toString();
0264: DAVTreeNode desiredNode=getNodeMatchingPath(fullPath);
0265: BusinessTracker.removeBusy("addNode");
0266: return desiredNode;
0267:
0268: }
0269: BusinessTracker.removeBusy("addNode");
0270: return possibleNode;
0271: }
0272: if (nodePath.startsWith(initialPath)) //case 3
0273: {
0274: removeNodeFromParent(possibleNode);
0275: }
0276: }
0277: DAVAuthenticator auth=new DAVAuthenticatorImpl(sd);
0278: DAVTreeNode node=_addConnectionNode(sd.usesSSL(),
0279: host,
0280: port,
0281: initialPath,
0282: auth,
0283: waitForIt,
0284: postRunner);
0285: BusinessTracker.removeBusy("addNode");
0286: return node;
0287: }
0288:
0289: public DAVTreeNode addConnectionNode(ServerData sd, Runnable postRunner)
0290: {
0291: return addConnectionNode(sd, false, postRunner);
0292: }
0293:
0294: private synchronized DAVTreeNode _addConnectionNode(final boolean usingSSL,
0295: final String host,
0296: final int port,
0297: final String initialPath,
0298: final DAVAuthenticator auth,
0299: final boolean waitForIt,
0300: final Runnable postRunner)
0301: {
0302: final DAVTreeNode retNode=null;
0303: Thread t=new Thread() {
0304: public void run() {
0305: DAVConnection dc=DAVConnectionPool.getDAVConnection(host, port, usingSSL);
0306: ExplorerApp.getAppContext().getConfigurator().configure(dc);
0307: Debug.trace(this , Debug.DP4, "configured new connection: its timeout is {0}",
0308: new Integer(dc.getSocketTimeout()));
0309: dc.setAuthenticator(auth);
0310: PropFindMethod method;
0311: if (DO_ALLPROP)
0312: method=new PropFindMethod(initialPath);
0313: else
0314: method=new PropFindMethod(initialPath, Depth.ONE, DAVProperty.BASIC_PROPERTIES);
0315: try
0316: {
0317: dc.execute(method);
0318: if (method.getStatus()>=400)
0319: return;
0320: DAVFile file=method.getDAVFile();
0321: final DAVTreeNode newNode=new DAVTreeNode(file, getShowNonCollections());
0322: newNode.setShowingHost(true); //display host, port and path
0323: retNode=newNode;
0324:
0325: Runnable runner=new Runnable()
0326: {
0327: public void run()
0328: {
0329: addConnection(newNode);
0330: if (postRunner!=null)
0331: postRunner.run();
0332: }
0333: };
0334:
0335: if (!waitForIt)
0336: {
0337: SwingUtilities.invokeLater(runner);
0338: }
0339: else
0340: {
0341: runner.run();
0342: }
0343: }
0344: catch (Exception ugh)
0345: {
0346: final Exception ugh2=ugh;
0347: Debug.trace(this , Debug.DP2, ugh);
0348: SwingUtilities.invokeLater(new Runnable() {
0349: public void run() {
0350: String message=ResourceManager
0351: .getMessage(ResourceManager.CONNECTION_ERROR_MESSAGE,
0352: new Object[] { new StringBuffer(host)
0353: .append(':')
0354: .append(port)
0355: .toString(),
0356: ugh2.getMessage() } );
0357: String title=ResourceManager
0358: .getMessage(ResourceManager.ERROR_DIALOG_TITLE);
0359: JOptionPane.showMessageDialog(Explorer.guessActiveExplorer(),
0360: message,
0361: title,
0362: JOptionPane.ERROR_MESSAGE);
0363: }
0364: });
0365: }
0366: }
0367: };
0368: if (waitForIt)
0369: t.run();
0370: else
0371: t.start();
0372: return retNode;
0373: }
0374:
0375: public void removeConnectionNode(String host, int port, String initialPath)
0376: {
0377: Object child=getConnectionNode(host, port, initialPath);
0378: if (child instanceof MutableTreeNode)
0379: removeNodeFromParent((MutableTreeNode)child);
0380: DAVConnectionPool.removeDAVConnection(host, port);
0381: }
0382:
0383: public void removeConnectionNode(ServerData sd)
0384: {
0385: Assertion.assert((sd!=null), "serverData is not null");
0386: removeConnectionNode(sd.getHost(), sd.getPort(), sd.getInitialPath());
0387: }
0388:
0389: private Object getConnectionNode(String host, int port, String initialPath)
0390: {
0391: Object root=getRoot();
0392: String connName=new StringBuffer(host)
0393: .append(':')
0394: .append(port)
0395: .append(initialPath)
0396: .toString();
0397: for (int i=0;i<getChildCount(root);i++)
0398: {
0399: Object child=getChild(root, i);
0400: if (child.toString().equals(connName))
0401: {
0402: return child;
0403: }
0404: }
0405: return null;
0406: }
0407:
0408: private Object getConnectionNode(String host, int port)
0409: {
0410: Object root=getRoot();
0411: String connName=new StringBuffer(host)
0412: .append(':')
0413: .append(port)
0414: .append('/')
0415: .toString();
0416: for (int i=0;i<getChildCount(root);i++)
0417: {
0418: Object child=getChild(root, i);
0419: if (child.toString().startsWith(connName))
0420: {
0421: return child;
0422: }
0423: }
0424: return null;
0425: }
0426:
0427: public byte[] get(DAVFile file)
0428: {
0429: BusinessTracker.addBusy(file);
0430: DAVConnection dc=DAVConnectionPool.getDAVConnection(file.getHost(), file.getPort());
0431: GetMethod method=new GetMethod(file.getName());
0432: byte[] body=null;
0433: try
0434: {
0435: dc.execute(method);
0436: int status=method.getStatus();
0437: Debug.trace(this , Debug.DP3, "status of response: {0}",
0438: new Object[] {new Integer(status) });
0439: if (status>=400)
0440: {
0441: handleStatusError(file.getName(), status, method.getResponseBody());
0442: }
0443: else body=method.getResponseBody();
0444: }
0445: catch (Exception davie)
0446: {
0447: Debug.trace(this , Debug.DP2, davie);
0448: handleConnectionError(davie, file.getHost(), file.getPort());
0449: }
0450: BusinessTracker.removeBusy(file);
0451: return body;
0452: }
0453:
0454: public void put(final DAVTreeNode parentNode,
0455: final String fileName,
0456: final byte[] bodyBytes)
0457: {
0458: put(parentNode, fileName, bodyBytes, null);
0459: }
0460:
0461: public void put(final DAVTreeNode parentNode,
0462: final String fileName,
0463: final byte[] bodyBytes,
0464: final Runnable successRunner)
0465: {
0466: Assertion.assert((parentNode!=null),
0467: "DAVTreeNode {0} is not null",
0468: new Object[] {parentNode});
0469: BusinessTracker.addBusy("put");
0470: Thread t=new Thread() {
0471: public void run()
0472: {
0473: ConnectStruct cs=getConnectStruct(parentNode, fileName);
0474: String filePath=cs.getFilePath();
0475: PutMethod method=new PutMethod(filePath, bodyBytes);
0476: /*
0477: * look for a locktoken on the target resource, if it exists
0478: * and we already know about it
0479: */
0480: DAVFile possibleTarget=null;
0481: for (Iterator it=parentNode.getDAVFile().children();it.hasNext();)
0482: {
0483: DAVFile kidFile=(DAVFile) it.next();
0484: if (kidFile.getFileName().equals(fileName))
0485: {
0486: possibleTarget=kidFile;
0487: break;
0488: }
0489: }
0490: if (possibleTarget!=null)
0491: {
0492: if (possibleTarget.isCollection())
0493: {
0494: //not allowed
0495: String title=ResourceManager.getMessage(ResourceManager.ERROR_DIALOG_TITLE);
0496: String message=ResourceManager.getMessage(ResourceManager.PUT_NOT_ALLOWED_MESSAGE);
0497: JOptionPane.showMessageDialog(Explorer.guessActiveExplorer(),
0498: message,
0499: title,
0500: JOptionPane.ERROR_MESSAGE);
0501: BusinessTracker.removeBusy("put");
0502: return;
0503: }
0504: else if (possibleTarget.isLocked())
0505: {
0506: method.setLockToken(possibleTarget.getExclusiveLockToken());
0507: }
0508: }
0509: try
0510: {
0511: cs.getDAVConnection().execute(method);
0512: //check status
0513: int status=method.getStatus();
0514: Debug.trace(this , Debug.DP3, "status of response: "+status);
0515: if (status>=400)
0516: handleStatusError(filePath, status, method.getResponseBody());
0517: else
0518: {
0519: reloadNode(parentNode);
0520: if (successRunner!=null)
0521: SwingUtilities.invokeLater(successRunner);
0522: BusinessTracker.removeBusy("put");
0523: }
0524: }
0525: catch (Exception davie)
0526: {
0527: Debug.trace(this , Debug.DP2, davie);
0528: handleConnectionError(davie, cs.getHost(), cs.getPort());
0529: }
0530: }
0531: };
0532: t.start();
0533: }
0534:
0535: public void lock(final DAVTreeNode parentNode, final DAVFile file, final String userInfo, final Runnable runnable)
0536: {
0537: Assertion.assert((parentNode!=null),
0538: "DAVTreeNode {0} is not null",
0539: new Object[] {parentNode});
0540: BusinessTracker.addBusy("lock");
0541: Thread t=new Thread() {
0542: public void run() {
0543: ConnectStruct cs=getConnectStruct(parentNode, file.getFileName());
0544: String filePath=cs.getFilePath();
0545: LockMethod method=new LockMethod(filePath, userInfo);
0546: try
0547: {
0548: cs.getDAVConnection().execute(method);
0549: int status=method.getStatus();
0550: if (status>=400)
0551: handleStatusError(filePath, status, method.getResponseBody());
0552: else
0553: {
0554: reloadNode(parentNode);
0555: if (runnable!=null)
0556: {
0557: SwingUtilities.invokeLater(runnable);
0558: }
0559: BusinessTracker.removeBusy("lock");
0560: }
0561: }
0562: catch (Exception e)
0563: {
0564: handleConnectionError(e, cs.getHost(), cs.getPort());
0565: }
0566: }
0567: };
0568: t.start();
0569: }
0570:
0571: public void proppatch(final DAVTreeNode parentNode, final DAVFile file, final Map propertyValueMap)
0572: {
0573: Assertion.assert((parentNode!=null),
0574: "DAVTreeNode {0} is not null",
0575: new Object[] {parentNode});
0576: BusinessTracker.addBusy("proppatch");
0577: Thread t=new Thread() {
0578: public void run() {
0579: ConnectStruct cs=getConnectStruct(parentNode, file.getFileName());
0580: String filePath=cs.getFilePath();
0581: PropPatchMethod method=new PropPatchMethod(filePath);
0582: if (file.isExclusiveLocked())
0583: {
0584: method.setLockToken(file.getExclusiveLockToken());
0585: }
0586:
0587: for (Iterator it=propertyValueMap.keySet().iterator();it.hasNext();)
0588: {
0589: Object o=it.next();
0590: Object val=propertyValueMap.get(o);
0591: if (!(o instanceof DAVProperty))
0592: {
0593: BusinessTracker.removeAllBusy();
0594: throw new IllegalArgumentException("propertyValueMap must map DAVProperty objects "
0595: + "to Object values. A null value indicates that "
0596: + "the property should be removed.");
0597: }
0598: if (val!=null)
0599: {
0600: method.putProperty((DAVProperty) o, val);
0601: }
0602: else method.removeProperty((DAVProperty) o);
0603: }
0604: try
0605: {
0606: cs.getDAVConnection().execute(method);
0607: int status=method.getStatus();
0608: if (status>=400)
0609: handleStatusError(filePath, status, method.getResponseBody());
0610: else if (status==207) //multistatus
0611: {
0612: //check response body of children
0613: DAVFile retFile = method.getDAVFile();
0614: Integer statusObj=retFile.getStatus();
0615: if (statusObj==null)
0616: {
0617: Debug.trace(this , Debug.DP2, "null status for "+ retFile);
0618: }
0619: else
0620: {
0621: status=statusObj.intValue();
0622: if (status>=400)
0623: {
0624: String s=retFile.getResponseDescription();
0625: if (s==null) s="";
0626: handleStatusError(retFile.getFullName(),
0627: status,
0628: s.getBytes());
0629: return;
0630: }
0631: }
0632: }
0633: reloadNode(parentNode);
0634: BusinessTracker.removeBusy("proppatch");
0635: }
0636: catch (Exception e)
0637: {
0638: handleConnectionError(e, cs.getHost(), cs.getPort());
0639: }
0640: }
0641: };
0642: t.start();
0643: }
0644:
0645:
0646:
0647: public void stealLock(final DAVTreeNode parentNode, final DAVFile file, final String userInfo)
0648: {
0649: Assertion.assert((parentNode!=null),
0650: "DAVTreeNode {0} is not null",
0651: new Object[] {parentNode});
0652: BusinessTracker.addBusy("steal");
0653: Thread t=new Thread() {
0654: public void run() {
0655: ConnectStruct cs=getConnectStruct(parentNode, file.getFileName());
0656: String filePath=cs.getFilePath();
0657: String lockToken=file.getExclusiveLockToken();
0658: Assertion.assert((lockToken!=null),
0659: "lockToken for {0} is not null",
0660: new Object[] {file});
0661: UnlockMethod unlockMethod=new UnlockMethod(filePath, lockToken);
0662: LockMethod lockMethod=new LockMethod(filePath, userInfo);
0663: try
0664: {
0665: cs.getDAVConnection().execute(unlockMethod);
0666: int status=unlockMethod.getStatus();
0667: if (status>=400)
0668: {
0669: if (status==403)
0670: {
0671: //obtain server data for node; set its permitsLockStealing property
0672: ServerData.getServer(((DAVTreeNode)getPathToRoot(parentNode)[1]).toString())
0673: .setPermitsLockStealing(false);
0674: }
0675: handleStatusError(filePath, status, unlockMethod.getResponseBody());
0676: }
0677: else
0678: {
0679: cs.getDAVConnection().execute(lockMethod);
0680: status=lockMethod.getStatus();
0681: if (status>=400)
0682: handleStatusError(filePath, status, lockMethod.getResponseBody());
0683: else
0684: {
0685: reloadNode(parentNode);
0686: BusinessTracker.removeBusy("steal");
0687: }
0688: }
0689: }
0690: catch (Exception e)
0691: {
0692: handleConnectionError(e, cs.getHost(), cs.getPort());
0693: }
0694: }
0695: };
0696: t.start();
0697: }
0698:
0699: public void unlock(final DAVTreeNode parentNode, final DAVFile file)
0700: {
0701: unlock(parentNode, file, null);
0702: }
0703:
0704: public void unlock(final DAVTreeNode parentNode, final DAVFile file, final Runnable successRunner)
0705: {
0706: Assertion.assert((parentNode!=null),
0707: "DAVTreeNode {0} is not null",
0708: new Object[] {parentNode});
0709: BusinessTracker.addBusy("unlock");
0710: Thread t=new Thread() {
0711: public void run() {
0712: ConnectStruct cs=getConnectStruct(parentNode, file.getFileName());
0713: String filePath=cs.getFilePath();
0714: String lockToken=file.getExclusiveLockToken();
0715: Assertion.assert((lockToken!=null),
0716: "lockToken for {0} is not null",
0717: new Object[] {file});
0718: UnlockMethod method=new UnlockMethod(filePath, lockToken);
0719: try
0720: {
0721: cs.getDAVConnection().execute(method);
0722: int status=method.getStatus();
0723: if (status>=400)
0724: handleStatusError(filePath, status, method.getResponseBody());
0725: else
0726: {
0727: reloadNode(parentNode);
0728: if (successRunner!=null) SwingUtilities.invokeLater(successRunner);
0729: BusinessTracker.removeBusy("unlock");
0730: }
0731: }
0732: catch (Exception e)
0733: {
0734: handleConnectionError(e, cs.getHost(), cs.getPort());
0735: }
0736: }
0737: };
0738: t.start();
0739: }
0740:
0741: public void delete(final DAVTreeNode parentNode, final String fileName)
0742: {
0743: Assertion.assert((parentNode!=null),
0744: "DAVTreeNode {0} is not null",
0745: new Object[] {parentNode});
0746: BusinessTracker.addBusy("delete");
0747: Thread t=new Thread() {
0748: public void run() {
0749: ConnectStruct cs=getConnectStruct(parentNode, fileName);
0750: String filePath=cs.getFilePath();
0751: DeleteMethod method=new DeleteMethod(filePath);
0752: try
0753: {
0754: cs.getDAVConnection().execute(method);
0755: int status=method.getStatus();
0756: if (status>=400)
0757: handleStatusError(filePath, status, method.getResponseBody());
0758: else
0759: {
0760: reloadNode(parentNode);
0761: BusinessTracker.removeBusy("delete");
0762: }
0763: }
0764: catch (Exception e)
0765: {
0766: handleConnectionError(e, cs.getHost(), cs.getPort());
0767: }
0768: }
0769: };
0770: t.start();
0771: }
0772:
0773: public void mkcol(final DAVTreeNode parentNode, final String collectionName)
0774: {
0775: Assertion.assert((parentNode!=null),
0776: "DAVTreeNode {0} is not null",
0777: new Object[] {parentNode});
0778: BusinessTracker.addBusy("mkcol");
0779: Thread t=new Thread() {
0780: public void run()
0781: {
0782: ConnectStruct cs=getConnectStruct(parentNode, collectionName);
0783: String filePath=cs.getFilePath();
0784: MkcolMethod method=new MkcolMethod(filePath);
0785: try
0786: {
0787: cs.getDAVConnection().execute(method);
0788: //check status
0789: int status=method.getStatus();
0790: Debug.trace(this , Debug.DP3, "status of response: "+status);
0791: if (status>=400)
0792: handleStatusError(filePath, status, method.getResponseBody());
0793: else
0794: {
0795: reloadNode(parentNode);
0796: BusinessTracker.removeBusy("mkcol");
0797: }
0798: }
0799: catch (Exception davie)
0800: {
0801: Debug.trace(this , Debug.DP2, davie);
0802: handleConnectionError(davie, cs.getHost(), cs.getPort());
0803: }
0804: }
0805: };
0806: t.start();
0807: }
0808:
0809: protected String getURLForPath(TreePath teepee)
0810: {
0811: Object[] path=teepee.getPath();
0812: return _getURLForPath(path);
0813: }
0814:
0815: private String _getURLForPath(Object[] path)
0816: {
0817: if (path==null || path.length<2)
0818: {
0819: return null;
0820: }
0821: String prefix=getProtocolForNode((DAVTreeNode)path[path.length-1]);
0822: StringBuffer sb=new StringBuffer(prefix);
0823: //discard root, which we don't show
0824: for (int i=1;i<path.length;i++)
0825: {
0826: if (i>1) sb.append(DAVConstants.DAV_FILE_SEPARATOR);
0827: sb.append(path[i]);
0828: }
0829: return sb.toString();
0830: }
0831:
0832: public DAVTreeNode getNodeMatchingPath(String path)
0833: {
0834: if (path.startsWith(DAVConstants.DAV_FILE_SEPARATOR))
0835: path=path.substring(1);
0836: if (!path.endsWith(DAVConstants.DAV_FILE_SEPARATOR))
0837: path=path+DAVConstants.DAV_FILE_SEPARATOR;
0838: return getNodeMatchingURL(path, getRoot(), false);
0839: }
0840:
0841: public DAVTreeNode getNodeMatchingURL(String url)
0842: {
0843: return getNodeMatchingURL(url, getRoot(), true);
0844: }
0845:
0846: protected DAVTreeNode getNodeMatchingURL(String url, Object nodeObj)
0847: {
0848: return getNodeMatchingURL(url, nodeObj, true);
0849: }
0850:
0851: private String getProtocolForNode(DAVTreeNode node)
0852: {
0853: if (node!=null)
0854: return node.getDAVFile().getDAVConnection().getProtocol();
0855: return null;
0856: }
0857:
0858:
0859: protected DAVTreeNode getNodeMatchingURL(String url,
0860: Object nodeObj,
0861: boolean useHttpPrefix)
0862: {
0863: String fullname=null;
0864: if (nodeObj instanceof DAVTreeNode)
0865: {
0866: DAVTreeNode dtn=(DAVTreeNode) nodeObj;
0867: StringBuffer nameBuffer=new StringBuffer();
0868: if (useHttpPrefix)
0869: {
0870: nameBuffer.append(getProtocolForNode(dtn))
0871: .append("://");
0872: }
0873: fullname=nameBuffer.append(((DAVTreeNode)nodeObj).getDAVFile().getFullName()).toString();
0874: Debug.trace(this , Debug.DP5, "testing node with fullname {0} against url {1}",
0875: new Object[] { fullname, url });
0876: if (url.equals(fullname))
0877: return (DAVTreeNode)nodeObj;
0878: }
0879: if (nodeObj.equals(getRoot())
0880: || (fullname!=null && url.startsWith(fullname))) //recurse into directory
0881: {
0882: int cnt=getChildCount(nodeObj);
0883: for (int i=0;i<cnt;i++)
0884: {
0885: DAVTreeNode dtn=getNodeMatchingURL(url, getChild(nodeObj, i), useHttpPrefix);
0886: if (dtn!=null)
0887: return dtn;
0888: }
0889: }
0890: return null;
0891: }
0892:
0893: private void copyOrMove(final DAVTreeNode node,
0894: final DAVFile file,
0895: final String destinationURL,
0896: final boolean move)
0897: {
0898: Assertion.assert((node!=null),
0899: "DAVTreeNode {0} is not null",
0900: new Object[] {node});
0901: BusinessTracker.addBusy("copyOrMove");
0902: int fileDivider=destinationURL.lastIndexOf(DAVConstants.DAV_FILE_SEPARATOR)+1;
0903: String destDir=destinationURL.substring(0, fileDivider);
0904: String destFileName=destinationURL.substring(fileDivider);
0905: Debug.trace(this , Debug.DP3,
0906: "destination directory is {0}, destination filename is {1}",
0907: new Object[] {destDir, destFileName});
0908: DAVTreeNode destNode=getNodeMatchingURL(destDir, getRoot());
0909: Debug.trace(this , Debug.DP3, "destNode: {0}",
0910: new Object[] {destNode});
0911: if (destNode==null)
0912: {
0913: Debug.trace(this , Debug.DP2, "destNode is null");
0914: //should throw or handle an error -- TO BE DONE
0915: BusinessTracker.removeBusy(this );
0916: return;
0917: }
0918: DAVFile possiblyLocked=null;
0919: //see if the destination file already exists and has a lockToken
0920: for (Iterator it=destNode.getDAVFile().children();it.hasNext();)
0921: {
0922: DAVFile kidFile=(DAVFile) it.next();
0923: if (destFileName.equals(kidFile.getFileName()))
0924: {
0925: possiblyLocked=kidFile;
0926: Debug.trace(this , Debug.DP3, "possiblyLocked file: {0}",
0927: new Object[] {kidFile});
0928: break;
0929: }
0930: }
0931:
0932: /*
0933: * at this point, check whether lock owner is same as current user;
0934: * if not, ??? TO BE DONE
0935: */
0936:
0937: final String destinationLockToken=(possiblyLocked!=null)
0938: ? possiblyLocked.getExclusiveLockToken()
0939: : null;
0940:
0941: Thread t=new Thread() {
0942: public void run()
0943: {
0944: ConnectStruct cs=getConnectStruct(node, file.getFileName());
0945: String filePath=cs.getFilePath();
0946: CopyMethod method=(move)
0947: ? (new MoveMethod(filePath, destinationURL))
0948: : (new CopyMethod(filePath, destinationURL));
0949: method.setSourceLockToken(file.getExclusiveLockToken());
0950: method.setDestLockToken(destinationLockToken);
0951: try
0952: {
0953: cs.getDAVConnection().execute(method);
0954: //check status
0955: int status=method.getStatus();
0956: Debug.trace(this , Debug.DP3, "status of response: "+status);
0957: if (status>=400)
0958: handleStatusError(filePath, status, method.getResponseBody());
0959: else
0960: {
0961: if (move) reloadNode(node);
0962: int lastDex=destinationURL.lastIndexOf(DAVConstants.DAV_FILE_SEPARATOR)+1;
0963: DAVTreeNode newNode=getNodeMatchingURL(destinationURL.substring(0, lastDex));
0964: reloadNode(newNode);
0965: BusinessTracker.removeBusy("copyOrMove");
0966: }
0967: }
0968: catch (Exception davie)
0969: {
0970: Debug.trace(this , Debug.DP2, davie);
0971: handleConnectionError(davie, cs.getHost(), cs.getPort());
0972: }
0973: }
0974: };
0975: t.start();
0976: }
0977:
0978: public void copy(DAVTreeNode node, DAVFile file, String destinationURL)
0979: {
0980: copyOrMove(node, file, destinationURL, false);
0981: }
0982:
0983:
0984: public void move(DAVTreeNode node, DAVFile file, String destinationURL)
0985: {
0986: copyOrMove(node, file, destinationURL, true);
0987: }
0988:
0989:
0990: protected void handleConnectionError(final Exception ugh, final String host, final int port)
0991: {
0992: Debug.trace(this , Debug.DP2, ugh);
0993: Runnable r = new Runnable() {
0994: public void run() {
0995: BusinessTracker.removeAllBusy();
0996: String message=ResourceManager
0997: .getMessage(ResourceManager.CONNECTION_ERROR_MESSAGE,
0998: new Object[] { host+DAVConstants.DAV_FILE_SEPARATOR+port,
0999: ugh.getMessage() } );
1000: String title=ResourceManager.getMessage(ResourceManager.ERROR_DIALOG_TITLE);
1001: JOptionPane.showMessageDialog(Explorer.guessActiveExplorer(),
1002: message,
1003: title,
1004: JOptionPane.ERROR_MESSAGE);
1005: }
1006: };
1007: if (SwingUtilities.isEventDispatchThread())
1008: r.run();
1009: else SwingUtilities.invokeLater(r);
1010: }
1011:
1012: protected void handleStatusError(final String filePath, final int status, final byte[] bodyBytes)
1013: {
1014: Runnable r=new Runnable() {
1015: public void run() {
1016: BusinessTracker.removeAllBusy();
1017: String title=ResourceManager.getMessage(ResourceManager.ERROR_DIALOG_TITLE);
1018: String message=ResourceManager.getMessage(ResourceManager.STATUS_ERROR_MESSAGE,
1019: new Object[] {filePath,
1020: new Integer(status),
1021: new String(bodyBytes)});
1022: JOptionPane.showMessageDialog(Explorer.guessActiveExplorer(),
1023: message,
1024: title,
1025: JOptionPane.ERROR_MESSAGE);
1026: }
1027: };
1028: if (SwingUtilities.isEventDispatchThread())
1029: r.run();
1030: else SwingUtilities.invokeLater(r);
1031: }
1032:
1033: private void handleInvalidDestinationError(final String destination)
1034: {
1035: Runnable r=new Runnable() {
1036: public void run() {
1037: BusinessTracker.removeAllBusy();
1038: String title=ResourceManager.getMessage(ResourceManager.ERROR_DIALOG_TITLE);
1039: String message=ResourceManager.getMessage(ResourceManager.INVALID_DESTINATION_ERROR_MESSAGE,
1040: new Object[] {destination});
1041: JOptionPane.showMessageDialog(Explorer.guessActiveExplorer(),
1042: message,
1043: title,
1044: JOptionPane.ERROR_MESSAGE);
1045: }
1046: };
1047: if (SwingUtilities.isEventDispatchThread())
1048: r.run();
1049: else SwingUtilities.invokeLater(r);
1050: }
1051:
1052: private ConnectStruct getConnectStruct(DAVTreeNode parentNode, String fileName)
1053: {
1054: DAVFile file=parentNode.getDAVFile();
1055: String host=file.getHost();
1056: int port=file.getPort();
1057: String parentName=file.getName();
1058: StringBuffer sb=new StringBuffer(parentName);
1059: if (!parentName.endsWith(DAVConstants.DAV_FILE_SEPARATOR))
1060: sb.append(DAVConstants.DAV_FILE_SEPARATOR);
1061: sb.append(fileName);
1062: DAVConnection dc=DAVConnectionPool.getDAVConnection(host, port);
1063: return new ConnectStruct(dc, sb.toString(), host, port);
1064: }
1065:
1066: private class ConnectStruct
1067: {
1068: private DAVConnection dc;
1069: private String filePath;
1070: private String host;
1071: private int port;
1072:
1073: public DAVConnection getDAVConnection() { return dc; }
1074: public String getFilePath() { return filePath; }
1075: public String getHost() { return host; }
1076: public int getPort() { return port; }
1077:
1078: ConnectStruct(DAVConnection dc, String filePath, String host, int port)
1079: {
1080: this .dc=dc;
1081: this .filePath=filePath;
1082: this .host=host;
1083: this .port=port;
1084: }
1085: }
1086:}
1087:
1088:/* $Log: DAVTreeModel.java,v $
1089:/* Revision 1.34 2001/07/25 21:17:39 smulloni
1090:/* fixed typo in error message.
1091:/*
1092:/* Revision 1.33 2001/06/13 01:09:03 smulloni
1093:/* Some improvements to the network customizer (which is still buggy).
1094:/* Also improvements for using the doAllprop switch, and for the layout of
1095:/* the PropertiesDialog.
1096:/*
1097:/* Revision 1.32 2001/06/05 01:09:53 smulloni
1098:/* fixed bug wtih failed connection attempts, which used to set the
1099:/* connection flag as if the connection had succeeded.
1100:/*
1101:/* Revision 1.31 2001/01/23 20:50:09 smulloni
1102:/* added configurable socket timeout
1103:/*
1104:/* Revision 1.30 2001/01/05 08:01:12 smulloni
1105:/* changes to the connection gui for the Explorer; added depth configurability to
1106:/* propfind in the jpython test script; added an experimental "allprop" system
1107:/* property which affects the propfind query type
1108:/*
1109:/* Revision 1.29 2001/01/03 00:30:35 smulloni
1110:/* a number of modifications along the way to replacing JFileChooser with
1111:/* something more suitable for remote (virtual) files.
1112:/*
1113:/* Revision 1.28 2001/01/02 17:34:21 smulloni
1114:/* added the ability to create DAVTreeModels that show non-directories. This
1115:/* permits me to use one with an org.skunk.swing.TreeNodeChooser.
1116:/*
1117:/* Revision 1.27 2000/12/19 22:36:05 smulloni
1118:/* adjustments to preamble.
1119:/*
1120:/* Revision 1.26 2000/12/18 23:22:47 smulloni
1121:/* added optional SSL capability to the gui application. Separate make targets
1122:/* exist for building it will SSL support.
1123:/*
1124:/* Revision 1.25 2000/12/04 23:51:16 smulloni
1125:/* added ImageViewer; fixed word in SimpleTextEditor
1126:/*
1127:/* Revision 1.24 2000/12/03 23:53:26 smulloni
1128:/* added license and copyright preamble to java files.
1129:/*
1130:/* Revision 1.23 2000/11/28 19:36:19 smullyan
1131:/* some bug fixes; notes.
1132:/*
1133:/* Revision 1.22 2000/11/23 04:37:31 smullyan
1134:/* editor and explorer now synch their files using the StateMonitor, which has
1135:/* been improved significantly
1136:/*
1137:/* Revision 1.21 2000/11/22 00:11:26 smullyan
1138:/* editor now locks and unlocks, more or less appropriately.
1139:/*
1140:/* Revision 1.20 2000/11/20 23:30:19 smullyan
1141:/* more editor integration work.
1142:/*
1143:/* Revision 1.19 2000/11/17 20:25:04 smullyan
1144:/* new SaveAction; a StateMonitor being added to handle application state.
1145:/*
1146:/* Revision 1.18 2000/11/14 23:17:08 smullyan
1147:/* more improvements to the PropertiesDialog.
1148:/*
1149:/* Revision 1.17 2000/11/13 23:28:30 smullyan
1150:/* first pass at a gui for proppatch, added to the propertiese dialog. Highly
1151:/* problematic still, needs substantial work.
1152:/*
1153:/* Revision 1.16 2000/11/10 22:40:06 smullyan
1154:/* added icon to table for resource type; fixes to copy and move; disabling of
1155:/* menu items that are inappropriate.
1156:/*
1157:/* Revision 1.15 2000/11/09 23:34:55 smullyan
1158:/* log added to every Java file, with the help of python. Lock stealing
1159:/* implemented, and treatment of locks made more robust.
1160:/* */
|