/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*
* C abinet.java
*
* Created on September 17, 2003, 10:04 AM
*/
package tufts.oki.remoteFiling;
import tufts.oki.shared.*;
import org.apache.commons.net.ftp.*;
import java.io.*;
import java.util.*;
import osid.OsidException;
/**
* RemoteCabinet is a implementation of osid.filing.Cabinet which models a remote
* directory accessed via FTP. In order to save internal memory space, a cabinet
* is not initialized to the entries in it until entries() is called. The add()
* method is provided to allow new entries in this directory to be included in its
* list of entries. The createCabinet() and createByteStore() methods are used to
* create a new directory or new file, respectively.
*
* @author Mark Norton - OKI compatibility.
* @author Salem Berhanu - much of the FTP transactions.
*
*/
public class RemoteCabinet extends RemoteCabinetEntry implements osid.filing.Cabinet {
/* parent is inherited from Cabinet Entry. */
Vector children = null;
tufts.oki.shared.Properties properties = null;
private boolean initialized = false;
//private boolean open = false;
//private File dir = null; // The remote directory being modeled.
/**
* Initializes the feilds interited from CabinetEntry and adds a vector of children.
* Creates a Properties object to hold properties associated with this cabinet.
*
* @author Mark Norton
*
*/
public RemoteCabinet(String displayName, osid.shared.Agent agentOwner, osid.filing.Cabinet parent, RemoteClient rc) {
super(displayName, agentOwner, parent,rc);
children = new Vector(100);
//FilingCabinetType type = new FilingCabinetType();
//properties = new osid_mjn.shared.Properties(type);
if (parent == null)
updateDisplayName("");
else
updateDisplayName(displayName);
}
/**
* Add the entry to the list of children.
*
* @author Mark Norton
*/
public void add(osid.filing.CabinetEntry entry, java.lang.String name) throws osid.filing.FilingException {
/* Update the display name in entry with name. Does this make sense? */
entry.updateDisplayName(name);
/* Add the element to the Vector array. */
children.addElement(entry);
}
/**
* Add the entry to the list of children.
*
* @author Mark Norton
*/
public void add(osid.filing.CabinetEntry entry) throws osid.filing.FilingException {
/* Add the element to the Vector array. */
children.addElement(entry);
}
/*
* The oldByteStore is copied into the new ByteStore.
*
* @author Mark Norton
*
* @return A new ByteStore with the name provided and this cabinet as parent.
*/
public osid.filing.ByteStore copyByteStore(String name, osid.filing.ByteStore oldByteStore) throws osid.filing.FilingException {
throw new osid.filing.FilingException(osid.filing.FilingException.UNIMPLEMENTED);
}
/**
* Currently unimplemented. A lot depends on the eventual use of the filing
* system implemented by these classes. Not all systems will have a restriction
* on available and used byes.
*
* @author Mark Norton
*
* @return The number of bytes available in this cabinet.
*/
public long getAvailableBytes() throws osid.filing.FilingException {
throw new osid.filing.FilingException(osid.filing.FilingException.UNIMPLEMENTED);
}
/**
* Currently unimplemented. A lot depends on the eventual use of the filing
* system implemented by these classes. Not all systems will have a restriction
* on available and used byes.
* <p>
* This could be implemented as the sum of of the sizes of the ByteStores in this
* directory, but its not clear what use that would be.
*
* @author Mark Norton
*
* @return The number of bytes used in this cabinet.
*/
public long getUsedBytes() throws osid.filing.FilingException {
throw new osid.filing.FilingException(osid.filing.FilingException.UNIMPLEMENTED);
}
/**
* Create a new ByteStore, add it to this Cabinet.
*
* @author Mark Norton
*
* @return A new ByteStore with the name provided and this cabinet as parent.
*/
public osid.filing.ByteStore createByteStore(String name) {
osid.filing.ByteStore bs = null;
try {
bs = new RemoteByteStore(name, this,rc);
this.add(bs);
} catch (osid.OsidException ex) {
}
return bs;
}
/**
* Create a new cabinet entry with
* the agentOwner of this new cabinet as this owner of this Cabinet.
*
* @author Mark Norton
*
* @return A new cabinet with the given displayName.
*/
public osid.filing.Cabinet createCabinet(String displayName) throws osid.filing.FilingException {
// Create the new cabinet entry.
osid.shared.Agent agentOwner = super.getCabinetEntryAgent();
RemoteCabinet entry = new RemoteCabinet(displayName, agentOwner, this,rc);
// Make a directory on the remote file system.
try {
FTPClient client = rc.getClient();
if (!client.makeDirectory(entry.getFullName()))
throw new osid.filing.FilingException(osid.filing.FilingException.ITEM_ALREADY_EXISTS);
} catch (java.io.IOException ex) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
/* Add the element to the Vector array. */
children.addElement(entry);
return entry;
}
/**
* Creates an iterator which lists the entries in this cabinet. A check is made to
* see if this cabinet was previously opened. If not, it is opened and initialized
* with the entries contained in it.
*
* @author Mark Norton
*
* @return Return an iterator for the entries in this cabinet.
*/
public osid.filing.CabinetEntryIterator entries() throws osid.filing.FilingException {
// Check to see if this cabinet is unopened. If not, intialize it.
if (!initialized) {
// Initialize the directory by getting all entries contained in it.
FTPFile[] files = null;
try {
String rootBase = rc.getRootBase();
RemotePath path = new RemotePath(rootBase, this);
String pathname = path.getPathString();
//System.out.println("PATHNAME:"+pathname);
FTPClient client = rc.getClient();
files = client.listFiles(pathname); // Executes an FTP LIST command.
osid.shared.Agent agentOwner = super.getCabinetEntryAgent();
// Iterate over the files returned and create CabinetEntries for them.
// Note that there is a lot of other information in the FTPFile objects which
// could be added to the entries being created here. In particular, creation
// date.
for (int i = 0;files != null && i < files.length; i++) {
if (files[i].isDirectory()) {
//System.out.println ("\tDir " + i + ": " + files[i].getName());
//RemoteCabinet cab = (RemoteCabinet) this.createCabinet (files[i].getName());
add(new RemoteCabinet(files[i].getName(), agentOwner, this,rc));
} else if (files[i].isFile()) {
//System.out.println ("\tFile " + i + ": " + files[i].getName());
//RemoteByteStore store = (RemoteByteStore) this.createByteStore (files[i].getName());
add(new RemoteByteStore(files[i].getName(), this,rc));
}
// Unknown cases are ignored.
}
} catch (java.io.IOException ex1) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
// The current working directory is now intialized.
initialized = true;
}
return (osid.filing.CabinetEntryIterator) new RemoteCabinetEntryIterator(children);
}
/**
* Get a cabinet entry given its name.
*
* @author Mark Norton
*
* @return The cabinet entry with the desired display name. Throws ITEM_DOES_NOT_EXIST if name is unknown.
*/
public osid.filing.CabinetEntry getCabinetEntryByName(String name) throws osid.filing.FilingException {
for (int i = 0; i < children.size(); i++) {
RemoteCabinetEntry entry = (RemoteCabinetEntry) children.elementAt(i);
if (name.compareTo(entry.getDisplayName()) == 0) {
return (osid.filing.CabinetEntry) entry;
}
}
/* ITEM_DOES_NOT_EXIST is not exactly the right sentiment for not finding the entry. */
throw new osid.filing.FilingException(osid.filing.FilingException.ITEM_DOES_NOT_EXIST);
}
/**
* Get a cabinet entry given its identifier. Throws ITEM_DOES_NOT_EXIST if Id is unknown.
*
* @author Mark Norton
*
* @return The cabinet entry corresponding to the identifier passed.
*/
public osid.filing.CabinetEntry getCabinetEntryById(osid.shared.Id id) throws osid.filing.FilingException {
for (int i = 0; i < children.size(); i++) {
RemoteCabinetEntry entry = (RemoteCabinetEntry) children.elementAt(i);
try {
if (id.isEqual(entry.getId())) {
return (osid.filing.CabinetEntry) entry;
}
} catch (osid.shared.SharedException ex) {
/* Not exactly sure what could go wrong with an Id comparison,
* but the compiler insists on catching this exception.
* This will fall through to ITEM_DOES_NOT_EXIST exception throw.
*/
}
}
/* ITEM_DOES_NOT_EXIST is not exactly the right sentiment for not finding the entry. */
throw new osid.filing.FilingException(osid.filing.FilingException.ITEM_DOES_NOT_EXIST);
}
/**
* Return the properties associated with this cabinet as a HashMap. This is better
* implmented using the expanded Properties objects defined elsewhere.
*
* @author Mark Norton
*
* @return The property set of the Properties object associated with this cabinet as a Map.
*/
public java.util.Map getProperties() throws osid.filing.FilingException {
HashMap map = properties.getPropertySet();
return (Map) map;
}
/**
* Get the root cabinet of this cabinet.
*
* @author Mark Norton
*
* @return The root cabinet of this cabinet by searching up the parent links.
*/
public osid.filing.Cabinet getRootCabinet() throws osid.filing.FilingException {
osid.filing.Cabinet cab = this;
while (cab.getParent() != null)
cab = cab.getParent();
return cab;
}
/**
* Currently, this always returns true, since cabinets are always defined to be
* listable. This could be made a property instead.
*
* @author Mark Norton
*
* @return True if this cabinet is listable.
*/
public boolean isListable() throws osid.filing.FilingException {
return true;
}
/**
* Currently, this always returns true, since cabinets are defined to be managable
* in this implementation. It could be made a property instead.
*
* @author Mark Norton
*
* @return True if this cabinet is managable.
*/
public boolean isManageable() throws osid.filing.FilingException {
return true;
}
/**
* Return true if this is a root cabinet, ie., its parent is null.
*
* @author Mark Norton
*
* @return True if this is a root cabinet (parent == null).
*/
public boolean isRootCabinet() throws osid.filing.FilingException {
return (this.getParent() == null);
}
/**
* Remove the entry indicated from the children of this cabinet. The entry is
* identified by comparing the Ids of the entry passed to the Id of the children
* present. This assumes that such Ids are globally unique.
* <p>
* No error is thrown if entry is not present.
* <p>
* Note: remove doesn't remove the entry on the remote file system at this time.
*
* @author Mark Norton
*/
public void remove(osid.filing.CabinetEntry entry) throws osid.filing.FilingException {
osid.shared.Id entry_id = entry.getId();
for (int i = 0; i < children.size(); i++) {
RemoteCabinetEntry ent = (RemoteCabinetEntry)children.elementAt(i);
try {
if (entry_id.isEqual(ent.getId())) {
children.remove(entry);
}
} catch (osid.shared.SharedException ex) {
/* Unlikely that isEqual() will throw a SharedException. */
}
}
}
/**
* Return the number of children this cabinent has.
* <p>
* This is an extension to osid.filing.Cabinet to support classes which imlement
* javax.swing.tree.TreeModel.
*
* @author Mark Norton
*
* @return the number of children in this cabinet.
*/
public int getChildCount() {
return children.size();
}
/**
* Check to see if this cabinet is initialized.
*
* @author Mark Norton
*
* @return true if the cabinet is initialized.
*/
public boolean isInitialized() {
return this.initialized;
}
/**
* Set the initialized flag to the value given.
*
* @author Mark Norton
*/
public void setInitialized(boolean flag) {
this.initialized = flag;
}
/**
* Get the full file name for this entry, including path to local root.
* <p>
* Warning! This name cannot be used to open local files, since it does
* not include rootBase. The absolute name can be created by concatenating
* rootBase and getFullName().
*
* @author Mark Norton
*/
public String getFullName() {
StringBuffer fn = new StringBuffer(rc.getRootBase());
fn = new StringBuffer();
ArrayList parts = new ArrayList(100);
// Walk path to root.
RemoteCabinet ptr = this;
while (ptr.getParent() != null) {
parts.add(0, ptr.getDisplayName());
ptr = (RemoteCabinet)ptr.getParent();
}
// Add intermediate path to file name.
for (int i=0; i < parts.size(); i++) {
fn.append("/" + parts.get(i));
}
// Add the final file name.
// fn.append("/" + getDisplayName()); //:REMOVED not needed
return fn.toString();
}
/**
* Rename the file corresponding to this cabinet and update it's display
* name to the new name.
*
* @author Mark Norton
*/
public void rename(String newName) throws osid.filing.FilingException {
// Check the name of the directory on the remote file system.
try {
FTPClient client = rc.getClient();
if (getParent() == null)
client.rename(getFullName(), rc.getRootBase() + "/" + newName);
else
client.rename(getFullName(), ((RemoteCabinet)getParent()).getFullName() + "/" + newName);
} catch (java.io.IOException ex) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
// Change the name of the Cabinet.
updateDisplayName(newName);
}
public String getUrl() {
String url = "ftp://"+rc.getUserName()+":"+rc.getPassword()+"@"+ rc.getServerName() + this.getFullName();
//System.out.println("CABINET URL:"+ url);
return url; }
}