Package ru.dreamteam.couch

Source Code of ru.dreamteam.couch.DbManager$SkipNewLinesFilterReader

package ru.dreamteam.couch;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.FilterReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.BasicHttpEntity;

import ru.dreamteam.couch.query.Query;
import static org.apache.http.HttpStatus.*;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Control updates for design documents
* @author dooman
*/
class DbManager {
    public final Logger log = Logger.getLogger(DbManager.class.getName());

    private static class SkipNewLinesFilterReader extends FilterReader {

        private boolean stringInProgress;

        /**
         * Creates a new filtered reader.
         *
         * @param in a reader object providing the underlying stream
         * @throws NullPointerException if <code>in</code> is <code>null</code>
         */
        public SkipNewLinesFilterReader(Reader in) {
            super(in);
        }

        @Override
        public int read() throws IOException {
            int cChar = in.read();
            if (cChar == '\"') {
                stringInProgress = !stringInProgress;
            }
            return ((cChar == '\n' || cChar == '\r') && stringInProgress) ? ' ' : cChar;
        }

        @Override
        public int read(char[] cbuf, int off, int len) throws IOException {
            for(int i=0; i < len; i++)
                cbuf[off + i] = (char)read();

            return len;
        }
    }

    private String dbName;
    private Couch dbInstance;
    private ObjectMapper mapper;

    public DbManager(String dbName, Couch dbInstance, ObjectMapper mapper) {
        super();
        this.dbName = dbName;
        this.dbInstance = dbInstance;
        this.mapper = mapper;
    }

    /**
     * <p>
     * Update design documents from files /views/[DBNAME]/*.json.
     * Each JSON file is one design document which will be called as filename.
     * Design document will be updated only if content of JSON file was updated.
     * </p>
     * <p>
     * Also, this method remove all design documents which listed in file /views/[DBNAME]/views.delete.
     * Each design document for remove should be starts on new line
     * </p>
     * @return Number of created/updated/deleted design documents.
     */
    public int updateViews() {
        int updated = 0;
        URL viewsUrl = getClass().getResource("/views/" + dbName + "/");
        if (viewsUrl != null) {
            File viewsDir = new File(viewsUrl.getFile());
            if (viewsDir.isDirectory() && viewsDir.exists()) {
                File[] viewsFiles = viewsDir.listFiles(new FilenameFilter() {
                    @Override
                    public boolean accept(File dir, String name) {
                        return name.endsWith(".json");
                    }
                });
                log.info("Importing database views");
                for (File view : viewsFiles) {
                    if (importView(view)) {
                        updated++;
                    }
                }
                log.info("Importing database views [complete]");

                log.info("Deleting database views");
                File deleteViewsFile = new File(viewsDir, "views.delete");
                try (FileInputStream s = new FileInputStream(deleteViewsFile)) {
                    for (String line : (List<String>) IOUtils.readLines(s)) {
                        String viewId = line.trim();
                        if (deleteDesignDocument(viewId)) {
                            updated++;
                        }
                    }
                } catch (IOException e) {
                    log.log(Level.SEVERE, "Error while reading delete file [" + deleteViewsFile.getAbsolutePath() + "]", e);
                }
                log.info("Deleting database views [complete]");
            }
        }
        return updated;
    }

    private boolean importView(File view) {
        try {
            if (needViewUpdate(view)) {
                log.info("The view import from: [" + view.getAbsolutePath() + "]");
                createView(view);
                return true;
            }
        } catch (Exception e) {
            log.log(Level.SEVERE, "Error the view update [" + view.getName() + "]", e);
        }
        return false;
    }

    /**
     * Returns current list of design documents in database
     * @return list of design documents in database
     */
    public List<ViewStore> getDesignDocuments() {
        Query<ViewStore> query = new Query<ViewStore>(dbInstance, "/" + dbName + "/_all_docs", ViewStore.class);
        return query.startKey("_design/").endKey("_design0").includeDocs(true).list();
    }

