RMI Server(服務端)主要包括以下功能:遠程用戶權限驗證管理、遠程服務接口實現類、Neo4j實體映射轉換等。項目目錄結構如下圖所示:
3.2.1 遠程用戶權限驗證管理
3.2.1.1 用戶權限驗證機制
用戶權限驗證機制分為三個層級。
第一級,遠程主機IP地址驗證。檢查是否允許遠程主機IP地址訪問RMI服務。
第二級,遠程用戶信息驗證。檢查用戶名稱和密碼是否正確,用戶是否啟用等。
第三級,遠程服務及接口方法驗證。檢查用戶是否有權訪問某個RMI服務以及服務下的指定接口方法。
3.2.1.2 遠程用戶配置信息
遠程用戶配置信息在文件remote.users.config.xml中,內容格式如下表所示:
<?xml version="1.0"?> <remote-users> <remote-user user-id="1" login-name="admin" password="admin" user-name="管理員用戶" enabled="true"></remote-user> <remote-user user-id="2" login-name="test" password="test" user-name="測試用戶" enabled="true"></remote-user> </remote-users>
3.2.1.3 遠程主機配置信息
遠程主機配置信息在文件remote.hosts.config.xml中,內容格式如下表所示:
<?xml version="1.0"?> <hosts> <allow-hosts> <host>*</host> </allow-hosts> <forbid-hosts> <host></host> </forbid-hosts> </hosts>
3.2.1.4 用戶權限配置信息
用戶權限配置信息在文件remote.users.permission.xml中,內容格式如下表所示:
<?xml version="1.0"?> <remote-users> <remote-user login-name="admin,test"> <remote-service name="neo4j-graph-manage-service"> <allow-methods> <method>*</method> </allow-methods> <forbid-methods> <method></method> </forbid-methods> </remote-service> <remote-service name="neo4j-graph-node-service"> <allow-methods> <method>*</method> </allow-methods> <forbid-methods> <method></method> </forbid-methods> </remote-service> <remote-service name="neo4j-graph-index-service"> <allow-methods> <method>*</method> </allow-methods> <forbid-methods> <method></method> </forbid-methods> </remote-service> <remote-service name="neo4j-graph-path-service"> <allow-methods> <method>*</method> </allow-methods> <forbid-methods> <method></method> </forbid-methods> </remote-service> <remote-service name="neo4j-graph-cypher-service"> <allow-methods> <method>*</method> </allow-methods> <forbid-methods> <method></method> </forbid-methods> </remote-service> </remote-user> </remote-users>
3.2.2 遠程服務接口實現類
絕大部分的非業務類工作都是在遠程服務基礎接口實現類BaseRemoteServiceImpl中完成了,譬如,獲取圖數據庫服務對象實例、用戶權限驗證、日志記錄、Neo4j實體映射轉換等。如下表所示:
package com.hnepri.neo4j.rmi.service; import java.rmi.RemoteException; import java.rmi.server.RemoteServer; import java.rmi.server.ServerNotActiveException; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; import org.apache.commons.lang.StringUtils; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.Transaction; import com.hnepri.common.util.DateTimeUtil; import com.hnepri.neo4j.client.rmi.bean.GDirection; import com.hnepri.neo4j.client.rmi.bean.GNode; import com.hnepri.neo4j.client.rmi.bean.GPage; import com.hnepri.neo4j.client.rmi.bean.GPath; import com.hnepri.neo4j.client.rmi.bean.GRelationship; import com.hnepri.neo4j.client.rmi.service.IBaseRemoteService; import com.hnepri.neo4j.client.rmi.util.RemoteClientFactory; import com.hnepri.neo4j.client.rmi.util.RemoteLoginStatusUtil; import com.hnepri.neo4j.common.model.GraphPageModel; import com.hnepri.neo4j.common.util.GraphTemplate; import com.hnepri.neo4j.rmi.bean.RemoteUser; import com.hnepri.neo4j.rmi.util.RemoteHostUtil; import com.hnepri.neo4j.rmi.util.RemoteServerFactory; import com.hnepri.neo4j.rmi.util.RemoteUserPermissionUtil; import com.hnepri.neo4j.rmi.util.RemoteUserUtil; /** * Description: 遠程服務基類實現類<br> * Copyright: Copyright (c) 2015<br> * Company: 河南電力科學研究院智能電網所<br> * @author shangbingbing 2015-09-01編寫 * @version 1.0 */ @SuppressWarnings("deprecation") public class BaseRemoteServiceImpl extends UnicastRemoteObject implements IBaseRemoteService { private static final long serialVersionUID = 7292764643219275924L; private String loginName = ""; private String password = ""; private String serviceName = ""; private String clientAddress = ""; private boolean loginStatus = false; private String loginStatusMessage = ""; private String graphName; private String graphPath; /** * 獲取圖數據庫操作實例GraphTemplate * @return */ public GraphTemplate getTemplate() { if(StringUtils.isNotBlank(this.getGraphName())) { return GraphTemplate.getInstanceByName(this.getGraphName()); } else { return GraphTemplate.getInstance(this.getGraphPath()); } } public BaseRemoteServiceImpl() throws RemoteException { super(); } /** * 獲取登錄用戶名稱 * @return */ protected String getLoginName() { return loginName; } /** * 獲取登錄用戶密碼 * @return */ protected String getPassword() { return password; } /** * 獲取遠程服務名稱(主要用於記錄日志) * @return */ protected String getServiceName() { return serviceName; } /** * 設置遠程服務名稱(主要用於記錄日志) * @param serviceName */ protected void setServiceName(String serviceName) { this.serviceName = serviceName; } protected String getClientAddress() { try { this.clientAddress = RemoteServer.getClientHost(); } catch (ServerNotActiveException e) { e.printStackTrace(); } return clientAddress; } /** * 獲取用戶登錄狀態。 * @return */ protected boolean getLoginStatus() { return loginStatus; } /** * 獲取用戶登錄狀態信息。 * @return */ protected String getLoginStatusMessage() { return loginStatusMessage; } /** * 獲取當前數據庫名稱。 * @return */ public String getGraphName() { return graphName; } /** * 獲取當前數據庫路徑。 * @return */ public String getGraphPath() { return graphPath; } /** * 檢查遠程調用方法的權限 * @param methodName */ protected void checkRemoteMethodPermission(String methodName) throws RemoteException { boolean hasPermission = RemoteUserPermissionUtil.isCanAccessServiceMethod(this.getLoginName(), this.getServiceName(), methodName); if(hasPermission) { String log = String.format("%s\t來自【%s】的用戶【%s】調用%s中的接口方法【%s】一次!", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName(), methodName); RemoteServerFactory.addLog(log); } else { String log = String.format("警告:來自【%s】的用戶【%s】無權調用%s中的接口方法【%s】!", this.getClientAddress(), this.getLoginName(), this.getServiceName(), methodName); RemoteServerFactory.addLog(log); throw new RemoteException(log); } } @Override public String remoteTest() throws RemoteException { return RemoteClientFactory.REMOTE_CALL_STATUS_SUCCESS; } @Override public int login(String loginName, String password) throws RemoteException { this.loginName = loginName; this.password = password; RemoteServerFactory.addLog(String.format("%s\t來自【%s】的用戶【%s】正在驗證訪問【%s】服務!", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName())); int loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_SUCCESS; if(RemoteHostUtil.isAllowHostAccess(this.getClientAddress())) { if(StringUtils.isBlank(loginName) || StringUtils.isBlank(password)) { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_PASSWORD_EMPTY; } else { if(RemoteUserUtil.getRemoteUserList().containsKey(loginName)) { if(RemoteUserUtil.getRemoteUserList().get(loginName).isEnabled()) { RemoteUser user = RemoteUserUtil.getRemoteUserList().get(loginName); if(user.getPassword().equals(password)) { if(RemoteUserPermissionUtil.isCanAccessService(loginName, this.getServiceName())) { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_SUCCESS; if(RemoteUserUtil.getOnlineRemoteHostList().containsKey(this.getClientAddress())) { RemoteUserUtil.getOnlineRemoteHostList().remove(this.getClientAddress()); } RemoteUserUtil.getOnlineRemoteHostList().put(this.getClientAddress(), DateTimeUtil.getFormatDateTime(new Date())); if(RemoteUserUtil.getOnlineRemoteUserList().containsKey(this.getLoginName())) { RemoteUserUtil.getOnlineRemoteUserList().remove(this.getLoginName()); } RemoteUserUtil.getOnlineRemoteUserList().put(this.getLoginName(), DateTimeUtil.getFormatDateTime(new Date())); } else { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_PERMISSION_ERROR; } } else { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_PASSWORD_ERROR; } } else { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_ERROR; } } else { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_USER_ERROR; } } } else { loginStatusValue = RemoteLoginStatusUtil.LOGIN_STATUS_IP_PERMISSION_ERROR; } this.loginStatusMessage = RemoteClientFactory.parseLoginStatusValue(loginStatusValue); if(loginStatusValue == 0) { this.loginStatus = true; RemoteServerFactory.addLog(String.format("%s\t來自【%s】的用戶【%s】通過【%s】服務的訪問驗證!", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName())); } else { this.loginStatus = false; RemoteServerFactory.addLog(String.format("%s\t來自【%s】的用戶【%s】未通過【%s】服務的訪問驗證。\r\n%s", DateTimeUtil.getFormatDateTime(new Date()), this.getClientAddress(), this.getLoginName(), this.getServiceName(), this.getLoginStatusMessage())); } return loginStatusValue; } @Override public void initGraphName(String graphName) throws RemoteException { this.graphName = graphName; } @Override public void initGraphPath(String graphPath) throws RemoteException { this.graphPath = graphPath; } }
3.2.3 Neo4j實體映射轉換
Neo4j實體映射轉換,即將Neo4j原生的接口類對象映射轉換為我們在RMI Client中自定義的可序列化的遠程服務實體類。主要轉換方法如下所示。
/** * 將Node轉換為GNode對象。<br> * <b>【備注:未進行事務處理】</b> * @param node * @return */ protected GNode convertNodeToGNode(Node node) { if(node == null) { return null; } GNode gnode = new GNode(); gnode.setId(node.getId()); gnode.setDegree(node.getDegree()); Iterator<Label> itLabel = node.getLabels().iterator(); while(itLabel.hasNext()) { Label label = itLabel.next(); gnode.getLabelNameList().add(label.name()); } for(String name : node.getAllProperties().keySet()) { Object value = node.getProperty(name); gnode.getPropertyList().put(name, value); } Iterator<Relationship> itRelationship = node.getRelationships().iterator(); while(itRelationship.hasNext()) { Relationship rel = itRelationship.next(); String relType = rel.getType().name(); gnode.getRelationshipList().add(rel.getId()); if(gnode.getRelationshipTypeList().containsKey(relType)) { gnode.getRelationshipTypeList().get(relType).add(rel.getId()); } else { ArrayList<Long> list = new ArrayList<Long>(); list.add(rel.getId()); gnode.getRelationshipTypeList().put(relType, list); } } Iterator<Relationship> itRelationshipIncoming = node.getRelationships(Direction.INCOMING).iterator(); ArrayList<Long> incomingList = new ArrayList<Long>(); while(itRelationshipIncoming.hasNext()) { Relationship rel = itRelationshipIncoming.next(); incomingList.add(rel.getId()); } gnode.getRelationshipDirectionList().put(Direction.INCOMING.name(), incomingList); Iterator<Relationship> itRelationshipOutgoing = node.getRelationships(Direction.OUTGOING).iterator(); ArrayList<Long> outgoingList = new ArrayList<Long>(); while(itRelationshipOutgoing.hasNext()) { Relationship rel = itRelationshipOutgoing.next(); outgoingList.add(rel.getId()); } gnode.getRelationshipDirectionList().put(Direction.OUTGOING.name(), outgoingList); return gnode; } /** * 將Node對象轉換為GNode對象。<br> * <b>【備注:已進行事務處理】</b> * @param nodes * @return */ public List<GNode> parseNodes(List<Node> nodes) { List<GNode> gNodeList = new ArrayList<GNode>(); if(nodes == null || nodes.size() == 0) return gNodeList; if(this.getTemplate() == null) return gNodeList; Transaction tx = this.getTemplate().createTransaction(); try { for(Node node : nodes) { GNode gnode = this.convertNodeToGNode(node); if(gnode == null) continue; gNodeList.add(gnode); } } catch (Exception ex) { ex.printStackTrace(); tx.failure(); } finally { tx.finish(); } return gNodeList; } /** * 將Node對象轉換為GNode對象。<br> * <b>【備注:已進行事務處理】</b> * @param node * @return */ public GNode parseNode(Node node) { List<Node> nodes = Arrays.asList(node); List<GNode> gNodeList = this.parseNodes(nodes); if(gNodeList == null || gNodeList.size() == 0) { return null; } else { return gNodeList.get(0); } } /** * 根據編碼解析節點對象,將其轉為GNode對象。<br> * <b>【備注:已進行事務處理】</b> * @param nodeIDs * @return */ public List<GNode> parseNodesByID(List<Long> nodeIDs) { List<GNode> gNodeList = new ArrayList<GNode>(); if(nodeIDs == null || nodeIDs.size() == 0) return gNodeList; if(this.getTemplate() == null) return gNodeList; Transaction tx = this.getTemplate().createTransaction(); try { for(long nodeID : nodeIDs) { Node node = this.getTemplate().getGraphDBService().getNodeById(nodeID); GNode gnode = this.convertNodeToGNode(node); if(gnode == null) continue; gNodeList.add(gnode); } } catch (Exception ex) { ex.printStackTrace(); tx.failure(); } finally { tx.finish(); } return gNodeList; } /** * 根據編碼解析節點對象,將其轉為GNode對象。<br> * <b>【備注:已進行事務處理】</b> * @param nodeID * @return */ public GNode parseNodeByID(long nodeID) { List<Long> nodeIDs = Arrays.asList(nodeID); List<GNode> gNodeList = this.parseNodesByID(nodeIDs); if(gNodeList == null || gNodeList.size() == 0) { return null; } else { return gNodeList.get(0); } } /** * 將Relationship轉換為GRelationship對象。<br> * <b>【備注:未進行事務處理】</b> * @param relationship * @return */ protected GRelationship convertRelToGRel(Relationship relationship) { if(relationship == null) { return null; } GRelationship grelationship = new GRelationship(); grelationship.setId(relationship.getId()); grelationship.setStartNodeID(relationship.getStartNode().getId()); grelationship.setEndNodeID(relationship.getEndNode().getId()); grelationship.setRelationshipType(relationship.getType().name()); for(String name : relationship.getAllProperties().keySet()) { Object value = relationship.getProperty(name); grelationship.getPropertyList().put(name, value); } return grelationship; } /** * 將Relationship對象轉換為GRelationship對象。<br> * <b>【備注:已進行事務處理】</b> * @param relationships * @return */ public List<GRelationship> parseRelationships(List<Relationship> relationships) { List<GRelationship> gRelationshipList = new ArrayList<GRelationship>(); if(relationships == null || relationships.size() == 0) return gRelationshipList; if(this.getTemplate() == null) return gRelationshipList; Transaction tx = this.getTemplate().createTransaction(); try { for(Relationship relationship : relationships) { GRelationship grelationship = this.convertRelToGRel(relationship); if(grelationship == null) continue; gRelationshipList.add(grelationship); } } catch (Exception ex) { ex.printStackTrace(); tx.failure(); } finally { tx.finish(); } return gRelationshipList; } /** * 將Relationship對象轉換為GRelationship對象。<br> * <b>【備注:已進行事務處理】</b> * @param relationship * @return */ public GRelationship parseRelationship(Relationship relationship) { List<Relationship> relationships = Arrays.asList(relationship); List<GRelationship> gRelationshipList = this.parseRelationships(relationships); if(gRelationshipList == null || gRelationshipList.size() == 0) { return null; } else { return gRelationshipList.get(0); } } /** * 根據編碼解析關系對象,將其轉為GRelationship對象。<br> * <b>【備注:已進行事務處理】</b> * @param relationshipIDs * @return */ public List<GRelationship> parseRelationshipsByID(List<Long> relationshipIDs) { List<GRelationship> gRelationshipList = new ArrayList<GRelationship>(); if(relationshipIDs == null || relationshipIDs.size() == 0) return gRelationshipList; if(this.getTemplate() == null) return gRelationshipList; Transaction tx = this.getTemplate().createTransaction(); try { for(long relationshipID : relationshipIDs) { Relationship relationship = this.getTemplate().getGraphDBService().getRelationshipById(relationshipID); GRelationship grelationship = this.convertRelToGRel(relationship); if(grelationship == null) continue; gRelationshipList.add(grelationship); } } catch (Exception ex) { ex.printStackTrace(); tx.failure(); } finally { tx.finish(); } return gRelationshipList; } /** * 根據編碼解析關系對象,將其轉為GRelationship對象。<br> * <b>【備注:已進行事務處理】</b> * @param relationshipID * @return */ public GRelationship parseRelationshipByID(long relationshipID) { List<Long> relationshipIDs = Arrays.asList(relationshipID); List<GRelationship> gRelationshipList = this.parseRelationshipsByID(relationshipIDs); if(gRelationshipList == null || gRelationshipList.size() == 0) { return null; } else { return gRelationshipList.get(0); } } /** * 將Path對象轉換為GPath對象。<br> * <b>【備注:未進行事務處理】</b> * @param paths * @return */ protected List<GPath> convertPathToGPath(List<Path> paths) { List<GPath> gPathList = new ArrayList<GPath>(); if(paths == null || paths.size() == 0) return gPathList; for(Path path : paths) { GPath gpath = new GPath(); Iterator<Node> itNode = path.nodes().iterator(); while(itNode.hasNext()) { Node node = itNode.next(); GNode gnode = this.convertNodeToGNode(node); if(gnode == null) continue; gpath.getNodes().add(gnode); } Iterator<Relationship> itRelationship = path.relationships().iterator(); while(itRelationship.hasNext()) { Relationship relationship = itRelationship.next(); GRelationship grelationship = this.convertRelToGRel(relationship); if(grelationship == null) continue; gpath.getRelationships().add(grelationship); } gPathList.add(gpath); } return gPathList; } /** * 將Path對象轉換為GPath對象。<br> * <b>【備注:未進行事務處理】</b> * @param path * @return */ protected GPath convertPathToGPath(Path path) { List<Path> paths = Arrays.asList(path); List<GPath> gPathList = this.convertPathToGPath(paths); if(gPathList == null || gPathList.size() == 0) { return null; } else { return gPathList.get(0); } } /** * 將GPage對象轉換為GraphPageModel對象。 * @param gpage * @return */ public GraphPageModel parseGPageToGraphPageModel(GPage gpage) { GraphPageModel pageModel = null; if(gpage == null) { pageModel = new GraphPageModel(20); } else { pageModel = new GraphPageModel(gpage.getPageSize()); pageModel.setPageIndex(gpage.getPageIndex()); pageModel.setTotalCount(gpage.getTotalCount()); } return pageModel; } /** * 將GraphPageModel對象轉換為GPage對象。 * @param pageModel * @return */ public GPage parseGraphPageModelToGPage(GraphPageModel pageModel) { GPage gpage = null; if(pageModel == null) { gpage = new GPage(20); } else { gpage = new GPage(pageModel.getPageSize()); gpage.setPageIndex(pageModel.getPageIndex()); gpage.setTotalCount(pageModel.getTotalCount()); List<GNode> nodeList = this.parseNodes(pageModel.getNodeList()); for(GNode node : nodeList) { gpage.getNodeList().add(node); } List<GRelationship> relationshipList = this.parseRelationships(pageModel.getRelationshipList()); for(GRelationship relationship : relationshipList) { gpage.getRelationshipList().add(relationship); } } return gpage; } /** * 將GDirection對象轉換為Direction對象。 * @param gdirection * @return */ public Direction parseGDirection(GDirection gDirection) { if(gDirection == null) { return null; } if(StringUtils.isBlank(gDirection.getName())) { return null; } return Direction.valueOf(gDirection.getName()); } /** * 將關系類型名稱解析為關系類型枚舉對象。 * @param relationshipType * @return */ public RelationshipType parseRelationshipType(String relationshipType) { if(StringUtils.isBlank(relationshipType)) { return null; } return this.getTemplate().getRelTypeUtil().get(relationshipType); }
3.3. RMI Server Form
RMI Server Form,即RMI服務窗口管理器,是通過窗口方式來管理RMI服務,包括啟動RMI服務,停止RMI服務,監控RMI日志信息,監控遠程登錄用戶信息,初始化相關配置等操作。此功能集成在“圖數據庫管理系統Server端”,功能界面如下圖所示:
【完】
作者:商兵兵
單位:河南省電力科學研究院智能電網所
QQ:52190634