/*
* Copyright 2012 Michael Chang, Tai-Lin Chu, Artin Menachekanian,
* Charles Rudolph, Eduard Sedakov, Suzanna Whiteside
*
* This file is part of ServerLibraryManager.
*
* ServerLibraryManager is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ServerLibraryManager is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ServerLibraryManager. If not, see <http://www.gnu.org/licenses/>.
*/
package edu.ucla.loni.server;
import edu.ucla.loni.client.FileService;
import edu.ucla.loni.shared.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.sql.Timestamp;
import java.util.ArrayList;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.jdom2.Document;
@SuppressWarnings("serial")
public class FileServiceImpl extends RemoteServiceServlet implements FileService {
////////////////////////////////////////////////////////////
// Private Functions
////////////////////////////////////////////////////////////
/**
* Returns returns an ArrayList of all the pipefiles in the root directory
* Note: this functions only search two levels in
*
* @param dir, file representing root directory
*/
private ArrayList<File> getAllPipefiles(File dir){
// Level == 2, dir == root directory
// Level == 1, dir == package folder
// Level == 0, dir == type folder (do not look go any deeper)
return getAllPipefilesRecursive(dir, 2);
}
/**
* Recursively get all pipefiles
*/
private ArrayList<File> getAllPipefilesRecursive(File dir, int level){
ArrayList<File> files = new ArrayList<File>();
if (level >= 0){
for (File file : dir.listFiles()){
if (file.isDirectory()){
files.addAll( getAllPipefilesRecursive(file, (level - 1)) );
}
else {
String name = file.getName();
if (name.endsWith(".pipe")){
files.add(file);
}
}
}
}
return files;
}
/**
* Remove files from the database in the case that they were deleted
*/
private void cleanDatabase(Directory root) throws Exception{
Pipefile[] pipes = Database.selectPipefiles(root.dirId);
if (pipes != null){
for(Pipefile pipe : pipes){
File file = new File(pipe.absolutePath);
if (!file.exists()){
Database.deletePipefile(pipe);
}
}
}
}
/**
* Update the database for this root
*/
private void updateDatabase(Directory root) throws Exception {
// Clean the database
cleanDatabase(root);
// Get all the files
File rootDir = new File(root.absolutePath);
ArrayList<File> files = getAllPipefiles(rootDir);
// For each pipefile
for (File file : files){
Timestamp db_lastModified = Database.selectPipefileLastModified(file.getAbsolutePath());
// Determine if the row needs to be updated or inserted
boolean update = false;
boolean insert = false;
Timestamp fs_lastModified = new Timestamp(file.lastModified());
if (db_lastModified != null){
// If file has been modified
if ( !db_lastModified.equals(fs_lastModified) ){
update = true;
}
}
else {
insert = true;
}
// If we need to update or insert a row
if (update || insert){
Pipefile pipe = ServerUtils.parseXML(file);
pipe.lastModified = fs_lastModified;
if (insert){
Database.insertPipefile(root.dirId, pipe);
} else {
pipe.fileId = Database.selectPipefileId(file.getAbsolutePath());
Database.updatePipefile(pipe);
}
}
}
}
private void updateMonitorAndAccessFile(Directory root) throws Exception{
ServerUtils.touchMonitorFile(root);
ServerUtils.writeAccessFile(root);
}
/**
* Removes a file from the server
* @param filename absolute path of the file
*/
private void removeFile(Directory root, Pipefile pipe) throws Exception {
File f = new File(pipe.absolutePath);
if (f.exists()){
// Delete file on file-system
boolean success = f.delete();
if (!success){
throw new Exception("Failed to remove file " + pipe.absolutePath);
}
// Remove parent directory if it is empty
ServerUtils.removeEmptyDirectory(f.getParentFile());
// Delete file from database
Database.deletePipefile(pipe);
}
}
/**
* Copy a file from the server to the proper package
* @param filename absolute path of the file
* @param packageName absolute path of the package
* @returns fileId of pipefile in its new location
*/
private int copyFile(Directory root, Pipefile pipe, String packageName) throws Exception {
return copyOrMoveFile(root, pipe, packageName, true);
}
/**
* Move a file to another package
* @param filename absolute path of the file = source path of file
* @param packageName is the name of the package as it appears in the Database in column PACKAGENAME
* @throws Exception
* @returns fileId of pipefile in its new location
*/
public int moveFile(Directory root, Pipefile pipe, String packageName) throws Exception{
return copyOrMoveFile(root, pipe, packageName, false);
}
private int copyOrMoveFile(Directory root, Pipefile pipe, String packageName, boolean copy) throws Exception{
// Source
String oldAbsolutePath = pipe.absolutePath;
File src = new File(oldAbsolutePath);
// If the source does not exist
if (!src.exists()) {
throw new Exception("Source file does not exist");
}
// Destination
String destPath = ServerUtils.newAbsolutePath(root.absolutePath, packageName, pipe.type, pipe.name);
File dest = new File(destPath);
// Update the name if the filename had to be saved as something else
String filename = dest.getName().replaceAll(".pipe", "").replace("_", " ");
if (!pipe.name.equals(filename)){
pipe.name = filename;
}
// If the destination directory does not exist, create it and necessary parent directories
File destDir = dest.getParentFile();
if (!destDir.exists()){
boolean success = destDir.mkdirs();
if (!success){
throw new Exception("Destination folders could not be created");
}
}
// Copy or Move the file
if (copy){
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dest);
int length = 0;
byte[] buffer = new byte[8192];
while ((length = in.read(buffer)) != -1){
out.write(buffer, 0, length);
}
in.close();
out.flush();
out.close();
}
else {
boolean success = src.renameTo(dest);
if(!success) {
throw new Exception("File could not be moved");
}
}
// Remove parent directory if it is empty
ServerUtils.removeEmptyDirectory(src.getParentFile());
// Update Pipefile
pipe.packageName = packageName;
pipe.absolutePath = destPath;
// Update XML
Document doc = ServerUtils.readXML(dest);
doc = ServerUtils.updateXML(doc, pipe);
ServerUtils.writeXML(dest, doc);
// Update Database
pipe.lastModified = new Timestamp(dest.lastModified());
if (copy) {
Database.insertPipefile(root.dirId, pipe);
return Database.selectPipefileId(pipe.absolutePath);
}
else {
Database.updatePipefile(pipe);
return pipe.fileId;
}
}
////////////////////////////////////////////////////////////
// Public Functions
////////////////////////////////////////////////////////////
/**
* Returns the directory object for the absolutePath
*/
public Directory getDirectory(String absolutePath) throws Exception {
try {
File rootDir = new File(absolutePath);
if (rootDir.exists() && rootDir.isDirectory()){
Directory root = new Directory();
root.absolutePath = rootDir.getAbsolutePath();
Timestamp monitorModified = ServerUtils.getMonitorFileModified(root);
Timestamp accessModified = ServerUtils.getAccessFileModified(root);
// Insert if needed
root = Database.selectDirectory(rootDir.getAbsolutePath());
boolean inserted = false;
if (root == null){
Database.insertDirectory(rootDir.getAbsolutePath(), monitorModified, accessModified);
root = Database.selectDirectory(rootDir.getAbsolutePath());
inserted = true;
}
// Update the file in the database
if (inserted || monitorModified == null || !monitorModified.equals(root.monitorModified)){
root.monitorModified = monitorModified;
Database.updateDirectory(root);
updateDatabase(root);
}
// Update access
if (accessModified != null && (inserted || !accessModified.equals(root.accessModified))){
ServerUtils.readAccessFile(root);
}
return root;
}
else {
return null;
}
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Returns an array of all the pipefiles
*/
public Pipefile[] getFiles(Directory root) throws Exception {
try {
return Database.selectPipefiles(root.dirId);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Returns an array of all the pipefiles that match the query
*/
public Pipefile[] getSearchResults(Directory root, String query) throws Exception{
try {
return Database.selectPipefilesSearch(root.dirId, query);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Updates the file on the server
*/
public void updateFile(Directory root, Pipefile pipe) throws Exception{
try {
File file = new File(pipe.absolutePath);
// Update the XML
Document doc = ServerUtils.readXML(file);
doc = ServerUtils.updateXML(doc, pipe);
ServerUtils.writeXML(file, doc);
// Update the filename if the name changed
if (pipe.nameUpdated || pipe.packageUpdated){
String destPath = ServerUtils.newAbsolutePath(root.absolutePath, pipe.packageName, pipe.type, pipe.name);
File dest = new File(destPath);
// Update the name if the filename had to be saved as something else
String filename = dest.getName().replaceAll(".pipe", "").replace("_", " ");
if (!pipe.name.equals(filename)){
pipe.name = filename;
}
// Create parent folders if needed
File destDir = dest.getParentFile();
if (!destDir.exists()){
boolean success = destDir.mkdirs();
if (!success){
throw new Exception("Destination folders could not be created");
}
}
// Move the file
boolean success = file.renameTo(dest);
if(!success) {
throw new Exception("Failed to rename file");
}
// Remove parent directory if it is empty
ServerUtils.removeEmptyDirectory(file.getParentFile());
// Update file and absolutePath
file = dest;
pipe.absolutePath = destPath;
}
// Update the database
pipe.lastModified = new Timestamp(file.lastModified());
Database.updatePipefile(pipe);
// Update monitor and access files
updateMonitorAndAccessFile(root);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Removes files from the server
*/
public void removeFiles(Directory root, Pipefile[] pipes) throws Exception {
try {
if (pipes.length == 0){
return;
}
// Remove each file
for (Pipefile pipe : pipes) {
removeFile(root, pipe);
}
// Update monitor and access files
updateMonitorAndAccessFile(root);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Copies files from the server to the proper package
*/
public int[] copyFiles(Directory root, Pipefile[] pipes, String packageName) throws Exception {
try {
if (pipes.length == 0){
return null;
}
int[] ret = new int[pipes.length];
int index = 0;
// Copy each file
for (Pipefile pipe : pipes) {
ret[index] = copyFile(root, pipe, packageName);
index++;
}
// Update monitor and access files
updateMonitorAndAccessFile(root);
return ret;
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Moves files from the server to the proper package
*/
public int[] moveFiles(Directory root, Pipefile[] pipes, String packageName) throws Exception{
try {
if (pipes.length == 0){
return null;
}
int[] ret = new int[pipes.length];
int index = 0;
// Move each file
for (Pipefile pipe : pipes) {
ret[index] = moveFile(root, pipe, packageName);
index++;
}
// Update monitor and access files
updateMonitorAndAccessFile(root);
return ret;
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Returns an array of all the groups
*/
public Group[] getGroups(Directory root) throws Exception {
try {
return Database.selectGroups(root.dirId);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Inserts or Updates a group on the server
*/
public void updateGroup(Directory root, Group group) throws Exception{
try {
// Insert or update the group
if (group.groupId == -1){
Database.insertGroup(root.dirId, group);
} else {
Database.updateGroup(group);
}
// Write the access file
ServerUtils.writeAccessFile(root);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
/**
* Deletes groups on the server
*/
public void removeGroups(Directory root, Group[] groups) throws Exception{
try {
// Delete each group
for (Group group: groups){
Database.deleteGroup(group);
}
// Write the access file
ServerUtils.writeAccessFile(root);
}
catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
}