/*
* 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.
*/
/*
* ByteStore.java
*
* Created on September 20, 2003, 7:39 PM
*
* The software contained in this file is copyright 2003 by Mark J. Norton, all rights reserved.
*/
package tufts.oki.remoteFiling;
import tufts.oki.shared.*;
import org.apache.commons.net.ftp.*;
import java.io.*;
import java.lang.*;
import java.util.*;
/**
* Implements the ByteStore class on a remote filing system. To read the contents of
* this byte store, use the getBytes() method. To replace or initialize the contents,
* use write(byte[]).
*
* @author Mark Norton
* @author Salem Berhanu - much of the FTP transactions.
*
*/
public class RemoteByteStore extends RemoteCabinetEntry implements osid.filing.ByteStore {
protected static final FTPFileListParser __fileListParser = new DefaultFTPFileListParser();
private int used = 0; // Bytes written to the buffer.
private String mime_type = null; // The mime type of this byte store.
private boolean writable = true; // Is it writable?
private boolean readable = true; // Is it readable?
private boolean appendable = true; // Is it appendable?
/**
* Create a ByteStore object given a display name and parent.
*
* @author Mark Norton
*
*/
public RemoteByteStore(String displayName, osid.filing.Cabinet parent, RemoteClient rc) throws osid.filing.FilingException {
super(displayName, parent.getCabinetEntryAgent(), parent,rc);
}
/**
* Commit any pending I/O operations.
* In this implementation, commit() doesn't do anything.
*
* @author Mark Norton
*/
public void commit() {
}
/**
* Get the digest string. Currently, this is unimplemented.
*
* @author Mark Norton
*
* @return A digest string.
*/
public String getDigest(osid.shared.Type algorithmType) throws osid.filing.FilingException {
throw new osid.filing.FilingException(osid.filing.FilingException.UNIMPLEMENTED);
}
/**
* Get an interator over all digest algorithm types supported. Currently unimplemented.
*
* @author Mark Norton
*
* @return A TypeIterator which lists all Digest Algorithm Types.
*/
public osid.shared.TypeIterator getDigestAlgorithmTypes() throws osid.filing.FilingException {
throw new osid.filing.FilingException(osid.filing.FilingException.UNIMPLEMENTED);
}
/*
* The mime types suffix table.
*/
private static java.util.Hashtable mimeTypesSuffixTable = new java.util.Hashtable();
static {
mimeTypesSuffixTable.put("doc", "application/msword");
mimeTypesSuffixTable.put("pdf", "application/pdf");
mimeTypesSuffixTable.put("ai", "application/postscript");
mimeTypesSuffixTable.put("ps", "application/postscript");
mimeTypesSuffixTable.put("eps", "application/postscript");
mimeTypesSuffixTable.put("xls", "application/vnd.ms-excel");
mimeTypesSuffixTable.put("ppt", "application/vnd.ms-powerpoint");
mimeTypesSuffixTable.put("dcr", "application/x-director");
mimeTypesSuffixTable.put("dir", "application/x-director");
mimeTypesSuffixTable.put("dxr", "application/x-director");
mimeTypesSuffixTable.put("swf", "application/x-shockwave-flash");
mimeTypesSuffixTable.put("zip", "application/zip");
//mimeTypesSuffixTable.put("application/x-compress");
mimeTypesSuffixTable.put("tar", "application/x-tar");
mimeTypesSuffixTable.put("mpga", "audio/mpeg");
mimeTypesSuffixTable.put("mp2", "audio/mpeg");
mimeTypesSuffixTable.put("mp3", "audio/mpeg");
mimeTypesSuffixTable.put("ram", "audio/x-pn-realaudio");
mimeTypesSuffixTable.put("rm", "audio/x-pn-realaudio");
mimeTypesSuffixTable.put("rpm", "audio/x-pn-realaudio");
mimeTypesSuffixTable.put("ra", "audio/x-pn-realaudio");
mimeTypesSuffixTable.put("wav", "audio/x-wav");
mimeTypesSuffixTable.put("gif", "image/gif");
mimeTypesSuffixTable.put("jpg", "image/jpeg");
mimeTypesSuffixTable.put("jpeg", "image/jpeg");
mimeTypesSuffixTable.put("jpe", "image/jpeg");
mimeTypesSuffixTable.put("tif", "image/tiff");
mimeTypesSuffixTable.put("tiff", "image/tiff");
mimeTypesSuffixTable.put("bmp", "image/bmp");
mimeTypesSuffixTable.put("html", "text/html");
mimeTypesSuffixTable.put("htm", "text/html");
mimeTypesSuffixTable.put("rtx", "text/richtext");
mimeTypesSuffixTable.put("rtf", "text/rtf");
mimeTypesSuffixTable.put("txt", "text/plain");
mimeTypesSuffixTable.put("mpeg", "video/mpeg");
mimeTypesSuffixTable.put("mpg", "video/mpeg");
mimeTypesSuffixTable.put("mpe", "video/mpeg");
mimeTypesSuffixTable.put("mov", "video/quicktime");
mimeTypesSuffixTable.put("qt", "video/quicktime");
mimeTypesSuffixTable.put("avi", "video/x-msvideo");
}
/*
* The mime types table.
*/
private static java.util.Hashtable mimeTypesTable = new java.util.Hashtable();
static {
mimeTypesTable.put("application/msword","doc");
mimeTypesTable.put("application/pdf","pdf");
mimeTypesTable.put("application/postscript","ai");
mimeTypesTable.put("application/postscript","ps");
mimeTypesTable.put("application/vnd.ms-excel","xls");
mimeTypesTable.put("application/vnd.ms-powerpoint","ppt" );
mimeTypesTable.put("application/zip","zip" );
mimeTypesTable.put("application/x-tar","tar" );
mimeTypesTable.put("audio/mpeg","mpga" );
mimeTypesTable.put("audio/mpeg","mp2" );
mimeTypesTable.put("audio/mpeg","mp3" );
mimeTypesTable.put("audio/x-pn-realaudio","ram" );
mimeTypesTable.put("audio/x-pn-realaudio","rm" );
mimeTypesTable.put("audio/x-pn-realaudio","rpm" );
mimeTypesTable.put("audio/x-pn-realaudio","ra" );
mimeTypesTable.put("audio/x-wav","wav" );
mimeTypesTable.put("image/gif","gif" );
mimeTypesTable.put("image/jpeg","jpg" );
mimeTypesTable.put("image/jpeg","jpeg" );
mimeTypesTable.put("image/jpeg","jpe" );
mimeTypesTable.put("image/tiff","tif" );
mimeTypesTable.put("image/tiff","tiff" );
mimeTypesTable.put("image/bmp","bmp" );
mimeTypesTable.put("text/html","html" );
mimeTypesTable.put("text/html","htm" );
mimeTypesTable.put("text/richtext","rtx" );
mimeTypesTable.put("text/rtf","rtf" );
mimeTypesTable.put("text/plain","txt");
mimeTypesTable.put("video/mpeg","mpeg" );
mimeTypesTable.put("video/mpeg","mpg" );
mimeTypesTable.put("video/mpeg","mpe" );
mimeTypesTable.put("video/quicktime","mov" );
mimeTypesTable.put("video/quicktime","qt" );
mimeTypesTable.put("video/x-msvideo","avi" );
}
/**
* Gets the mime-type of this ByteStore.
*
* @author Salem Berhanu
*/
public String getMimeType() throws osid.filing.FilingException
{
String dispName = getDisplayName();
String extension = dispName.substring(dispName.lastIndexOf(".") + 1);
String mimeType = (String) mimeTypesSuffixTable.get(extension);
if (mimeType != null)
return mimeType;
else
return "text/plain";
}
/**
* Set the mime-type of this ByteStore.
*/
/**
* Check to see if the mime type passed is valid. If so, set the
* byte store mime type to it. Return the current mime type, which
* may be the old one if new one isn't valid.
*
* @author Salem Berhanu
*
* @return The current mime type of this byte store.
*/
public String updateMimeType(String mimeType) throws osid.filing.FilingException {
throw new osid.filing.FilingException (osid.filing.FilingException.UNIMPLEMENTED);
/* This needs work to make it compile.
String extension = (String) mimeTypesTable.get(mimeType);
System.out.println("extension :" +extension);
if(extension != null) {
FTPClient client = RemoteClient.getClient();
try {
String parentPath = ((RemoteCabinetEntry) getParent()).getIdString();
String oldPath = parentPath + "/" + this.displayname;
String newPath = parentPath + "/";
String newdisplayname = "";
if(this.displayname.lastIndexOf(".") != -1)
newdisplayname += this.displayname.substring(0, this.displayname.lastIndexOf(".") + 1) + extension;
else
newdisplayname += this.displayname + "." + extension;
newPath += newdisplayname;
System.out.println("new "+newPath+" old "+oldPath);
if(client.rename(oldPath,newPath))
{
String oldname = this.displayname;
this.displayname = newdisplayname;
this.parent.childRenameUpdate(this, oldname);
return mimeType;
}
else
throw new FilingException("FTPByteStore.updateMimeType: "+FilingException.IO_ERROR);
} catch (IOException e) {
throw new FilingException("FTPByteStore.updateMimeType: "+FilingException.IO_ERROR);
}
}
return getMimeType();
*/
}
/**
* Determine if this byte store is readable.
*
* @author Mark Norton
*
* @return True if this byte store is readable.
*/
public boolean isReadable() {
return readable;
}
/**
* Update this byte store to being read only.
*
* @author Mark Norton
*/
public void updateReadOnly() {
readable = true;
writable = false;
}
/**
* Determine if this byte store is writable.
* @author Mark Norton
*
* @return True if this byte store is writable.
*/
public boolean isWritable() {
return writable;
}
/**
* Force this byte store to be marked as writable.
*
* @author Mark Norton
*/
public void updateWritable() {
writable = true;
}
/**
* Determine if this byte store can be appeneded.
*
* @author Mark Norton
*
* @return True if this byte store can be appended.
*/
public boolean canAppend() {
return appendable;
}
/**
* Force this byte store to be marked as appendable.
*
* @author Mark Norton
*/
public void updateAppendOnly() {
appendable = true;
}
/**
* Get the current length of thsi byte store.
*
* @author Mark Norton
*
* @return The current length of this byte store.
*/
public long length() throws osid.filing.FilingException {
long length = 0;
try {
FTPClient client = rc.getClient();
client.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
// The file to open consists of the root base plus, path to current directory plus name.
//String fn = rc.getRootBase() + ((RemoteCabinet)getParent()).separator() + getDisplayName();
//String fn = rc.getRootBase() + "/" + getDisplayName();
String fn = getFullName();
//System.out.println("length - file name to open: " + fn);
FTPFile[] replies = client.listFiles(__fileListParser, fn);
System.out.println("File Name = "+fn+" replies ="+replies+"Client:"+client.getStatus());
if(replies == null) {
System.out.println(client.getReplyCode());
throw new osid.filing.FilingException("RemoteByteStore.length: "+osid.filing.FilingException.IO_ERROR);
}
//System.out.println(client.getReplyCode());
length = replies[0].getSize();
} catch (IOException e) {
throw new osid.filing.FilingException("RemoteByteStore.length: "+osid.filing.FilingException.IO_ERROR);
}
return length;
}
/**
* Return the number of bytes used in this byte store.
* Note that this method is not part of osid.filing.ByteStore.
*
* @author Mark Norton
*
* @return The number of bytes currently written in the buffer.
*/
public int used() {
return used;
}
/**
* Get the bytes in a file associated with this byte store.
*/
public byte[] getBytes() throws osid.filing.FilingException {
// Open the file for input access.
InputStream stream = null;
String fn = getFullName();
// Allocate a buffer to hold the file. Note that this must fit in memory and be
// small in size than Integer.MAX_VALUE.
long trueLen = length();
if (trueLen > (long) Integer.MAX_VALUE)
throw new osid.filing.FilingException("File is too big to read.");
int len = (int) trueLen;
byte[] buf = new byte[len];
//System.out.println ("getBytes - file name to retrieve: " + fn);
try {
FTPClient client = rc.getClient();
stream = client.retrieveFileStream(fn);
}
catch (java.io.IOException ex1) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
// Copy the file stream into a buffer.
try {
for (int i = 0; i < len; i++) {
buf[i] = (byte)stream.read();
}
stream.close();
}
catch (java.io.IOException ex) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
return buf;
}
/**
* Iterate over bytes given a version.
*
* @author Mark Norton
*
* @return A ByteValueIterator which lists all bytes saved in this byte store.
*/
public osid.shared.ByteValueIterator read(java.util.Calendar version) throws osid.filing.FilingException {
byte[] buf = getBytes();
osid.shared.ByteValueIterator it = (osid.shared.ByteValueIterator) new ByteValueIterator(buf);
return it;
}
/**
* Replace the byte_store of this object with the array of bytes passed.
* <p>
* Note that there is no indication that this should be an append operation
* versus an overwrite in the documentation. It is imlemented as overwrite here.
* <p>
* Note also that this assumes that the byte array passed is full of data (nothing unused).
* This is important to maintain the used total byte count.
*
* @author Mark Norton
*
*/
public void write(byte[] b) throws osid.filing.FilingException {
OutputStream stream = null;
String fn = getFullName();
try {
FTPClient client = rc.getClient();
stream = client.storeFileStream(fn);
}
catch (java.io.IOException ex1) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
// Copy the file stream into a buffer.
try {
for (int i = 0; i < b.length; i++) {
stream.write(b[i]);
}
stream.close();
}
catch (java.io.IOException ex) {
throw new osid.filing.FilingException(osid.filing.FilingException.IO_ERROR);
}
}
/**
* Append the byte passed to the byte store.
* <br>
* A clarification request has been made to SourceForge on this method. The
* value passed is declared as int, but documented as byte. Until this is
* cleared up, this method throws UNIMPLEMENTED.
*
* @author Mark Norton
*/
public void writeByte(int b) throws osid.filing.FilingException {
throw new osid.filing.FilingException (osid.filing.FilingException.UNIMPLEMENTED);
}
/**
* Write the bytes passed to the offset given. If this set of bytes would
* extend beyond the current size of the byte store, it is expanded. Note that
* len is redundant in this interface, as b.length should equal len.
* <p>
* Note also that any previous data in the range of off to off+len will be
* overwritten with the new bytes.
*
* @author Mark Norton
*/
public void writeBytesAtOffset(byte[] b, int off, int len) throws osid.filing.FilingException {
throw new osid.filing.FilingException (osid.filing.FilingException.UNIMPLEMENTED);
}
/**
* Get the full file name of this byte store, all the way from the absolute root.
*/
public String getFullName() {
StringBuffer fn = new StringBuffer("/");
ArrayList parts = new ArrayList(100);
// Walk path to root.
RemoteCabinet ptr = (RemoteCabinet) getParent();
while (ptr.getParent() != null) {
parts.add(0, ptr.getDisplayName());
ptr = (RemoteCabinet)ptr.getParent();
// System.out.println("GETTING FN: parent "+ptr.getDisplayName());
}
// 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());
return fn.toString();
}
public String getUrl() {
String url = "ftp://"+rc.getUserName()+":"+rc.getPassword()+"@"+ rc.getServerName() + this.getFullName();
//System.out.println("URL:"+ url);
return url;
}
}