    private static final String NOT_FOUND_DOC = "404DOC";
    /**
     * Delete design document from database.
     * @param docId id of design document <b>without</b> _design/ prefix
     * @return {@code true} if design document was deleted successfully, {@code false} if design document does not exists
     */
    public boolean deleteDesignDocument(final String docId) {
        if (StringUtils.isBlank(docId)) {
            return false;
        }

        final String revision = dbInstance.execute(new HttpCall<String>() {
            @Override
            public HttpRequest getRequest() throws URISyntaxException,
                    IOException {
                return new HttpHead("/" + dbName + "/_design/" + docId);
            }

            @Override
            public String doWithResponse(HttpResponse response)
                    throws IOException {
                if (response.getStatusLine().getStatusCode() == SC_NOT_FOUND) {
                    return NOT_FOUND_DOC;
                }
                return StringUtils.remove(response.getHeaders("ETag")[0].getValue(), '"');
            }
        }, SC_OK, SC_NOT_FOUND);

        if (StringUtils.equals(NOT_FOUND_DOC, revision)) {
            return false;
        }
        dbInstance.execute(new HttpCall<Void>() {
            @Override
            public HttpRequest getRequest() throws URISyntaxException,
                    IOException {
                return new HttpDelete("/" + dbName + "/_design/" + docId + "?" + CouchConstants.REV + "=" + revision);
            }

            @Override
            public Void doWithResponse(HttpResponse response) { return null; }
        }, SC_OK, SC_ACCEPTED);

        log.info("Deleted design document [" + docId + "]");
        return true;
    }

    private boolean needViewUpdate(final File viewFile) throws ParseException, IOException, NoSuchAlgorithmException {
        final String viewId = viewFile.getName().replace(".json", "");
        return dbInstance.execute(new HttpCall<Boolean>() {
            @Override
            public HttpRequest getRequest() throws URISyntaxException, IOException {
                return new HttpGet(Db.buildUri("/" + dbName + "/_design/" + viewId));
            }

            @Override
            public Boolean doWithResponse(HttpResponse response) throws Exception {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == SC_OK) {
                    ViewStore oldStore = mapper.readValue(response.getEntity().getContent(), ViewStore.class);
                    byte[] fileContent = IOUtils.toByteArray(new FileInputStream(viewFile));
                    String md5 = md5(fileContent);
                    return !md5.equals(oldStore.getFileMd5());
                }
                return true;
            }
        }, SC_OK, SC_NOT_FOUND);
    }

    private void createView(File file) throws IOException, ParseException, NoSuchAlgorithmException {
        InputStream fileInputStream = new FileInputStream(file);
        try {
            byte[] fileContent = IOUtils.toByteArray(fileInputStream);
            final ViewStore viewStore = mapper.readValue(new SkipNewLinesFilterReader(new InputStreamReader(new ByteArrayInputStream(fileContent), Charset.forName("UTF-8"))), ViewStore.class);
            viewStore.setId(file.getName().replace(".json", ""));
            viewStore.setFileMd5(md5(fileContent));
   
            final String viewUri = "/" + dbName + "/_design/" + viewStore.getId();

            //Setting last revision to view
            dbInstance.execute(new HttpCall<Void>() {
                @Override
                public HttpRequest getRequest() throws URISyntaxException, IOException {
                    return new HttpGet(Db.buildUri(viewUri));
                }

                @Override
                public Void doWithResponse(HttpResponse response) throws IOException {
                    if (response.getStatusLine().getStatusCode() == SC_OK) {
                        ViewStore oldStore = mapper.readValue(response.getEntity().getContent(), ViewStore.class);
                        viewStore.setRevision(oldStore.getRevision());
                    }
                    return null;
                }
            }, SC_OK, SC_NOT_FOUND);

            //Store view to database
            dbInstance.execute(new HttpCall<Void>() {
                @Override
                public HttpRequest getRequest() throws URISyntaxException, IOException {
                    HttpPut command = new HttpPut(Db.buildUri(viewUri));
                    BasicHttpEntity entity = new BasicHttpEntity();
                    entity.setContentType("application/json");
                    byte[] data = Db.toBytes(viewStore, mapper);
                    entity.setContent(new ByteArrayInputStream(data));
                    entity.setContentLength(data.length);
                    command.setEntity(entity);
                    return command;
                }

                @Override
                public Void doWithResponse(HttpResponse response) { return null; }
            }, SC_CREATED);
        } finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    private String md5(byte[] content) throws FileNotFoundException, IOException, NoSuchAlgorithmException {
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        byte[] md5Bytes = messageDigest.digest(content);
        StringBuilder md5String = new StringBuilder();
        for (byte b : md5Bytes) {
            md5String.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1,3));
        }
        return md5String.toString();
    }
}
TOP

Related Classes of ru.dreamteam.couch.DbManager$SkipNewLinesFilterReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.