com.github.cmisbox.core.Queue.java Source code

Java tutorial

Introduction

Here is the source code for com.github.cmisbox.core.Queue.java

Source

/*  
 *   CMISBox - Synchronize and share your files with your CMIS Repository
 *
 *   Copyright (C) 2011 - Andrea Agili 
 *  
 *    CMISBox is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  CMISBox 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with CMISBox.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package com.github.cmisbox.core;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.DelayQueue;
import java.util.regex.Pattern;

import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.github.cmisbox.persistence.Storage;
import com.github.cmisbox.persistence.StoredItem;
import com.github.cmisbox.remote.CMISRepository;
import com.github.cmisbox.remote.ChangeItem;
import com.github.cmisbox.remote.Changes;
import com.github.cmisbox.ui.UI;
import com.github.cmisbox.ui.UI.Status;

public class Queue implements Runnable {

    private static Queue instance = new Queue();

    public static Queue getInstance() {
        return Queue.instance;
    }

    private boolean active = true;

    // by default do not synch files starting with a dot
    private Pattern filter = Pattern.compile("^\\..*");

    private Thread thread;

    private DelayQueue<LocalEvent> delayQueue = new DelayQueue<LocalEvent>();

    private Log log;

    private Queue() {
        this.thread = new Thread(this, "Queue");
        this.thread.start();
        this.log = LogFactory.getLog(this.getClass());
    }

    public synchronized void add(LocalEvent localEvent) {
        this.log.debug("Asked to queue" + localEvent);
        if (!this.active) {
            return;
        }

        if ((localEvent.getName() != null) && this.filter.pattern().matches(localEvent.getName())) {
            this.log.debug("Filtered " + localEvent);
            return;
        }

        if (this.delayQueue.contains(localEvent) || localEvent.isRename()) {
            Iterator<LocalEvent> i = this.delayQueue.iterator();
            while (i.hasNext()) {
                LocalEvent queuedEvent = i.next();
                if (queuedEvent.equals(localEvent)) {
                    localEvent.merge(queuedEvent);
                    i.remove();
                    this.log.debug("" + "Merged " + queuedEvent);

                } else if (Config.getInstance().isMacOSX() && localEvent.isRename() && queuedEvent.isDelete()) {
                    if (localEvent.isParent(queuedEvent)) {
                        i.remove();
                    }
                }
            }
        }

        if (!(localEvent.isCreate() && localEvent.isDelete())) {
            this.delayQueue.put(localEvent);
            this.log.debug("Queued " + localEvent);
        }
    }

    public Pattern getFilter() {
        return this.filter;
    }

    private StoredItem getSingleItem(String path) throws Exception {
        List<StoredItem> itemList = Storage.getInstance().findByPath(path);
        if (itemList.size() == 1) {
            return itemList.get(0);
        } else {
            throw new Exception(String.format("Expected one result in index: %s -> %s", path, itemList));
        }
    }

    public void manageEvent(LocalEvent event) {
        Log log = LogFactory.getLog(this.getClass());
        log.debug("managing: " + event);

        // any platform
        // - a folder can be renamed before containing files are managed: on
        // folder rename all children must be updated while still in queue;

        // linux
        // - if a file or folder is moved out of a watched folder it is reported
        // as a rename to null (check if it's still there)

        // mac osx
        // - recursive folder operations (e.g. unzip an archive or move a folder
        // inside a watched folder) are not reported, only root folder is
        // reported as create
        // - folder rename causes children to be notified as deleted (with old
        // path)

        try {
            if (event.isSynch()) {
                this.synchAllWatches();
                return;
            }

            File f = new File(event.getFullFilename());
            if (event.isCreate()) {
                StoredItem item = this.getSingleItem(event.getLocalPath());

                if ((item != null) && (item.getLocalModified().longValue() >= f.lastModified())) {
                    return;
                }
                String parent = f.getParent().substring(Config.getInstance().getWatchParent().length());

                CmisObject obj = CMISRepository.getInstance().addChild(this.getSingleItem(parent).getId(), f);
                Storage.getInstance().add(f, obj);
            } else if (event.isDelete()) {
                StoredItem item = this.getSingleItem(event.getLocalPath());
                if (f.exists()) {
                    throw new Exception(
                            String.format("File %s reported to be deleted but stil exists", f.getAbsolutePath()));
                }
                CMISRepository.getInstance().delete(item.getId());
                Storage.getInstance().delete(item, true);
            } else if (event.isModify()) {
                if (f.isFile()) {
                    StoredItem item = this.getSingleItem(event.getLocalPath());

                    if (item.getLocalModified().longValue() < f.lastModified()) {

                        Document doc = CMISRepository.getInstance().update(item, f);

                        Storage.getInstance().localUpdate(item, f, doc);
                    } else {
                        log.debug("file" + f + " modified in the past");
                    }

                }
            } else if (event.isRename()) {
                StoredItem item = this.getSingleItem(event.getLocalPath());
                CmisObject obj = CMISRepository.getInstance().rename(item.getId(), f);
                Storage.getInstance().localUpdate(item, f, obj);
            }

        } catch (Exception e) {
            log.error(e);
            if (log.isDebugEnabled()) {
                e.printStackTrace();
            }
            if (UI.getInstance().isAvailable()) {
                UI.getInstance().notify(e.toString());
                UI.getInstance().setStatus(Status.KO);
            }
        }
    }

    private String resolvePath(Folder parent) throws Exception {
        StoredItem item = Storage.getInstance().findById(parent.getId());
        String path = File.separator + parent.getName();
        Folder ancestor = parent.getFolderParent();
        while (item == null) {
            if (ancestor == null) {
                return null;
            }
            item = Storage.getInstance().findById(ancestor.getId());
            if (item == null) {
                path = File.separator + ancestor.getName() + path;
            } else {
                path = item.getPath() + path;
            }
            ancestor = ancestor.getFolderParent();
        }

        return Config.getInstance().getWatchParent() + path;
    }

    public void run() {
        while (this.active) {
            try {
                this.manageEvent(this.delayQueue.take());
                if (this.delayQueue.isEmpty()) {

                }
            } catch (InterruptedException e) {
                LogFactory.getLog(this.getClass()).info(this, e);
            }
        }

    }

    public void setFilter(Pattern filter) {
        this.filter = filter;
    }

    public void stop() {
        this.active = false;
        this.delayQueue.clear();
        this.thread.interrupt();
    }

    private void synchAllWatches() throws Exception {
        Storage storage = Storage.getInstance();
        CMISRepository cmisRepository = CMISRepository.getInstance();
        UI ui = UI.getInstance();
        Config config = Config.getInstance();

        if (ui.isAvailable()) {
            ui.setStatus(Status.SYNCH);
        }

        List<String[]> updates = new ArrayList<String[]>();

        Changes changes = cmisRepository.getContentChanges(storage.getRootIds());

        LinkedHashMap<String, File> downloadList = new LinkedHashMap<String, File>();

        boolean errors = false;

        for (ChangeItem item : changes.getEvents()) {
            try {
                if (item == null) {
                    continue;
                }
                String id = "workspace://SpacesStore/" + item.getId();
                String type = item.getT();
                StoredItem storedItem = storage.findById(id);
                if (type.equals("D")) {
                    if (storedItem != null) {
                        File f = new File(config.getWatchParent() + storedItem.getPath());
                        f.delete();
                        storage.delete(storedItem, true);
                    }
                } else if (type.equals("C") || type.equals("U")) {
                    CmisObject remoteObject = cmisRepository.findObject(id);
                    remoteObject.refresh();
                    if (remoteObject.getType().getBaseTypeId().equals(BaseTypeId.CMIS_FOLDER)) {
                        Folder folder = (Folder) remoteObject;
                        File newFile = new File(this.resolvePath(folder.getFolderParent()), folder.getName());
                        if (storedItem == null) {
                            storage.add(newFile, folder, false);
                        } else {
                            if ((folder.getLastModificationDate().getTimeInMillis() > storedItem
                                    .getRemoteModified()) && !storedItem.getName().equals(folder.getName())) {
                                if (new File(storedItem.getAbsolutePath()).renameTo(newFile)) {
                                    storage.localUpdate(storedItem, newFile, folder);
                                } else {
                                    if (ui.isAvailable()) {
                                        ui.notify(Messages.renameError + " " + storedItem.getAbsolutePath() + " -> "
                                                + newFile.getAbsolutePath());
                                    }
                                    this.log.error("Unable to rename " + storedItem.getAbsolutePath() + " to "
                                            + newFile.getAbsolutePath());

                                }
                            }
                        }
                    } else {
                        Document document = (Document) remoteObject;
                        this.log.debug("preparing to update or create " + document.getName());
                        File newFile = new File(this.resolvePath(document.getParents().get(0)), document.getName());
                        if (storedItem == null) {
                            downloadList.put(id, newFile);
                        } else {
                            File current = new File(storedItem.getAbsolutePath());
                            if (storedItem.getLocalModified() < document.getLastModificationDate()
                                    .getTimeInMillis()) {
                                if (!current.getAbsolutePath().equals(newFile.getAbsolutePath())) {
                                    if (!current.renameTo(newFile)) {
                                        if (ui.isAvailable()) {
                                            ui.notify(Messages.renameError + " " + storedItem.getAbsolutePath()
                                                    + " -> " + newFile.getAbsolutePath());
                                        }
                                        this.log.error("Unable to rename " + storedItem.getAbsolutePath() + " to "
                                                + newFile.getAbsolutePath());
                                    }
                                }
                                downloadList.put(id, newFile);
                            }
                        }
                    }
                }
            } catch (Exception e1) {
                errors = true;
                this.log.error("Error getting remote chahges for " + item, e1);
            }
        }

        if (ui.isAvailable() && (downloadList.size() > 0)) {
            ui.notify(Messages.downloading + " " + downloadList.size() + " " + Messages.files);
        }
        for (Entry<String, File> e : downloadList.entrySet()) {
            try {
                storage.deleteById(e.getKey());
                e.getValue().delete();
                cmisRepository.download(cmisRepository.getDocument(e.getKey()), e.getValue());
                storage.add(e.getValue(), cmisRepository.getDocument(e.getKey()));
            } catch (Exception e1) {
                errors = true;
                this.log.error("Error downloading " + e, e1);
                if (ui.isAvailable()) {
                    ui.notify(Messages.errorDownloading + " " + e);
                }
            }
        }

        if (!errors) {
            config.setChangeLogToken(changes.getToken());
        }

        if (ui.isAvailable()) {
            ui.setStatus(Status.OK);
            if (updates.size() == 1) {
                ui.notify(updates.get(0)[0] + " " + Messages.updatedBy + " " + updates.get(0)[1]);
            } else if (updates.size() > 1) {
                ui.notify(Messages.updated + " " + updates.size() + Messages.files);
            }

        }
    }
}