package com.wilutions.jsfs;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import byps.BContentStreamWrapper;
import byps.BException;
import byps.BExceptionC;
import byps.RemoteException;
/**
* This class implements the JSFS Agent interface.
*/
public class FileSystemImpl extends BSkeleton_FileSystemService {
public FileSystemImpl(BClient_JSFS client, String yourWebapp) {
this.bclient = client;
this.yourWebapp = yourWebapp;
}
@Override
public List<FileInfo> findFiles(String path, FindOptions findOptions) throws RemoteException {
File file = getFile(path);
String name = file.getName();
name = name.replaceAll("\\.", "\\\\.");
name = name.replaceAll("\\*", ".*");
name = name.replaceAll("\\?", ".?");
File dir = file.getParentFile();
if (!dir.exists()) throw createBException(WindowsErrorCode.ERROR_PATH_NOT_FOUND, dir.getAbsolutePath());
File[] files = dir.listFiles();
ArrayList<FileInfo> fileInfos = new ArrayList<FileInfo>();
for (File f : files) {
if (f.getName().matches(name)) {
fileInfos.add(FileInfoHelper.makeFileInfo(f));
}
}
return fileInfos;
}
@Override
public String readAllText(String path) throws RemoteException {
return internalReadAllText(path, null);
}
public String internalReadAllText(String path, String defaultCharset) throws RemoteException {
File file = getExistingFile(path);
Reader rd = null;
try {
BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
fis.mark(3);
int byte1 = fis.read();
int byte2 = byte1 != -1 ? fis.read() : -1;
if (byte1 == 0xFE && byte2 == 0xFF) {
rd = new InputStreamReader(fis, "UTF-16BE");
}
else if (byte1 == 0xFF && byte2 == 0xFE) {
rd = new InputStreamReader(fis, "UTF-16LE");
}
else {
int byte3 = byte2 != -1 ? fis.read() : -1;
if (byte1 == 0xEF && byte2 == 0xBB && byte3 == 0xBF) {
rd = new InputStreamReader(fis, "UTF-8");
}
else {
fis.reset();
if (defaultCharset != null) {
rd = new InputStreamReader(fis, defaultCharset);
}
else {
rd = new InputStreamReader(fis);
}
}
}
char[] buf = new char[(int) file.length()];
int len = rd.read(buf);
return new String(buf, 0, len);
} catch (Throwable e) {
throw createBException(WindowsErrorCode.ERROR_INVALID_HANDLE, path + ", " + e.toString());
} finally {
if (rd != null) {
try {
rd.close();
} catch (Exception e) {
}
}
}
}
@Override
public void writeAllText(String path, String text) throws RemoteException {
internalWriteAllText(path, text, false);
}
private void internalWriteAllText(String path, String text, boolean utf8) throws RemoteException {
PrintWriter pr = null;
File file = getFile(path);
try {
FileOutputStream fos = new FileOutputStream(file);
Writer wr = null;
if (utf8) {
fos.write(0xEF);
fos.write(0xBB);
fos.write(0xBF);
wr = new OutputStreamWriter(fos, "UTF-8");
}
else {
wr = new OutputStreamWriter(fos);
}
pr = new PrintWriter(wr);
pr.print(text);
} catch (Throwable e) {
throw createBException(WindowsErrorCode.ERROR_INVALID_HANDLE, path + ", " + e.toString());
} finally {
if (pr != null) {
try {
pr.close();
} catch (Exception e) {
}
}
}
}
@Override
public void executeNotifyExit(final String[] args, ExecuteOptions opts) throws RemoteException {
internalExecuteNotifyExit(args, opts, opts != null);
}
public void internalExecuteNotifyExit(final String[] args, final ExecuteOptions execOpts, final boolean notify) throws RemoteException {
if (args == null || args.length == 0) throw createBException(WindowsErrorCode.ERROR_INVALID_PARAMETER, "args must not be null or empty");
final File file = getExistingFile(args[0]);
final BException[] rex = new BException[1];
Thread thread = new Thread() {
public void run() {
if (notify) {
final ExecuteNotifyInfo notifyInfo = new ExecuteNotifyInfo();
notifyInfo.setExtraInfo(execOpts.getExtraInfo());
ProcessBuilder pb = new ProcessBuilder(args);
try {
final Process process = pb.start();
final String stdin = execOpts.getStandardInput();
if (stdin != null && stdin.length() != 0) {
Thread thread = new Thread() {
public void run() {
Writer wr = null;
try {
wr = new OutputStreamWriter(process.getOutputStream());
wr.write(stdin);
} catch (Throwable ignored) {
} finally {
if (wr != null) {
try {
wr.close();
} catch (IOException ignored) {
}
}
}
}
};
thread.start();
}
if (execOpts.isCaptureOutput()) {
Thread thread = new Thread() {
public void run() {
StringBuilder sbuf = new StringBuilder();
InputStream is = process.getInputStream();
Reader rd = null;
try {
rd = new InputStreamReader(is);
char[] buf = new char[1000];
int len = -1;
while ((len = rd.read(buf)) != -1) {
sbuf.append(buf, 0, len);
}
} catch (Throwable ignroed) {
} finally {
if (rd != null) {
try {
rd.close();
} catch (IOException ignored) {
}
}
}
notifyInfo.setStandardOutput(sbuf.toString());
}
};
thread.start();
}
if (execOpts.isCaptureError()) {
Thread thread = new Thread() {
public void run() {
StringBuilder sbuf = new StringBuilder();
InputStream is = process.getErrorStream();
Reader rd = null;
try {
rd = new InputStreamReader(is);
char[] buf = new char[1000];
int len = -1;
while ((len = rd.read(buf)) != -1) {
sbuf.append(buf, 0, len);
}
} catch (Throwable ignroed) {
} finally {
if (rd != null) {
try {
rd.close();
} catch (IOException ignored) {
}
}
}
notifyInfo.setStandardError(sbuf.toString());
}
};
thread.start();
}
process.waitFor();
int exitCode = process.exitValue();
notifyInfo.setExitCode(exitCode);
} catch (Exception e) {
BException ex = createBException(WindowsErrorCode.ERROR_INVALID_HANDLE, args[0] + ", " + e.toString());
notifyInfo.setError(ex.toString());
}
try {
getNotifyService().notify(notifyInfo);
} catch (RemoteException e) {
}
}
else if (args.length == 1) {
try {
java.awt.Desktop.getDesktop().open(file);
} catch (Exception e) {
synchronized (rex) {
rex[0] = createBException(WindowsErrorCode.ERROR_INVALID_HANDLE, args[0] + ", " + e.toString());
}
}
}
else {
ProcessBuilder pb = new ProcessBuilder(args);
try {
pb.start();
} catch (Exception e) {
synchronized (rex) {
rex[0] = createBException(WindowsErrorCode.ERROR_INVALID_HANDLE, args[0] + ", " + e.toString());
}
}
}
}
};
thread.start();
if (!notify) {
try {
thread.join();
synchronized (rex) {
if (rex[0] != null) throw rex[0];
}
} catch (InterruptedException e) {
throw createBException(BExceptionC.CANCELLED, args[0] + ", " + e.toString());
}
}
}
@Override
public int beginWatchFolder(String dir, boolean recursive, String extraInfo) throws RemoteException {
try {
dir = FileInfoHelper.makeValidPath(dir);
int watchHandle = nextNotifyId.incrementAndGet();
WatchDir watcher;
watcher = new WatchDir(getNotifyService(), watchHandle, extraInfo, Paths.get(dir), recursive);
watcher.start();
watchers.put(watchHandle, watcher);
return watchHandle;
} catch (IOException e) {
throw new BException(BExceptionC.INTERNAL, "Failed to create directory watcher.", e);
}
}
@Override
public void endWatchFolder(int handle) throws RemoteException {
WatchDir watcher = watchers.get(handle);
if (watcher != null) {
watcher.done();
}
}
private BException createBException(int err, String details) {
return new BException(10000 + err, WindowsErrorCode.toString(err), details);
}
private File getExistingFile(String path) throws BException {
return internalGetExistingFile(path, true);
}
private File internalGetExistingFile(String path, boolean mustExist) throws BException {
File file = getFile(path);
if (file.exists()) {
if (!mustExist) throw createBException(WindowsErrorCode.ERROR_ALREADY_EXISTS, path);
}
else {
if (mustExist) {
boolean found = false;
File dir = file.getParentFile();
if (dir == null) {
String libpath = System.getProperty("java.library.path");
String[] dirs = libpath.split("\\" + File.pathSeparator);
for (String sdir : dirs) {
file = new File(new File(sdir), path);
found = file.exists();
if (found) break;
}
}
if (!found) {
throw createBException(WindowsErrorCode.ERROR_FILE_NOT_FOUND, path);
}
}
}
return file;
}
private File getFile(String path) throws BException {
path = FileInfoHelper.makeValidPath(path);
File file = new File(path);
return file;
}
private FileSystemNotify getNotifyService() throws RemoteException {
String token = ((JsfsAuthentication) bclient.getAuthentication()).getToken();
FileSystemNotify ns = bclient.dispatcherService.getNotifyService(token, false);
return ns;
}
@Override
public InputStream readFile(String path) throws RemoteException {
File file = internalGetExistingFile(path, true);
try {
BContentStreamWrapper cstream = new BContentStreamWrapper(file);
return cstream;
} catch (FileNotFoundException e) {
throw createBException(WindowsErrorCode.ERROR_FILE_NOT_FOUND, e.toString());
}
}
private static boolean isStringEmpty(String s) {
return s == null || s.isEmpty();
}
@Override
public void uploadFilesMultipartFormdata(FormItem[] items, String url, String method) throws RemoteException {
if (items == null) throw new BException(BExceptionC.INTERNAL, "Invalid parameter: items must not be null.");
if (items.length == 0) return;
if (url == null) throw new BException(BExceptionC.INTERNAL, "Invalid parameter: url must not be null.");
if (items[0] == null) throw new BException(BExceptionC.INTERNAL, "Invalid parameter: items[0] must not be null.");
// Relative URL?
if (!url.startsWith("http:")) {
url = yourWebapp + url;
}
if (isStringEmpty(method)) method = "POST";
try {
uploadMultipartFormdata(items, url, method);
}
catch (IOException e) {
throw new BException(BExceptionC.IOERROR, "Upload failed.", e);
}
}
@Override
public void uploadFile(String path, String url) throws RemoteException {
if (isStringEmpty(path)) throw new BException(BExceptionC.INTERNAL, "Invalid parameter: path must not be empty.");
if (isStringEmpty(url)) throw new BException(BExceptionC.INTERNAL, "Invalid parameter: url must not be empty.");
// Relative URL?
if (!url.startsWith("http:")) {
url = yourWebapp + url;
}
HttpURLConnection connection = null;
File binaryFile = new File(path);
OutputStream output = null;
InputStream input = null;
try {
connection = (HttpURLConnection)new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
connection.setRequestProperty("Content-Type: ", URLConnection.guessContentTypeFromName(binaryFile.getName()));
connection.setFixedLengthStreamingMode(binaryFile.length());
output = connection.getOutputStream();
input = new FileInputStream(binaryFile);
try {
byte[] buffer = new byte[1024];
int length = 0;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
output = null;
} finally {
try {
input.close();
} catch (IOException ignored) {
}
input = null;
}
int status = connection.getResponseCode();
if (status != HttpURLConnection.HTTP_OK){
throw new IOException("HTTP Status " + status);
}
try {
input = connection.getInputStream();
}
catch (IOException e) {
input = connection.getErrorStream();
}
while (input.read() != -1);
}
catch (IOException e) {
throw new BException(BExceptionC.IOERROR, "Failed to upload file.", e);
}
finally {
if (output != null) try { output.close(); } catch (IOException ignored) {}
if (connection != null) connection.disconnect();
}
}
private void uploadMultipartFormdata(FormItem[] items, String url, String method) throws IOException {
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
// http://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests
final long boundaryId = System.nanoTime();
final String boundary = Long.toHexString(boundaryId);
final String charset = "UTF-8";
HttpURLConnection connection = null;
PrintWriter writer = null;
InputStream input = null;
try {
connection = (HttpURLConnection)new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestMethod(method);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream output = connection.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
for (int itemIdx = 0; itemIdx < items.length; itemIdx++) {
final FormItem item = items[itemIdx];
if (item == null) continue;
if (isStringEmpty(item.type)) item.type = "text";
if (isStringEmpty(item.value)) item.value = "";
writer.append("--").append(boundary).append(CRLF);
if (item.type.equals("file")) {
String filePath = item.value;
File binaryFile = new File(filePath);
writeMultipartContentDisposition(writer, "form-data", item.name, binaryFile.getName());
writeMultipartFileContent(writer, output, binaryFile);
}
else {
writeMultipartContentDisposition(writer, "form-data", item.name, null);
writer.append(CRLF);
final String value = item.value;
writer.append(value).append(CRLF).flush();
}
} // for
//End of multipart/form-data.
writer.append("--" + boundary + "--").append(CRLF);
writer.flush();
writer.close();
writer = null;
try {
input = connection.getInputStream();
}
catch (Exception e) {
input = connection.getErrorStream();
}
while (input.read() != -1);
} catch (IOException e) {
throw e;
} finally {
if (writer != null) writer.close();
if (connection != null) connection.disconnect();
}
}
private void writeMultipartContentDisposition(PrintWriter writer, String type, String name, String fileName) {
writer.append("Content-Disposition: ").append(type);
if (name != null) {
writer.append("; name=\"").append(name).append("\"");
}
if (fileName != null) {
writer.append("; filename=\"").append(fileName).append("\"");
}
writer.append(CRLF);
}
private void writeMultipartFileContent(PrintWriter writer, OutputStream output, File binaryFile) throws FileNotFoundException, IOException {
InputStream input;
String contentType = URLConnection.guessContentTypeFromName(binaryFile.getName());
if (contentType == null) contentType = "application/octet-stream";
writer.append("Content-Type: ").append(contentType).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF).flush();
input = new FileInputStream(binaryFile);
try {
byte[] buffer = new byte[1024];
int length = 0;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
output.flush();
} finally {
try {
input.close();
} catch (IOException ignored) {
}
input = null;
}
writer.append(CRLF).flush();
}
private final BClient_JSFS bclient;
private final String yourWebapp;
private final ConcurrentHashMap<Integer, WatchDir> watchers = new ConcurrentHashMap<Integer, WatchDir>();
private final AtomicInteger nextNotifyId = new AtomicInteger();
private final static String CRLF = "\r\n";
}