public abstract class ConnectionPool {

	public abstract Connection getConnection() throws SQLException;

} public class SQLAPIHandler extends Handler implements APIHandler {

    private static final Logger logger = Logger.getLogger(SQLAPIHandler.class.getName());
    private Device apiDevice = new Device(Constants.API_DEVICE_ID);

    public SQLAPIHandler(ConnectionPool pool) throws SQLException,
            NoStorageManagerAvailable {
        super(pool);
    }

    @Override
    public APIGetMetadata getMetadata(User user, Long fileId,
            Boolean includeChunks, Long version, Boolean isFolder) {

        ItemMetadata responseObject = null;
        Integer errorCode = 0;
        Boolean success = false;
        String description = "";

        try {

            if (fileId == null) {
                // retrieve metadata from the root folder
                responseObject = this.itemDao.findByUserId(user.getId(), false);
            } else {

                // check if user has permission on this file
                List<User> users = this.userDao.findByItemId(fileId);

                if (users.isEmpty()) {
                    throw new DAOException(DAOError.FILE_NOT_FOUND);
                }

                if (!userHasPermission(user, users)) {
                    throw new DAOException(DAOError.USER_NOT_AUTHORIZED);
                }

                responseObject = this.itemDao.findById(fileId, false, version,
                        false, includeChunks);
            }

            if (responseObject.isFolder() != isFolder) {
                throw new DAOException(DAOError.FILE_NOT_FOUND);
            }

            success = true;

        } catch (DAOException e) {
            description = e.getError().getMessage();
            errorCode = e.getError().getCode();
            logger.error(e.toString(), e);
        }

        APIGetMetadata response = new APIGetMetadata(responseObject, success,
                errorCode, description);
        return response;
    }

    public APIGetMetadata getFolderContent(User user, Long folderId,
            Boolean includeDeleted) {

        ItemMetadata responseObject = null;
        Integer errorCode = 0;
        Boolean success = false;
        String description = "";

        try {

            if (folderId == null) {
                // retrieve metadata from the root folder
                responseObject = this.itemDao.findByUserId(user.getId(),
                        includeDeleted);
            } else {

                // check if user has permission on this file
                List<User> users = this.userDao.findByItemId(folderId);

                if (users.isEmpty()) {
                    throw new DAOException(DAOError.FILE_NOT_FOUND);
                }

                if (!userHasPermission(user, users)) {
                    throw new DAOException(DAOError.USER_NOT_AUTHORIZED);
                }

                responseObject = this.itemDao.findById(folderId, true, null,
                        includeDeleted, false);
            }

            success = true;

        } catch (DAOException e) {
            description = e.getError().getMessage();
            errorCode = e.getError().getCode();
            logger.error(e.toString(), e);
        }

        APIGetMetadata response = new APIGetMetadata(responseObject, success,
                errorCode, description);
        return response;
    }

    @Override
    public APICommitResponse createFile(User user, ItemMetadata fileToSave) {

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APICommitResponse(fileToSave, false, 404,
                    "User not found.");
        }

        // Get user workspaces
        try {
            List<Workspace> workspaces = workspaceDAO.getByUserId(user.getId());
            user.setWorkspaces(workspaces);
        } catch (DAOException e) {
            logger.error(e);
            return new APICommitResponse(fileToSave, false, 404,
                    "No workspaces found for the user.");
        }

        boolean includeList = true;
        Long version = null;
        boolean includeDeleted = false;
        boolean includeChunks = false;

        // check that the given parent ID exists
        ItemMetadata parent;
        if (fileToSave.getParentId() != null) {
            try {
                parent = itemDao.findById(fileToSave.getParentId(),
                        includeList, version, includeDeleted, includeChunks);
                fileToSave.setParentVersion(parent.getVersion());

                // check if parent is a folder
                if (!parent.isFolder()) {
                    return new APICommitResponse(fileToSave, false, 400,
                            "Parent must be a folder, not a file.");
                }

            } catch (DAOException e) {
                return new APICommitResponse(fileToSave, false, 404,
                        "Parent folder not found");
            }
        } else {
            try {
                parent = this.itemDao
                        .findByUserId(user.getId(), includeDeleted);
                Workspace parentWorkspace = workspaceDAO
                        .getDefaultWorkspaceByUserId(user.getId());
                parent.setWorkspaceId(parentWorkspace.getId());
            } catch (DAOException e) {
                return new APICommitResponse(fileToSave, false, e.getError()
                        .getCode(), e.getMessage());
            }
        }

        // check if the user has permission on the file and parent
        boolean permissionParent = false;
        for (Workspace w : user.getWorkspaces()) {
            if (parent.isRoot() || w.getId().equals(parent.getWorkspaceId())) {
                permissionParent = true;
                break;
            }
        }
        if (!permissionParent) {
            return new APICommitResponse(fileToSave, false, 403,
                    "You are not allowed to modify this file");
        }

        // check if there is already a file with the same name
        boolean repeated = false;
        for (ItemMetadata child : parent.getChildren()) {
            if (child.getFilename().equals(fileToSave.getFilename())) {
                repeated = true;
                break;
            }
        }
        if (repeated) {
            return new APICommitResponse(fileToSave, false, 400,
                    "This name is already used in the same folder. Please use a different one. ");
        }

        APICommitResponse responseAPI;

        try {
            CommitNotification commitResult = saveNewItemAPI(user, fileToSave, parent);
            responseAPI = new APICommitResponse(fileToSave, true, 0, "");
            responseAPI.setQuotaLimit(commitResult.getLimitQuota());
            responseAPI.setQuotaUsed(commitResult.getUsedQuota());
        } catch (Exception e) {
            logger.error(e);
            responseAPI = new APICommitResponse(fileToSave, false, 500,
                    e.toString());
        }

        return responseAPI;
    }

    @Override
    public APICommitResponse updateData(User user, ItemMetadata fileToUpdate) {

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APICommitResponse(fileToUpdate, false, 404,
                    "User not found.");
        }

        // Get user workspaces
        try {
            List<Workspace> workspaces = workspaceDAO.getByUserId(user.getId());
            user.setWorkspaces(workspaces);
        } catch (DAOException e) {
            logger.error(e);
            return new APICommitResponse(fileToUpdate, false, 404,
                    "No workspaces found for the user.");
        }

        boolean includeList = true;
        Long version = null;
        boolean includeDeleted = false;
        boolean includeChunks = false;

        // check that the given file ID exists
        ItemMetadata file;
        try {
            file = itemDao.findById(fileToUpdate.getId(), includeList, version,
                    includeDeleted, includeChunks);
        } catch (DAOException e) {
            return new APICommitResponse(fileToUpdate, false, 404,
                    "File not found");
        }

        // check if the user has permission on the file and parent
        boolean permission = false;
        for (Workspace w : user.getWorkspaces()) {
            if (w.getId().equals(file.getWorkspaceId())) {
                permission = true;
            }
        }
        if (!permission) {
            return new APICommitResponse(fileToUpdate, false, 403,
                    "You are not allowed to modify this file");
        }

        // update file attributes

        file.setMimetype(fileToUpdate.getMimetype());
        file.setChecksum(fileToUpdate.getChecksum());
        file.setSize(fileToUpdate.getSize());
        file.setChunks(fileToUpdate.getChunks());
        file.setVersion(file.getVersion() + 1L);
        file.setModifiedAt(new Date());
        file.setStatus(Status.CHANGED.toString());

        // Commit the file
        List<ItemMetadata> items = new ArrayList<ItemMetadata>();
        items.add(file);

        Workspace workspace = new Workspace(file.getWorkspaceId());

        APICommitResponse responseAPI;
        try {
            CommitNotification commitResult = this.doCommit(user, workspace, apiDevice, items);
            responseAPI = new APICommitResponse(file, true, 0, "");
            responseAPI.setQuotaLimit(commitResult.getLimitQuota());
            responseAPI.setQuotaUsed(commitResult.getUsedQuota());
        } catch (Exception e) {
            logger.error(e);
            responseAPI = new APICommitResponse(file, false, 500, e.toString());
        }

        return responseAPI;
    }

    @Override
    public APICommitResponse updateMetadata(User user, ItemMetadata fileToUpdate, Boolean parentUpdated) {

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APICommitResponse(fileToUpdate, false, 404,
                    "User not found.");
        }

        // Get user workspaces
        try {
            List<Workspace> workspaces = workspaceDAO.getByUserId(user.getId());
            user.setWorkspaces(workspaces);
        } catch (DAOException e) {
            logger.error(e);
            return new APICommitResponse(fileToUpdate, false, 404,
                    "No workspaces found for the user.");
        }

        boolean includeList = true;
        Long version = null;
        boolean includeDeleted = false;
        boolean includeChunks = true;

        // check that the given file ID exists
        ItemMetadata file;
        try {
            file = itemDao.findById(fileToUpdate.getId(), includeList, version,
                    includeDeleted, includeChunks);
        } catch (DAOException e) {
            return new APICommitResponse(fileToUpdate, false, 404,
                    "File not found");
        }

        // check that the given parent ID exists
        ItemMetadata parent;
        if (fileToUpdate.getParentId() != null) {
            try {
                parent = itemDao.findById(fileToUpdate.getParentId(),
                        includeList, version, includeDeleted, includeChunks);

                // check if parent is a folder
                if (!parent.isFolder()) {
                    return new APICommitResponse(fileToUpdate, false, 400, "Parent must be a folder, not a file.");
                }

            } catch (DAOException e) {
                return new APICommitResponse(fileToUpdate, false, 404, "Parent folder not found");
            }
        } else {
            try {
                parent = this.itemDao.findByUserId(user.getId(), includeDeleted);
            } catch (DAOException e) {
                return new APICommitResponse(fileToUpdate, false, e.getError().getCode(), e.getMessage());
            }
        }

        // check if the user has permission on the file and parent
        boolean permissionFile = false;
        boolean permissionParent = false;
        for (Workspace w : user.getWorkspaces()) {
            if (w.getId().equals(file.getWorkspaceId())) {
                permissionFile = true;
            }
            if (parent.isRoot() || w.getId().equals(parent.getWorkspaceId())) {
                permissionParent = true;
            }
        }
        if (!permissionFile || !permissionParent) {
            return new APICommitResponse(fileToUpdate, false, 403,
                    "You are not allowed to modify this file");
        }

        // check if there is already a file with the same name
        boolean repeated = false;
        for (ItemMetadata child : parent.getChildren()) {
            if (child.getFilename().equals(fileToUpdate.getFilename())) {
                repeated = true;
            }
        }
        if (repeated) {
            return new APICommitResponse(fileToUpdate, false, 400,
                    "This name is already used in the same folder. Please use a different one. ");
        }

        // update file attributes
        if (fileToUpdate.getFilename() != null) {
            file.setFilename(fileToUpdate.getFilename());
        }
        if (parentUpdated) {
            file.setParentId(parent.getId());
            file.setParentVersion(parent.getVersion());
        }
        file.setVersion(file.getVersion() + 1L);
        file.setModifiedAt(new Date());
        file.setStatus(Status.RENAMED.toString());

        // Commit the file
        List<ItemMetadata> items = new ArrayList<ItemMetadata>();
        items.add(file);

        Workspace workspace = new Workspace(file.getWorkspaceId());
        
        APICommitResponse responseAPI;
        try {
            CommitNotification commitResult = this.doCommit(user, workspace, apiDevice, items);
            responseAPI = new APICommitResponse(file, true, 0, "");
            responseAPI.setQuotaLimit(commitResult.getLimitQuota());
            responseAPI.setQuotaUsed(commitResult.getUsedQuota());
        } catch (Exception e) {
            logger.error(e);
            responseAPI = new APICommitResponse(file, false, 500, e.toString());
        }

        return responseAPI;
    }

    @Override
    public APICreateFolderResponse createFolder(User user, ItemMetadata item) {

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            APICreateFolderResponse response = new APICreateFolderResponse(
                    item, false, 404, "User not found.");
            return response;
        }

        // get metadata of the parent item
        APIGetMetadata parentResponse = this.getFolderContent(user,
                item.getParentId(), false);
        ItemMetadata parentMetadata = parentResponse.getItemMetadata();

        // if it is the root, get the default workspace
        if (parentMetadata.isRoot()) {

            try {
                Workspace workspace = workspaceDAO
                        .getDefaultWorkspaceByUserId(user.getId());
                parentMetadata.setWorkspaceId(workspace.getId());
            } catch (DAOException e) {
                logger.error(e);
                APICreateFolderResponse response = new APICreateFolderResponse(
                        item, false, 404, "Workspace not found.");
                return response;
            }
        } else {

            if (!parentMetadata.isFolder()) {
                return new APICreateFolderResponse(item, false, 400,
                        "Parent must be a folder, not a file.");
            }

            item.setParentVersion(parentMetadata.getVersion());
        }

        String folderName = item.getFilename();
        List<ItemMetadata> files = parentMetadata.getChildren();

        // check if there exists a folder with the same name
        ItemMetadata object = null;
        for (ItemMetadata file : files) {
            if (file.getFilename().equals(folderName)
                    && !file.getStatus().equals("DELETED")) {
                object = file;
                break;
            }
        }

        if (object != null) {
            APICreateFolderResponse response = new APICreateFolderResponse(
                    object, false, 400, "Folder already exists.");
            return response;
        }

        APICreateFolderResponse responseAPI = this.createNewFolder(user, item, parentMetadata);
        
        return responseAPI;
    }

    @Override
    public APIRestoreMetadata restoreMetadata(User user, ItemMetadata item) {
        try {

            Item serverItem = itemDao.findById(item.getId());
            ItemMetadata lastObjectVersion = itemDao.findById(item.getId(),
                    false, null, false, false);
            if (serverItem != null && lastObjectVersion != null) {

                ItemMetadata metadata = itemVersionDao.findByItemIdAndVersion(
                        serverItem.getId(), item.getVersion());

                ItemVersion restoredObject = new ItemVersion(metadata);

                if (restoredObject.getStatus().compareTo(
                        Status.DELETED.toString()) != 0) {
                    restoredObject
                            .setVersion(lastObjectVersion.getVersion() + 1);
                    restoredObject.setStatus(Status.CHANGED.toString());

                    // save restoredObject
                    itemVersionDao.add(restoredObject);

                    List<String> chunks = new ArrayList<String>();
                    // If no folder, create new chunks
                    if (!restoredObject.getChunks().isEmpty()) {
                        for (Chunk chunk : restoredObject.getChunks()) {
                            chunks.add(chunk.getClientChunkName());
                        }
                        this.createChunks(chunks, restoredObject);
                    }

                    serverItem.setLatestVersion(restoredObject.getVersion());
                    itemDao.put(serverItem);

                    item.setChecksum(restoredObject.getChecksum());
                    item.setChunks(chunks);
                    item.setModifiedAt(restoredObject.getModifiedAt());
                    item.setDeviceId(restoredObject.getDevice().getId());
                    item.setFilename(restoredObject.getItem().getFilename());
                    item.setSize(restoredObject.getSize());

                    item.setIsFolder(serverItem.isFolder());
                    item.setMimetype(serverItem.getMimetype());

                    item.setParentVersion(serverItem
                            .getClientParentFileVersion());

                    item.setStatus(restoredObject.getStatus());
                    item.setVersion(restoredObject.getVersion());

                    APIRestoreMetadata response = new APIRestoreMetadata(item,
                            true, 200, "");
                    return response;
                } else {
                    APIRestoreMetadata response = new APIRestoreMetadata(item,
                            false, 400, "File not found.");
                    return response;
                }
            } else {
                APIRestoreMetadata response = new APIRestoreMetadata(item,
                        false, 400, "File not found.");
                return response;
            }
        } catch (DAOException e) {
            APIRestoreMetadata response = new APIRestoreMetadata(item, false,
                    400, e.getMessage());
            return response;
        }
    }

    @Override
    public APIDeleteResponse deleteItem(User user, ItemMetadata item) {
        List<ItemMetadata> filesToDelete;

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APIDeleteResponse(null, false, 404, "User not found.");
        }

        // Get user workspaces
        try {
            List<Workspace> workspaces = workspaceDAO.getByUserId(user.getId());
            user.setWorkspaces(workspaces);
        } catch (DAOException e) {
            logger.error(e);
            return new APIDeleteResponse(null, false, 404,
                    "No workspaces found for the user.");
        }

        // check that the given file ID exists
        try {
            filesToDelete = itemDao.getItemsById(item.getId());
        } catch (DAOException e) {
            return new APIDeleteResponse(null, false, 404,
                    "File or folder not found");
        }
        if (filesToDelete.isEmpty()) {
            return new APIDeleteResponse(null, false, 404,
                    "File or folder not found.");
        }

        // check if it's a file or a folder
        if (filesToDelete.get(0).isFolder() != item.isFolder()) {
            return new APIDeleteResponse(null, false, 400,
                    "Type missmatch (file and folder)");
        }

        // check if the user has permission on the file and parent
        boolean permission = false;
        for (Workspace w : user.getWorkspaces()) {
            if (w.getId().equals(filesToDelete.get(0).getWorkspaceId())) {
                permission = true;
            }
        }
        if (!permission) {
            return new APIDeleteResponse(null, false, 403,
                    "You are not allowed to deleted this file");
        }

        Workspace workspace = new Workspace(filesToDelete.get(0)
                .getWorkspaceId());

        APIDeleteResponse response;
        try {
            response = deleteItemsAPI(user, workspace, filesToDelete);
        } catch (DAOException e) {
            logger.error(e.toString(), e);
            response = new APIDeleteResponse(null, false, e.getError()
                    .getCode(), e.getMessage());
        }

        return response;
    }

    @Override
    public APIGetVersions getVersions(User user, ItemMetadata item) {
        ItemMetadata serverItem;

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetVersions(null, false, 404, "User not found.");
        }

        // Get user workspaces
        try {
            List<Workspace> workspaces = workspaceDAO.getByUserId(user.getId());
            user.setWorkspaces(workspaces);
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetVersions(null, false, 404,
                    "No workspaces found for the user.");
        }

        // check that the given file ID exists
        try {
            serverItem = itemDao.findItemVersionsById(item.getId());
        } catch (DAOException e) {
            return new APIGetVersions(null, false, 404,
                    "File or folder not found");
        }

        // check if it's a file or a folder
        if (serverItem.isFolder()) {
            return new APIGetVersions(null, false, 400,
                    "Incorrect file type. Must be a file, not a folder.");
        }

        APIGetVersions response = new APIGetVersions(serverItem, true, 0, "");
        return response;
    }

    @Override
    public APIShareFolderResponse shareFolder(User user, Item item,
            List<String> emails) {

        APIShareFolderResponse response;

        Workspace workspace;
        try {
            workspace = this.doShareFolder(user, emails, item, false);
            response = new APIShareFolderResponse(workspace, true, 0, "");
        } catch (ShareProposalNotCreatedException e) {
            response = new APIShareFolderResponse(null, false, 400,
                    e.getMessage());
        } catch (UserNotFoundException e) {
            response = new APIShareFolderResponse(null, false, 404,
                    e.getMessage());
        }

        return response;
    }

    @Override
    public APIUnshareFolderResponse unshareFolder(User user, Item item,
            List<String> emails) {

        APIUnshareFolderResponse response;
        UnshareData infoUnshare;

        try {
            infoUnshare = this.doUnshareFolder(user, emails, item, false);
            response = new APIUnshareFolderResponse(infoUnshare.getWorkspace(),
                    infoUnshare.getUsersToRemove(), infoUnshare.isUnshared(),
                    true, 0, "");
        } catch (ShareProposalNotCreatedException e) {
            response = new APIUnshareFolderResponse(null, null, false, false, 400,
                    e.getMessage());
        } catch (UserNotFoundException e) {
            response = new APIUnshareFolderResponse(null, null, false, false, 404,
                    e.getMessage());
        }

        return response;
    }

    @Override
    public APIGetFolderMembersResponse getFolderMembers(User user, Item item) {

        APIGetFolderMembersResponse response;

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (NoResultReturnedDAOException e) {
            logger.warn(e);
            return new APIGetFolderMembersResponse(null, false, 404,
                    e.toString());
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetFolderMembersResponse(null, false, 500,
                    e.toString());
        }

        // Get folder metadata
        try {
            item = itemDao.findById(item.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetFolderMembersResponse(null, false, 500,
                    e.toString());
        }

        if (item == null || !item.isFolder()) {
            return new APIGetFolderMembersResponse(null, false, 404,
                    "No folder found with the given ID.");
        }

        List<UserWorkspace> members;
        try {
            members = this.doGetWorkspaceMembers(user, item.getWorkspace());
        } catch (InternalServerError e) {
            return new APIGetFolderMembersResponse(null, false, 500,
                    e.toString());
        }

        response = new APIGetFolderMembersResponse(members, true, 0, "");

        return response;
    }

    @Override
    public APIGetWorkspaceInfoResponse getWorkspaceInfo(User user,
            ItemMetadata item) {

        // Check the owner
        try {
            user = userDao.findById(user.getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetWorkspaceInfoResponse(null, false, 404,
                    "User not found.");
        }

        // Get user workspaces
        try {
            List<Workspace> workspaces = workspaceDAO.getByUserId(user.getId());
            user.setWorkspaces(workspaces);
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetWorkspaceInfoResponse(null, false, 404,
                    "No workspaces found for the user.");
        }

        // get the workspace

        Workspace workspace;
        if (item.getId() == null) {
            try {
                workspace = workspaceDAO.getDefaultWorkspaceByUserId(user
                        .getId());
            } catch (DAOException e) {
                return new APIGetWorkspaceInfoResponse(null, false, 404,
                        "Workspace not found");
            }
        } else {
            try {
                workspace = workspaceDAO.getByItemId(item.getId());
            } catch (DAOException e) {
                return new APIGetWorkspaceInfoResponse(null,false, 404,
                        "Workspace not found");
            }
        }

        // check if the user has permission on the file and parent
        boolean permission = false;
        for (Workspace w : user.getWorkspaces()) {
            if (item.getId() == null || w.getId().equals(workspace.getId())) {
                permission = true;
                break;
            }
        }
        if (!permission) {
            return new APIGetWorkspaceInfoResponse(null, false, 403,
                    "You are not allowed to access this file");
        }
        
        User workspaceOwner;
        try {
        	workspaceOwner = userDao.findById(workspace.getOwner().getId());
        } catch (DAOException e) {
            logger.error(e);
            return new APIGetWorkspaceInfoResponse(null, false, 404,
                    "User not found.");
        }
        workspace.setOwner(workspaceOwner);
        APIGetWorkspaceInfoResponse response = new APIGetWorkspaceInfoResponse(
                workspace,  true, 0, "");
        return response;
    }

    private boolean userHasPermission(User user, List<User> users) {
        boolean hasPermission = false;
        for (User u : users) {
            if (u.getId().equals(user.getId())) {
                hasPermission = true;
                break;
            }
        }
        return hasPermission;
    }

    private CommitNotification saveNewItemAPI(User user, ItemMetadata itemToSave,
            ItemMetadata parent) throws DAOException {

        itemToSave.setWorkspaceId(parent.getWorkspaceId());
        Workspace workspace = new Workspace(parent.getWorkspaceId());

        List<ItemMetadata> objects = new ArrayList<ItemMetadata>();
        objects.add(itemToSave);

        return this.doCommit(user, workspace, apiDevice, objects);

    }

    private APICreateFolderResponse createNewFolder(User user, ItemMetadata item, ItemMetadata parent) {

        // Create metadata

        if (!parent.isRoot()) {
            item.setParentId(parent.getId());
            item.setParentVersion(parent.getVersion());
        }

        item.setVersion(1L);
        item.setWorkspaceId(parent.getWorkspaceId());
        item.setStatus("NEW");
        item.setSize(0L);
        item.setIsFolder(true);
        item.setMimetype("inode/directory");
        item.setModifiedAt(new Date());
        item.setDeviceId(Constants.API_DEVICE_ID);
        item.setChecksum(0L);

        List<ItemMetadata> items = new ArrayList<ItemMetadata>();
        items.add(item);

        Workspace workspace = new Workspace(item.getWorkspaceId());

        CommitNotification commitResult;
        try {
            commitResult = this.doCommit(user, workspace, apiDevice, items);
        } catch (DAOException e) {
            logger.error(e);
            return new APICreateFolderResponse(item, false, 500, "Item could not be committed.");
        }
        
        APICreateFolderResponse responseAPI;
        if (!commitResult.getItems().get(0).isCommitSucceed()) {
            responseAPI = new APICreateFolderResponse(item, false, 500, "Item could not be committed.");
            return responseAPI;
        }

        APICreateFolderResponse response = new APICreateFolderResponse(item, true, 0, "");
        response.setQuotaLimit(commitResult.getLimitQuota());
        response.setQuotaUsed(commitResult.getUsedQuota());
        return response;
    }

    private void createChunks(List<String> chunksString,
            ItemVersion objectVersion) throws IllegalArgumentException,
            DAOException {

        if (chunksString.size() > 0) {
            List<Chunk> chunks = new ArrayList<Chunk>();
            int i = 0;

            for (String chunkName : chunksString) {
                chunks.add(new Chunk(chunkName, i));
                i++;
            }

            itemVersionDao.insertChunks(chunks, objectVersion.getId());
        }
    }

    private APIDeleteResponse deleteItemsAPI(User user, Workspace workspace,
            List<ItemMetadata> filesToDelete) throws DAOException {

        List<ItemMetadata> items = new ArrayList<ItemMetadata>();

        for (ItemMetadata fileToDelete : filesToDelete) {

            if (fileToDelete.getStatus().equals("DELETED")) {
                continue;
            }

            fileToDelete.setStatus("DELETED");
            // fileToDelete.setFileSize(0L);
            fileToDelete.setChunks(new ArrayList<String>());
            // fileToDelete.setChecksum(0L);
            fileToDelete.setVersion(fileToDelete.getVersion() + 1);

            Date date = new Date();
            fileToDelete.setModifiedAt(date);

            items.add(fileToDelete);
        }

        Boolean success = false;
        ItemMetadata fileToDelete = null;

        CommitNotification commitResult = this.doCommit(user, workspace,
                apiDevice, items);

        if (!commitResult.getItems().isEmpty()) {
            fileToDelete = commitResult.getItems().get(0).getMetadata();
            success = true;
        }

        APIDeleteResponse response = new APIDeleteResponse(fileToDelete, success, 0, "");
        response.setQuotaLimit(commitResult.getLimitQuota());
        response.setQuotaUsed(commitResult.getUsedQuota());
        
        return response;
    }
} public enum Status {

    NEW, DELETED, CHANGED, RENAMED, MOVED
}; public class APICommitResponse extends APIResponse {

    public APICommitResponse(ItemMetadata item, Boolean success, int error, String description) {
        this.success = success;
        this.errorCode = error;
        this.description = description;
        if (item != null) {
            this.item = new CommitInfo(item.getVersion(),
                    success, item);
        }
    }
} public class APICreateFolderResponse extends APIResponse {

	public APICreateFolderResponse(ItemMetadata item, Boolean success, int error, String description) {
		super();
		this.success = success;
		this.errorCode = error;
		this.description = description;
		if (item != null) {
			this.item = new CommitInfo(item.getVersion(),
					success, item);
		}
	}

} public class APIDeleteResponse extends APIResponse {

	public APIDeleteResponse(ItemMetadata item, Boolean success, int error, String description) {
		super();
		this.success = success;
		this.errorCode = error;
		this.description = description;
		if (item != null) {
			this.item = new CommitInfo(item.getVersion(),
					success, item);
		}
	}

} public class APIGetFolderMembersResponse extends APIResponse {

	private List<UserWorkspace> members;

	public APIGetFolderMembersResponse(List<UserWorkspace> members, Boolean success, int error, String description) {
		super();

		this.success = success;
		this.members = members;
		this.description = description;
		this.errorCode = error;
	}

	public List<UserWorkspace> getMembers() {
		return members;
	}

	@Override
	public String toString() {
		JsonObject jResponse = new JsonObject();

		if (getSuccess()) {

			JsonArray list = new JsonArray();

			for (UserWorkspace userWorkspace : members) {
				JsonObject jUser = parseUserWorkspace(userWorkspace);
				list.add(jUser);
			}

			return list.toString();

		} else {
			jResponse.addProperty("error", getErrorCode());
			jResponse.addProperty("description", getDescription());
			return jResponse.toString();
		}

		
	}
} public class APIGetMetadata extends APIResponse {

	private ItemMetadata itemMetadata;
	
	public APIGetMetadata(ItemMetadata item, Boolean success, int error, String description) {
		super();

		this.success = success;
		this.itemMetadata = item;
		this.description = description;
		this.errorCode = error;
	}
	
	public ItemMetadata getItemMetadata(){
		return itemMetadata;
	}
	
	@Override
	public String toString() {
		JsonObject jResponse = new JsonObject();

		if (getSuccess()) {
			ItemMetadata metadata = getItemMetadata();
			jResponse = parseObjectMetadataForAPI(metadata);

			if (metadata.getChildren() != null) {
				JsonArray contents = new JsonArray();

				for (ItemMetadata entry : metadata.getChildren()) {
					JsonObject entryJson = parseObjectMetadataForAPI(entry);
					contents.add(entryJson);
				}

				jResponse.add("contents", contents);
			}
		} else {
			jResponse.addProperty("error", getErrorCode());
			jResponse.addProperty("description", getDescription());
		}
		
		return jResponse.toString();
	}
} public class APIGetVersions extends APIResponse {

	private ItemMetadata itemMetadata;
	
	public APIGetVersions(ItemMetadata item, Boolean success, int error, String description) {
		super();

		this.success = success;
		this.itemMetadata = item;
		this.description = description;
		this.errorCode = error;
	}
	
	public ItemMetadata getItemMetadata(){
		return itemMetadata;
	}
	
	@Override
	public String toString() {
		JsonObject jResponse = new JsonObject();

		if (getSuccess()) {
			ItemMetadata metadata = getItemMetadata();
			jResponse = parseObjectMetadataForAPI(metadata);

			if (metadata.getChildren() != null) {
				JsonArray contents = new JsonArray();

				for (ItemMetadata entry : metadata.getChildren()) {
					JsonObject entryJson = parseObjectMetadataForAPI(entry);
					contents.add(entryJson);
				}

				jResponse.add("versions", contents);
			}
		} else {
			jResponse.addProperty("error", getErrorCode());
			jResponse.addProperty("description", getDescription());
		}

		return jResponse.toString();
	}
} public class APIGetWorkspaceInfoResponse extends APIResponse {

	private Workspace workspace;

	public APIGetWorkspaceInfoResponse(Workspace workspace, Boolean success, int error, String description) {
		super();
		this.success = success;
		this.description = description;
		this.errorCode = error;
		this.workspace = workspace;
	}

	public Workspace getWorkspace() {
		return workspace;
	}

	@Override
	public String toString() {
		JsonObject jResponse;

		if (!getSuccess()) {
			jResponse = new JsonObject();
			jResponse.addProperty("description", getDescription());
			jResponse.addProperty("error", getErrorCode());
		} else {
			jResponse = this.parseWorkspace();
		}

		return jResponse.toString();
	}

	protected JsonObject parseWorkspace() {

		JsonObject jMetadata = new JsonObject();

		if (workspace == null) {
			return jMetadata;
		}

		jMetadata.addProperty("id", workspace.getId().toString());
		jMetadata.addProperty("name", workspace.getName());
		jMetadata.addProperty("swift_container", workspace.getSwiftContainer());
		jMetadata.addProperty("is_shared", workspace.isShared());
		jMetadata.addProperty("owner", workspace.getOwner().getId().toString());
		jMetadata.addProperty("latest_revision", workspace.getLatestRevision());
		jMetadata.addProperty("parent_item_id", workspace.getParentItem().getId());
		jMetadata.addProperty("is_encrypted", workspace.isEncrypted());
		jMetadata.addProperty("quota_used", workspace.getOwner().getQuotaUsedLogical().toString());
		jMetadata.addProperty("quota_limit", workspace.getOwner().getQuotaLimit().toString());


		return jMetadata;
	}

} public abstract class APIResponse {

    protected CommitInfo item;
    protected Long quotaLimit;
    protected Long quotaUsed;
    protected Boolean success;
    protected int errorCode;
    protected String description;

    public Boolean getSuccess() {
        return success;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public String getDescription() {
        return description;
    }

    public CommitInfo getItem() {
        return item;
    }

    public ItemMetadata getMetadata() {
        return item.getMetadata();
    }

    public Long getQuotaLimit() {
        return quotaLimit;
    }

    public void setQuotaLimit(Long quotaLimit) {
        this.quotaLimit = quotaLimit;
    }

    public Long getQuotaUsed() {
        return quotaUsed;
    }

    public void setQuotaUsed(Long quotaUsed) {
        this.quotaUsed = quotaUsed;
    }

    @Override
    public String toString() {
        JsonObject jResponse;

        if (!getSuccess()) {
            jResponse = new JsonObject();
            jResponse.addProperty("error", getErrorCode());
            jResponse.addProperty("description", getDescription());
        } else {
            ItemMetadata file = getItem().getMetadata();
            jResponse = this.parseItemMetadata(file);
        }

        return jResponse.toString();
    }

    private JsonObject parseItemMetadata(ItemMetadata metadata) {
        JsonObject jMetadata = parseMetadata(metadata);

        if (metadata.getParentId() == null) {
            jMetadata.addProperty("parent_file_id", "");

        } else {
            jMetadata.addProperty("parent_file_id", metadata.getParentId());
        }

        if (metadata.getParentId() == null) {
            jMetadata.addProperty("parent_file_version", "");
        } else {
            jMetadata.addProperty("parent_file_version", metadata.getParentVersion());
        }

        // TODO: send only chunks when is a file
        if (!metadata.isFolder()) {
            JsonArray chunks = new JsonArray();
            for (String chunk : metadata.getChunks()) {
                JsonElement elem = new JsonPrimitive(chunk);
                chunks.add(elem);
            }
            jMetadata.add("chunks", chunks);
        }

        return jMetadata;
    }

    protected JsonObject parseObjectMetadataForAPI(ItemMetadata metadata) {
        JsonObject jMetadata = parseMetadata(metadata);

        if (metadata.isFolder()) {
            jMetadata.addProperty("is_root", metadata.isRoot());
        } else {
            JsonArray chunks = new JsonArray();

            for (String chunk : metadata.getChunks()) {
                JsonElement elem = new JsonPrimitive(chunk);
                chunks.add(elem);
            }

            jMetadata.add("chunks", chunks);
        }

        return jMetadata;
    }

    protected JsonObject parseMetadata(ItemMetadata metadata) {
        JsonObject jMetadata = new JsonObject();

        if (metadata == null) {
            return jMetadata;
        }

        jMetadata.addProperty("id", metadata.getId());
        jMetadata.addProperty("parent_id", metadata.getParentId());
        jMetadata.addProperty("filename", metadata.getFilename());
        jMetadata.addProperty("is_folder", metadata.isFolder());
        jMetadata.addProperty("status", metadata.getStatus());

        if (metadata.getModifiedAt() != null) {
            jMetadata.addProperty("modified_at", metadata.getModifiedAt().toString());
        }

        jMetadata.addProperty("version", metadata.getVersion());
        jMetadata.addProperty("checksum", metadata.getChecksum());
        jMetadata.addProperty("size", metadata.getSize());
        jMetadata.addProperty("mimetype", metadata.getMimetype());

        return jMetadata;
    }

    protected JsonObject parseUser(User user) {
        JsonObject jUser = new JsonObject();

        if (user == null) {
            return jUser;
        }

        jUser.addProperty("name", user.getName());
        jUser.addProperty("email", user.getEmail());

        return jUser;

    }

    protected JsonObject parseUserWorkspace(UserWorkspace userWorkspace) {

        if (userWorkspace == null) {
            return new JsonObject();
        }

        JsonObject jUser = parseUser(userWorkspace.getUser());
        jUser.addProperty("is_owner", userWorkspace.isOwner());
        jUser.addProperty("joined_at", userWorkspace.getJoinedAt().toString());

        return jUser;

    }
} public class APIRestoreMetadata extends APIResponse {

	public APIRestoreMetadata(ItemMetadata item, Boolean success, int error, String description) {
		super();
		this.success = success;
		this.errorCode = error;
		this.description = description;
		this.item = new CommitInfo(item.getVersion(),
				success, item);
	}
} public class APIShareFolderResponse extends APIResponse {

	private Workspace workspace;

	public APIShareFolderResponse(Workspace workspace, Boolean success, int error, String description) {
		super();

		this.success = success;
		this.workspace = workspace;
		this.description = description;
		this.errorCode = error;
	}

	public Workspace getWorkspace() {
		return workspace;
	}

	@Override
	public String toString() {
		JsonObject jResponse = new JsonObject();

		if (getSuccess()) {

			JsonArray sharedTo = new JsonArray();

			for (User user : workspace.getUsers()) {
				JsonObject jUser = parseUser(user);
				sharedTo.add(jUser);
			}

			jResponse.add("shared_to", sharedTo);

		} else {
			jResponse.addProperty("error", getErrorCode());
			jResponse.addProperty("description", getDescription());
		}

		return jResponse.toString();
	}
} public class APIUnshareFolderResponse extends APIResponse {
	private List<User> usersToRemove;
	private Workspace workspace;
	private boolean isUnshared;

	public APIUnshareFolderResponse(Workspace workspace,  List<User> usersToRemove, boolean isUnshared, Boolean success, int error, String description) {
		super();

		this.success = success;
		this.workspace = workspace;
		this.description = description;
		this.errorCode = error;
		this.usersToRemove = usersToRemove;
		this.isUnshared = isUnshared;
	}

	public Workspace getWorkspace() {
		return workspace;
	}
	
	public List<User> getUsersToRemove() {
		return usersToRemove;
	}

	public boolean isUnshared() {
		return isUnshared;
	}

	@Override
	public String toString() {
		JsonObject jResponse = new JsonObject();

		if (getSuccess()) {

			JsonArray sharedTo = new JsonArray();

			for (User user : usersToRemove) {
				JsonObject jUser = parseUser(user);
				sharedTo.add(jUser);
			}

			jResponse.add("unshared_to", sharedTo);

		} else {
			jResponse.addProperty("error", getErrorCode());
			jResponse.addProperty("description", getDescription());
		}

		return jResponse.toString();
	}
} public interface IParser {
	public String createResponse(APIResponse m);
} public class Constants {

	/* PROPERTIES */
	public static final String PROP_DATASOURCE = "datasource";
	public static final String DEFAULT_CONFIG_FILE = "config.properties";
	public static final UUID API_DEVICE_ID = new UUID(1, 1);

	// QUEUE
	public static final String PROP_OMQ_HOST = ParameterQueue.RABBIT_HOST;
	public static final String PROP_OMQ_PORT = ParameterQueue.RABBIT_PORT;
	public static final String PROP_OMQ_EXCHANGE = ParameterQueue.RPC_EXCHANGE;
	public static final String PROP_OMQ_THREADS = ParameterQueue.NUM_THREADS;
	public static final String PROP_OMQ_USER = ParameterQueue.USER_NAME;
	public static final String PROP_OMQ_PASSWORD = ParameterQueue.USER_PASS;

	// PostgreSQL
	public static final String PROP_POSTGRESQL_HOST = "postgresql.host";
	public static final String PROP_POSTGRESQL_PORT = "postgresql.port";
	public static final String PROP_POSTGRESQL_DATABASE = "postgresql.database";
	public static final String PROP_POSTGRESQL_USERNAME = "postgresql.user";
	public static final String PROP_POSTGRESQL_PASSWORD = "postgresql.password";
	public static final String PROP_POSTGRESQL_INITIAL_CONNS = "postgresql.initial_cons";
	public static final String PROP_POSTGRESQL_MAX_CONNS = "postgresql.max_cons";

	/* SWIFT */
	public static final String PROP_SWIFT_HOST = "swift.host";
	public static final String PROP_SWIFT_PORT = "swift.port";
	public static final String PROP_SWIFT_PROTOCOL = "swift.protocol";
	public static final String PROP_SWIFT_KEYSTONE_HOST = "swift.keystone_host";
	public static final String PROP_SWIFT_KEYSTONE_PORT = "swift.keystone_port";
	public static final String PROP_SWIFT_KEYSTONE_ADMIN_PORT = "swift.keystone_admin_port";
	public static final String PROP_SWIFT_KEYSTONE_PROTOCOL = "swift.keystone_protocol";
	public static final String PROP_SWIFT_TENANT = "swift.tenant";
	public static final String PROP_SWIFT_USER = "swift.user";
	public static final String PROP_SWIFT_PASSWORD = "swift.password";
	
	/* GENERAL */
	public static final String DEFAULT_DATASOURCE = "postgresql";

	/* QUEUE */
	public static final String DEFAULT_OMQ_HOST = "localhost";
	public static final Integer DEFAULT_OMQ_PORT = 5672;
	public static final String DEFAULT_OMQ_EXCHANGE = "rpc_global_exchange";
	public static final String DEFAULT_OMQ_USER = "guest";
	public static final String DEFAULT_OMQ_PASSWORD = "guest";
	public static final String DEFAULT_OMQ_THREADS = "4";

	/* POSTGRESQL */
	public static final String DEFAULT_POSTGRESQL_HOST = "localhost";
	public static final String DEFAULT_POSTGRESQL_PORT = "5432";
	public static final String DEFAULT_POSTGRESQL_DATABASE = "stacksync";
	public static final String DEFAULT_POSTGRESQL_DRIVER = "org.postgresql.Driver";
	public static final String DEFAULT_POSTGRESQL_USERNAME = "stacksync";
	public static final String DEFAULT_POSTGRESQL_PASSWORD = "stacksync";
	public static final String DEFAULT_POSTGRESQL_INITIAL_CONNS = "1";
	public static final String DEFAULT_POSTGRESQL_MAX_CONNS = "100";

	/* SWIFT */
	public static final String DEFAULT_SWIFT_HOST = "localhost";
	public static final String DEFAULT_SWIFT_PORT = "8080";
	public static final String DEFAULT_SWIFT_PROTOCOL = "http";
	public static final String DEFAULT_SWIFT_KEYSTONE_HOST = "localhost";
	public static final String DEFAULT_SWIFT_KEYSTONE_PORT = "5000";
	public static final String DEFAULT_SWIFT_KEYSTONE_ADMIN_PORT = "35357";
	public static final String DEFAULT_SWIFT_KEYSTONE_PROTOCOL = "http";
	public static final String DEFAULT_SWIFT_TENANT = "stacksync";
	public static final String DEFAULT_SWIFT_USER = "stacksync_admin";
	public static final String DEFAULT_SWIFT_PASSWORD = "secrete";
	
	/* XMLRPC */
	public static final Integer XMLRPC_PORT = 61234;
}
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import omq.common.broker.Broker;
import omq.exception.RemoteException;
import org.apache.log4j.Logger;
import com.stacksync.commons.models.CommitInfo;
import com.stacksync.commons.models.Item;
import com.stacksync.commons.models.ItemMetadata;
import com.stacksync.commons.models.User;
import com.stacksync.commons.models.Workspace;
import com.stacksync.commons.notifications.CommitNotification;
import com.stacksync.commons.notifications.ShareProposalNotification;
import com.stacksync.commons.notifications.UnshareNotification;
import com.stacksync.commons.omq.RemoteClient;
import com.stacksync.commons.omq.RemoteWorkspace;
import com.stacksync.syncservice.db.ConnectionPool;
import com.stacksync.syncservice.handler.SQLAPIHandler;
import com.stacksync.syncservice.handler.Handler.Status;
import com.stacksync.syncservice.rpc.messages.APICommitResponse;
import com.stacksync.syncservice.rpc.messages.APICreateFolderResponse;
import com.stacksync.syncservice.rpc.messages.APIDeleteResponse;
import com.stacksync.syncservice.rpc.messages.APIGetFolderMembersResponse;
import com.stacksync.syncservice.rpc.messages.APIGetMetadata;
import com.stacksync.syncservice.rpc.messages.APIGetVersions;
import com.stacksync.syncservice.rpc.messages.APIGetWorkspaceInfoResponse;
import com.stacksync.syncservice.rpc.messages.APIResponse;
import com.stacksync.syncservice.rpc.messages.APIRestoreMetadata;
import com.stacksync.syncservice.rpc.messages.APIShareFolderResponse;
import com.stacksync.syncservice.rpc.messages.APIUnshareFolderResponse;
import com.stacksync.syncservice.rpc.parser.IParser;
import com.stacksync.syncservice.util.Constants;
package com.stacksync.syncservice.rpc;





public class XmlRpcSyncHandler {

    private static final Logger logger = Logger.getLogger(XmlRpcSyncHandler.class.getName());
    private SQLAPIHandler apiHandler;
    private IParser parser;
    private Broker broker;

    public XmlRpcSyncHandler(Broker broker, ConnectionPool pool) {
        try {
            this.apiHandler = new SQLAPIHandler(pool);
            this.broker = broker;
            this.parser = Reader.getInstance("com.stacksync.syncservice.rpc.parser.JSONParser");

            logger.info("XMLRPC server set up done.");
        } catch (Exception e) {
            logger.error("XMLRPC server could not initiliaze.");
        }
    }

    public String getMetadata(String strUserId, String strItemId, String strIncludeChunks, String strVersion,
            String strIsFolder) {

        logger.debug(String.format("XMLRPC Request. getMetadata [userId: %s, fileId: %s, chunks: %s, version: %s]",
                strUserId, strItemId, strIncludeChunks, strVersion));

        Long fileId = null;
        try {
            fileId = Long.parseLong(strItemId);
        } catch (NumberFormatException ex) {
        }

        Boolean isFolder = Boolean.parseBoolean(strIsFolder);

        Boolean includeChunks = Boolean.parseBoolean(strIncludeChunks);

        Long version = null;
        try {
            version = Long.parseLong(strVersion);
        } catch (NumberFormatException ex) {
        }

        User user = new User();
        user.setId(UUID.fromString(strUserId));

        APIGetMetadata response = this.apiHandler.getMetadata(user, fileId, includeChunks, version, isFolder);

        logger.debug(String.format("XMLRPC Response. %s", response.toString()));

        return response.toString();
    }

    public String getFolderContents(String strUserId, String strFolderId, String strIncludeDeleted) {

        Boolean includeList = true;

        logger.debug(String.format("XMLRPC Request. getMetadata [userId: %s, fileId: %s, includeList: %s]", strUserId,
                strFolderId, includeList, strIncludeDeleted));

        Long folderId = null;
        try {
            folderId = Long.parseLong(strFolderId);
        } catch (NumberFormatException ex) {
        }

        Boolean includeDeleted = Boolean.parseBoolean(strIncludeDeleted);

        User user = new User();
        user.setId(UUID.fromString(strUserId));

        APIGetMetadata response = this.apiHandler.getFolderContent(user, folderId, includeDeleted);

        logger.debug(String.format("XMLRPC Response. %s", response.toString()));

        return response.toString();
    }

    public String getVersions(String strUserId, String strFileId) {

        Long itemId = null;
        try {
            itemId = Long.parseLong(strFileId);
        } catch (NumberFormatException ex) {
        }

        // TODO: filtrar versiones borradas!!
        logger.debug("XMLRPC -> get_versions -->[User:" + strUserId + ", itemId:" + itemId + "]");

        User user = new User();
        user.setId(UUID.fromString(strUserId));

        ItemMetadata item = new ItemMetadata();
        item.setId(itemId);

        APIGetVersions response = this.apiHandler.getVersions(user, item);

        logger.debug("XMLRPC -> resp -->[" + response.toString() + "]");
        return response.toString();
    }

    public String newFolder(String strUserId, String strFolderName, String strParentId) {

        logger.debug(String.format("XMLRPC Request. createFolder [userId: %s, folderName: %s, parentId: %s]",
                strUserId, strFolderName, strParentId));

        Long parentId = null;
        try {
            parentId = Long.parseLong(strParentId);
        } catch (NumberFormatException ex) {
        }

        if (strFolderName.length() == 0) {
            APIResponse response = new APICreateFolderResponse(null, false, 400, "Folder name cannot be empty.");