com.networknt.light.rule.AbstractBfnRule.java Source code

Java tutorial

Introduction

Here is the source code for com.networknt.light.rule.AbstractBfnRule.java

Source

/*
 * Copyright 2015 Network New Technologies Inc.
 *
 * 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 com.networknt.light.rule;

import com.fasterxml.jackson.core.type.TypeReference;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.networknt.light.server.DbService;
import com.networknt.light.util.HashUtil;
import com.networknt.light.util.ServiceLocator;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.index.OCompositeKey;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.serialization.serializer.OJSONWriter;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
import com.tinkerpop.blueprints.impls.orient.OrientVertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by steve on 28/12/14.
 * This the abstract class that implements functions for Blog, Forum and News as
 * they share similar traits. Since ids are generated and there is no need to check
 * uniqueness. Just make sure parent and children are checked and converted to ids.
 *
 */
public abstract class AbstractBfnRule extends BranchRule implements Rule {
    static final Logger logger = LoggerFactory.getLogger(AbstractBfnRule.class);

    public abstract boolean execute(Object... objects) throws Exception;

    public boolean addPost(String bfnType, Object... objects) throws Exception {
        Map<String, Object> inputMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) inputMap.get("data");
        String parentId = (String) data.get("parentId");
        String host = (String) data.get("host");
        String error = null;
        Map<String, Object> payload = (Map<String, Object>) inputMap.get("payload");
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            OrientVertex parent = getBranchByHostId(graph, bfnType, host, parentId);
            if (parent == null) {
                error = "Id " + parentId + " doesn't exist on host " + host;
                inputMap.put("responseCode", 400);
            } else {
                Map<String, Object> user = (Map<String, Object>) payload.get("user");
                Map eventMap = getEventMap(inputMap);
                Map<String, Object> eventData = (Map<String, Object>) eventMap.get("data");
                inputMap.put("eventMap", eventMap);
                eventData.putAll((Map<String, Object>) inputMap.get("data"));
                eventData.put("postId", HashUtil.generateUUID());
                eventData.put("createDate", new java.util.Date());
                eventData.put("createUserId", user.get("userId"));
            }
        } catch (Exception e) {
            logger.error("Exception:", e);
            throw e;
        } finally {
            graph.shutdown();
        }
        if (error != null) {
            inputMap.put("result", error);
            return false;
        } else {
            // update the bfn tree as the number of posts has changed.
            Map<String, Object> bfnMap = ServiceLocator.getInstance().getMemoryImage("bfnMap");
            ConcurrentMap<Object, Object> cache = (ConcurrentMap<Object, Object>) bfnMap.get("treeCache");
            if (cache != null) {
                cache.remove(host + bfnType);
            }
            return true;
        }
    }

    public boolean addPostEv(String bfnType, Object... objects) throws Exception {
        Map<String, Object> eventMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) eventMap.get("data");
        addPostDb(bfnType, data);
        return true;
    }

    protected void addPostDb(String bfnType, Map<String, Object> data) throws Exception {
        String className = bfnType.substring(0, 1).toUpperCase() + bfnType.substring(1);
        String id = bfnType + "Id";
        String index = className + "." + id;
        String host = (String) data.get("host");
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            graph.begin();
            Vertex createUser = graph.getVertexByKey("User.userId", data.remove("createUserId"));
            OrientVertex post = graph.addVertex("class:Post", data);
            createUser.addEdge("Create", post);
            // parent
            OrientVertex parent = getBranchByHostId(graph, bfnType, host, (String) data.get("parentId"));
            if (parent != null) {
                parent.addEdge("HasPost", post);
            }
            // tag
            Set<String> inputTags = data.get("tags") != null
                    ? new HashSet<String>(Arrays.asList(((String) data.get("tags")).split("\\s*,\\s*")))
                    : new HashSet<String>();
            for (String tagId : inputTags) {
                Vertex tag = null;
                // get the tag is it exists
                OIndex<?> tagHostIdIdx = graph.getRawGraph().getMetadata().getIndexManager()
                        .getIndex("tagHostIdIdx");
                logger.debug("tagHostIdIdx = " + tagHostIdIdx);
                OCompositeKey tagKey = new OCompositeKey(host, tagId);
                logger.debug("tagKey =" + tagKey);
                OIdentifiable tagOid = (OIdentifiable) tagHostIdIdx.get(tagKey);
                if (tagOid != null) {
                    tag = graph.getVertex(tagOid.getRecord());
                    post.addEdge("HasTag", tag);
                } else {
                    tag = graph.addVertex("class:Tag", "host", host, "tagId", tagId, "createDate",
                            data.get("createDate"));
                    createUser.addEdge("Create", tag);
                    post.addEdge("HasTag", tag);
                }
            }
            graph.commit();
        } catch (Exception e) {
            logger.error("Exception:", e);
            graph.rollback();
        } finally {
            graph.shutdown();
        }
    }

    public boolean delPost(String bfnType, Object... objects) throws Exception {
        Map<String, Object> inputMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) inputMap.get("data");
        String rid = (String) data.get("@rid");
        String host = (String) data.get("host");
        String error = null;
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            OrientVertex post = (OrientVertex) DbService.getVertexByRid(graph, rid);
            if (post != null) {
                // check if the post has comment, if yes, you cannot delete it for now
                // TODO fix it after orientdb 2.2 release.
                // https://github.com/orientechnologies/orientdb/issues/1108
                if (post.countEdges(Direction.OUT, "HasComment") > 0) {
                    error = "Post has comment(s), cannot be deleted";
                    inputMap.put("responseCode", 400);
                } else {
                    Map eventMap = getEventMap(inputMap);
                    Map<String, Object> eventData = (Map<String, Object>) eventMap.get("data");
                    inputMap.put("eventMap", eventMap);
                    eventData.put("postId", post.getProperty("postId"));
                }
            } else {
                error = "@rid " + rid + " cannot be found";
                inputMap.put("responseCode", 404);
            }
        } catch (Exception e) {
            logger.error("Exception:", e);
            throw e;
        } finally {
            graph.shutdown();
        }
        if (error != null) {
            inputMap.put("result", error);
            return false;
        } else {
            // update the bfn tree as the number of posts has changed.
            Map<String, Object> bfnMap = ServiceLocator.getInstance().getMemoryImage("bfnMap");
            ConcurrentMap<Object, Object> cache = (ConcurrentMap<Object, Object>) bfnMap.get("treeCache");
            if (cache != null) {
                cache.remove(host + bfnType);
            }
            return true;
        }
    }

    public boolean delPostEv(String bfnType, Object... objects) throws Exception {
        Map<String, Object> eventMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) eventMap.get("data");
        delPostDb(bfnType, data);
        return true;
    }

    protected void delPostDb(String bfnType, Map<String, Object> data) throws Exception {
        String className = bfnType.substring(0, 1).toUpperCase() + bfnType.substring(1);
        String id = bfnType + "Id";
        String index = className + "." + id;
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            graph.begin();
            OrientVertex post = (OrientVertex) graph.getVertexByKey("Post.postId", data.get("postId"));
            if (post != null) {
                // TODO cascade deleting all comments belong to the post.
                // Need to come up a query on that to get the entire tree.
                /*
                // https://github.com/orientechnologies/orientdb/issues/1108
                delete graph...
                */
                graph.removeVertex(post);
            }
            graph.commit();
        } catch (Exception e) {
            logger.error("Exception:", e);
            graph.rollback();
        } finally {
            graph.shutdown();
        }
    }

    public boolean updPost(String bfnType, Object... objects) throws Exception {
        Map<String, Object> inputMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) inputMap.get("data");
        String rid = (String) data.get("@rid");
        String host = (String) data.get("host");
        String error = null;
        Map<String, Object> payload = (Map<String, Object>) inputMap.get("payload");
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            // update post itself and we might have a new api to move post from one parent to another.
            Vertex post = DbService.getVertexByRid(graph, rid);
            if (post != null) {
                Map<String, Object> user = (Map<String, Object>) payload.get("user");
                Map eventMap = getEventMap(inputMap);
                Map<String, Object> eventData = (Map<String, Object>) eventMap.get("data");
                inputMap.put("eventMap", eventMap);
                eventData.put("postId", post.getProperty("postId"));
                eventData.put("title", data.get("title"));
                eventData.put("source", data.get("source"));
                eventData.put("summary", data.get("summary"));
                eventData.put("content", data.get("content"));
                eventData.put("updateDate", new java.util.Date());
                eventData.put("updateUserId", user.get("userId"));
                // tags
                Set<String> inputTags = data.get("tags") != null
                        ? new HashSet<String>(Arrays.asList(((String) data.get("tags")).split("\\s*,\\s*")))
                        : new HashSet<String>();
                Set<String> storedTags = new HashSet<String>();
                for (Vertex vertex : (Iterable<Vertex>) post.getVertices(Direction.OUT, "HasTag")) {
                    storedTags.add((String) vertex.getProperty("tagId"));
                }

                Set<String> addTags = new HashSet<String>(inputTags);
                Set<String> delTags = new HashSet<String>(storedTags);
                addTags.removeAll(storedTags);
                delTags.removeAll(inputTags);

                if (addTags.size() > 0)
                    eventData.put("addTags", addTags);
                if (delTags.size() > 0)
                    eventData.put("delTags", delTags);
            } else {
                error = "@rid " + rid + " cannot be found";
                inputMap.put("responseCode", 404);
            }
        } catch (Exception e) {
            logger.error("Exception:", e);
            throw e;
        } finally {
            graph.shutdown();
        }
        if (error != null) {
            inputMap.put("result", error);
            return false;
        } else {
            // update the bfn tree as the last update time has changed.
            Map<String, Object> bfnMap = ServiceLocator.getInstance().getMemoryImage("bfnMap");
            ConcurrentMap<Object, Object> cache = (ConcurrentMap<Object, Object>) bfnMap.get("treeCache");
            if (cache != null) {
                cache.remove(host + bfnType);
            }
            return true;
        }
    }

    public boolean updPostEv(String bfnType, Object... objects) throws Exception {
        Map<String, Object> eventMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) eventMap.get("data");
        updPostDb(bfnType, data);
        return true;
    }

    protected void updPostDb(String bfnType, Map<String, Object> data) throws Exception {
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            graph.begin();
            Vertex updateUser = graph.getVertexByKey("User.userId", data.remove("updateUserId"));
            OrientVertex post = (OrientVertex) graph.getVertexByKey("Post.postId", data.get("postId"));
            if (post != null) {
                updateUser.addEdge("Update", post);
                // fields
                if (data.get("title") != null) {
                    post.setProperty("title", data.get("title"));
                } else {
                    post.removeProperty("name");
                }
                if (data.get("source") != null) {
                    post.setProperty("source", data.get("source"));
                } else {
                    post.removeProperty("source");
                }
                if (data.get("summary") != null) {
                    post.setProperty("summary", data.get("summary"));
                } else {
                    post.removeProperty("summary");
                }
                if (data.get("content") != null) {
                    post.setProperty("content", data.get("content"));
                } else {
                    post.removeProperty("content");
                }
                post.setProperty("updateDate", data.get("updateDate"));

                // handle addTags and delTags
                OIndex<?> hostIdIdx = graph.getRawGraph().getMetadata().getIndexManager().getIndex("tagHostIdIdx");
                Set<String> addTags = (Set) data.get("addTags");
                if (addTags != null) {
                    for (String tagId : addTags) {
                        OCompositeKey key = new OCompositeKey(data.get("host"), tagId);
                        OIdentifiable oid = (OIdentifiable) hostIdIdx.get(key);
                        if (oid != null) {
                            OrientVertex tag = (OrientVertex) oid.getRecord();
                            post.addEdge("HasTag", tag);
                        } else {
                            Vertex tag = graph.addVertex("class:Tag", "host", data.get("host"), "tagId", tagId,
                                    "createDate", data.get("createDate"));
                            updateUser.addEdge("Create", tag);
                            post.addEdge("HasTag", tag);
                        }
                    }
                }
                Set<String> delTags = (Set) data.get("delTags");
                if (delTags != null) {
                    for (String tagId : delTags) {
                        OCompositeKey key = new OCompositeKey(data.get("host"), tagId);
                        OIdentifiable oid = (OIdentifiable) hostIdIdx.get(key);
                        if (oid != null) {
                            OrientVertex tag = (OrientVertex) oid.getRecord();
                            for (Edge edge : (Iterable<Edge>) post.getEdges(Direction.OUT, "HasTag")) {
                                if (edge.getVertex(Direction.IN).equals(tag))
                                    graph.removeEdge(edge);
                            }
                        }
                    }
                }
            }
            graph.commit();
        } catch (Exception e) {
            logger.error("Exception:", e);
            graph.rollback();
        } finally {
            graph.shutdown();
        }
    }

    public boolean getBfnPost(Object... objects) throws Exception {
        Map<String, Object> inputMap = (Map<String, Object>) objects[0];
        Map<String, Object> data = (Map<String, Object>) inputMap.get("data");
        String rid = (String) data.get("@rid");
        String host = (String) data.get("host");
        if (rid == null) {
            inputMap.put("result", "@rid is required");
            inputMap.put("responseCode", 400);
            return false;
        }
        Integer pageSize = (Integer) data.get("pageSize");
        Integer pageNo = (Integer) data.get("pageNo");
        if (pageSize == null) {
            inputMap.put("result", "pageSize is required");
            inputMap.put("responseCode", 400);
            return false;
        }
        if (pageNo == null) {
            inputMap.put("result", "pageNo is required");
            inputMap.put("responseCode", 400);
            return false;
        }
        String sortDir = (String) data.get("sortDir");
        String sortedBy = (String) data.get("sortedBy");
        if (sortDir == null) {
            sortDir = "desc";
        }
        if (sortedBy == null) {
            sortedBy = "createDate";
        }
        boolean allowPost = false;
        Map<String, Object> payload = (Map<String, Object>) inputMap.get("payload");
        if (payload != null) {
            Map<String, Object> user = (Map<String, Object>) payload.get("user");
            List roles = (List) user.get("roles");
            if (roles.contains("owner")) {
                allowPost = true;
            } else if (roles.contains("admin") || roles.contains("blogAdmin") || roles.contains("blogUser")) {
                if (host.equals(user.get("host"))) {
                    allowPost = true;
                }
            }
        }

        // TODO support the following lists: recent, popular, controversial
        // Get the page from cache.
        List<String> list = null;
        Map<String, Object> bfnMap = ServiceLocator.getInstance().getMemoryImage("bfnMap");
        ConcurrentMap<Object, Object> listCache = (ConcurrentMap<Object, Object>) bfnMap.get("listCache");
        if (listCache == null) {
            listCache = new ConcurrentLinkedHashMap.Builder<Object, Object>().maximumWeightedCapacity(1000).build();
            bfnMap.put("listCache", listCache);
        } else {
            list = (List<String>) listCache.get(rid + sortedBy);
        }

        ConcurrentMap<Object, Object> postCache = (ConcurrentMap<Object, Object>) bfnMap.get("postCache");
        if (postCache == null) {
            postCache = new ConcurrentLinkedHashMap.Builder<Object, Object>().maximumWeightedCapacity(1000).build();
            bfnMap.put("postCache", postCache);
        }

        if (list == null) {
            // get the list for db
            list = new ArrayList<String>();
            String json = getBfnPostDb(rid, sortedBy);
            if (json != null) {
                // convert json to list of maps.
                List<Map<String, Object>> posts = mapper.readValue(json,
                        new TypeReference<ArrayList<HashMap<String, Object>>>() {
                        });
                for (Map<String, Object> post : posts) {
                    String postRid = (String) post.get("rid");
                    list.add(postRid);
                    post.remove("@rid");
                    post.remove("@type");
                    post.remove("@version");
                    post.remove("@fieldTypes");
                    postCache.put(postRid, post);
                }
            }
            listCache.put(rid + sortedBy, list);
        }
        long total = list.size();
        if (total > 0) {
            List<Map<String, Object>> posts = new ArrayList<Map<String, Object>>();
            for (int i = pageSize * (pageNo - 1); i < Math.min(pageSize * pageNo, list.size()); i++) {
                String postRid = list.get(i);
                Map<String, Object> post = (Map<String, Object>) postCache.get(postRid);
                posts.add(post);
            }
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("total", total);
            result.put("posts", posts);
            result.put("allowPost", allowPost);
            inputMap.put("result", mapper.writeValueAsString(result));
            return true;
        } else {
            // there is no post available. but still need to return allowPost
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("total", 0);
            result.put("allowPost", allowPost);
            inputMap.put("result", mapper.writeValueAsString(result));
            return true;
        }
    }

    protected String getBfnPostDb(String rid, String sortedBy) {
        String json = null;
        // TODO there is a bug that prepared query only support one parameter. That is why sortedBy is concat into the sql.
        String sql = "select @rid, postId, title, content, createDate, parentId, in_Create[0].@rid as createRid, in_Create[0].userId as createUserId "
                + "from (traverse out_Own, out_HasPost from ?) where @class = 'Post' order by " + sortedBy
                + " desc";
        OrientGraph graph = ServiceLocator.getInstance().getGraph();
        try {
            OSQLSynchQuery<ODocument> query = new OSQLSynchQuery<ODocument>(sql);
            List<ODocument> posts = graph.getRawGraph().command(query).execute(rid);
            if (posts.size() > 0) {
                json = OJSONWriter.listToJSON(posts, null);
            }
        } catch (Exception e) {
            logger.error("Exception:", e);
        } finally {
            graph.shutdown();
        }
        return json;
    }

}