de.elomagic.carafile.client.CaraCloud.java Source code

Java tutorial

Introduction

Here is the source code for de.elomagic.carafile.client.CaraCloud.java

Source

/*
 * Copyright 2014 Carsten Rambow, elomagic.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.elomagic.carafile.client;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.prefs.Preferences;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.apache.log4j.Logger;

import de.elomagic.carafile.share.CloudFileData;
import de.elomagic.carafile.share.FileChangesList;
import de.elomagic.carafile.share.JsonUtil;
import de.elomagic.carafile.share.MetaData;

/**
 *
 */
public class CaraCloud {
    private static final Logger LOG = Logger.getLogger(CaraCloud.class);
    private CaraFileClient client;
    private Path basePath;
    private WatchService watcher;
    private long lastMillis;

    /**
     * Set the client for core service communictations.
     *
     * @param client The {@link CaraFileClient}
     * @return Returns itself
     */
    public CaraCloud setClient(final CaraFileClient client) {
        this.client = client;
        return this;
    }

    /**
     * Set the to be sychronized folder
     *
     * @param basePath The path
     * @return Returns itself
     */
    public CaraCloud setBasePath(final Path basePath) {
        this.basePath = basePath;
        return this;
    }

    /**
     * Starts synchronization of the given base path with the registry.
     * <p/>
     * This method will block till call method {@link CaraCloud#stop() }
     *
     * @throws IOException Thrown when an I/O error occurs
     * @throws InterruptedException Thrown when method stop was called or application will terminate
     * @throws GeneralSecurityException
     */
    public void start() throws IOException, InterruptedException, GeneralSecurityException {
        if (client == null) {
            throw new IllegalStateException("Attribute \"client\" must be set.");
        }

        if (basePath == null) {
            throw new IllegalStateException("Attribute \"basePath\" must be set.");
        }

        if (!Files.exists(basePath)) {
            throw new IllegalStateException("Path \"" + basePath + "\" must exists.");
        }

        if (!Files.isDirectory(basePath)) {
            throw new IllegalStateException("Path \"" + basePath + "\" must be a directory/folder.");
        }

        watcher = FileSystems.getDefault().newWatchService();
        registerDefaultWatch(basePath);

        while (!Thread.interrupted()) {
            WatchKey key = watcher.take();
            for (WatchEvent<?> event : key.pollEvents()) {
                if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
                    Path path = basePath.resolve(event.context().toString());
                    createFile(path);
                } else if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {

                } else if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
                    Path path = basePath.resolve(event.context().toString());
                    deleteFile(path);
                } else {
                    LOG.error("Unsupported kind: " + event.kind() + ", path: " + event.context());
                }
            }

            key.reset();
        }
    }

    /**
     * Stops watching the base path on any kind of changes.
     *
     * @throws IOException Thrown when an I/O error occurs
     */
    public void stop() throws IOException {
        if (watcher == null) {
            return;
        }

        watcher.close();
    }

    public Set<CloudFileData> list(final Path remotePath) throws IOException {
        LOG.debug("List remote folder \"" + remotePath + "\" file at " + client.getRegistryURI());

        URI uri = CaraFileUtils.buildURI(client.getRegistryURI(), "cloud", "list", remotePath.toString());
        HttpResponse response = client.executeRequest(Request.Get(uri)).returnResponse();

        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new IOException("HTTP responce code " + response.getStatusLine().getStatusCode() + " "
                    + response.getStatusLine().getReasonPhrase());
        }

        Charset charset = ContentType.getOrDefault(response.getEntity()).getCharset();

        Set<CloudFileData> set = JsonUtil.read(new InputStreamReader(response.getEntity().getContent(), charset),
                Set.class);

        LOG.debug("Folder contains " + set.size() + " item(s)");

        return set;
    }

    private void createFile(final Path path) throws IOException, GeneralSecurityException {
        LOG.debug("Creating file \"" + path + "\" file at " + client.getRegistryURI());

        URI uri;

        if (Files.isDirectory(path)) {
            registerDefaultWatch(path);
            uri = CaraFileUtils.buildURI(client.getRegistryURI(), "cloud", "create", getSubPath(path));
        } else {
            MetaData md = client.uploadFile(path, path.getFileName().toString());
            uri = CaraFileUtils.buildURI(client.getRegistryURI(), "cloud", "create", getSubPath(path), md.getId());
        }

        HttpResponse response = client.executeRequest(Request.Post(uri)).returnResponse();

        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new IOException();
        }
    }

    private void deleteFile(final Path path) throws IOException {
        LOG.debug("Deleting file \"" + path + "\" file at " + client.getRegistryURI());
        URI uri = CaraFileUtils.buildURI(client.getRegistryURI(), "cloud", "delete", getSubPath(path));
        HttpResponse response = client.executeRequest(Request.Delete(uri)).returnResponse();

        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new IOException();
        }

        MetaData md = client.getMetaDataFromResponse(response);

        client.deleteFile(md.getId(), true);
    }

    private void syncFolder() {

    }

    private FileChangesList getRemoteChangeList(long lastMillis) throws IOException {
        URI uri = CaraFileUtils.buildURI(client.getRegistryURI(), "cloud", "changes", Long.toString(lastMillis));
        HttpResponse response = client.executeRequest(Request.Get(uri)).returnResponse();

        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new IOException("HTTP responce code " + response.getStatusLine().getStatusCode() + " "
                    + response.getStatusLine().getReasonPhrase());
        }

        Charset charset = ContentType.getOrDefault(response.getEntity()).getCharset();

        FileChangesList changeList = JsonUtil
                .read(new InputStreamReader(response.getEntity().getContent(), charset), FileChangesList.class);

        LOG.debug("Folder contains " + changeList.getChangeList() + " item(s)");

        return changeList;
    }

    private List<Path> checkLocalFolder(final Path basePath) throws IOException {
        Preferences preferences = Preferences.systemNodeForPackage(getClass());
        final long lastScanMillis = preferences.getLong("lastScan", 0);

        final List<Path> changedPathList = getLocalChangeList(basePath, lastScanMillis);

        for (Path path : basePath) {
            // xxx
        }

        return changedPathList;
    }

    private List<Path> getLocalChangeList(final Path basePath, final long lastScanMillis) throws IOException {
        if (Files.notExists(basePath)) {
            return Collections.EMPTY_LIST;
        }

        final List<Path> changedPathList = new ArrayList();

        Files.walkFileTree(basePath, new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                if (attrs.lastModifiedTime().toMillis() > lastScanMillis) {
                    changedPathList.add(dir);
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (attrs.lastModifiedTime().toMillis() > lastScanMillis) {
                    changedPathList.add(file);
                }
                return FileVisitResult.CONTINUE;
            }

        });

        return changedPathList;
    }

    private void registerDefaultWatch(final Path path) throws IOException {
        path.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY,
                StandardWatchEventKinds.ENTRY_DELETE);
    }

    String getSubPath(final Path path) {
        Path p = path.subpath(basePath.getNameCount(), path.getNameCount());

        return p.toString().replace("\\", "/");
    }
}