/******************************************************************************* * Copyright (c) 2014 EURA NOVA. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Aldemar Reynaga - initial API and implementation * Salim Jouili - initial API and implementation ******************************************************************************/ package com.steffi.model; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.infinispan.Cache; import org.infinispan.distexec.DefaultExecutorService; import org.infinispan.distexec.DistributedExecutorService; import org.infinispan.remoting.transport.Address; import org.zeromq.ZMQ; import com.steffi.common.Configuration; import com.steffi.common.Configuration.Key; import com.steffi.index.ImgIndex; import com.steffi.index.ImgMapIndex; import com.steffi.loader.ResponseProcessor; import com.steffi.networking.NodeClients; import com.steffi.networking.messages.IdentifiableMessage; import com.steffi.storage.CacheContainer; import com.steffi.storage.CellTransactionFactory; import com.steffi.storage.CellTransactionThread; import com.steffi.storage.GraphNamesManager; import com.steffi.storage.StorageTools; import com.steffi.storage.CellTransaction.TransactionConclusion; /** * @author Aldemar Reynaga * The Singleton class representing the Imgraph data base, this is the main point of access * to the graph functionalities */ public class SteffiGraph implements Serializable { /** * */ private static final long serialVersionUID = -3640095076173432800L; private ImgIndex<SteffiVertex> vertexIndex; private ImgIndex<SteffiEdge> edgeIndex; private boolean serializedCells; private boolean compressCells; private Map<String, Integer> itemNames; private Map<Integer, String> reveresItemNames; private Map<String, Integer> memberIndexes; private int numberOfMembers; private String localAddress; private NodeClients nodeClients; private AtomicInteger traversalManagerTurn; private String[] traversalManagerIps; private final ZMQ.Context context; private static class SingletonHolder { public static final SteffiGraph instance = new SteffiGraph(); } public static SteffiGraph getInstance() { return SingletonHolder.instance; } private SteffiGraph() { context = ZMQ.context(1); vertexIndex = new ImgMapIndex<SteffiVertex>(CacheContainer.VERTEX_INDEX_CACHE_NAME, SteffiVertex.class, false); edgeIndex = new ImgMapIndex<SteffiEdge>(CacheContainer.EDGE_INDEX_CACHE_NAME, SteffiEdge.class, false); serializedCells = Configuration.getProperty(Key.STORE_SERIALIZED_CELLS).equals("true"); compressCells = Configuration.getProperty(Key.COMPRESS_CELLS).equals("true"); itemNames = new ConcurrentHashMap<String, Integer>(); reveresItemNames = new ConcurrentHashMap<Integer, String>(); memberIndexes = new HashMap<String, Integer>(); localAddress = CacheContainer.getCellCache(). getCacheManager().getAddress().toString(); traversalManagerTurn = new AtomicInteger(0); traversalManagerIps = Configuration.getProperty(Key.MANAGER_IPS).split(","); } public ZMQ.Context getZMQContext() { return this.context; } private boolean isLocalCell(long cellId) { return StorageTools.getCellAddress(cellId).equals(localAddress); } public <T extends Cell> ImgIndex<T> getIndex(String indexName, Class<T> className) { return new ImgMapIndex<T>(indexName, className, false); } public ImgIndex<SteffiVertex> createVertexIndex(String indexName) { return new ImgMapIndex<SteffiVertex>(indexName, SteffiVertex.class, true); } public ImgIndex<SteffiEdge> createEdgeIndex(String indexName) { return new ImgMapIndex<SteffiEdge>(indexName, SteffiEdge.class, true); } public boolean isCompressCells() { return compressCells; } public void initializeMemberIndexes() { memberIndexes.clear(); for (Address address : CacheContainer.getCacheContainer().getTransport().getMembers()) memberIndexes.put(address.toString(), memberIndexes.size()); numberOfMembers = memberIndexes.size(); nodeClients = new NodeClients(this); } public int getNextTraversalManagerIndex() { int value = traversalManagerTurn.getAndIncrement(); return Math.abs(value%traversalManagerIps.length) ; } public String[] getTraversalManagerIps() { return traversalManagerIps; } public String getNextManagerIp() { return traversalManagerIps[getNextTraversalManagerIndex()]; } public String getLocalAddress() { return localAddress; } public int getLocalAddressIndex() { return getMemberIndex(localAddress); } public void sendMessageToNode(int memberIndex, IdentifiableMessage message, ResponseProcessor sender) { if (nodeClients == null) throw new RuntimeException("The cluster must be initialized"); nodeClients.sendMessage(memberIndex, message, sender); } public int getMemberIndex(String address) { return getMemberIndexes().get(address); } public Map<String, Integer> getMemberIndexes() { if (memberIndexes == null) throw new RuntimeException("The cluster must be initialized"); return memberIndexes; } public void storeCell(long cellId, Cell cell) { Cache <Long, Object> cache = CacheContainer.getCellCache(); if (storeSerializedCells()) throw new UnsupportedOperationException("Cell serialization is not implemented"); else cache.put(cellId, cell); } public int getNumberOfMembers() { if (memberIndexes.isEmpty()) initializeMemberIndexes(); return numberOfMembers; } public void validateEdgeName(String name) { if (name != null && !name.trim().equals("") && !itemNames.containsKey(name)) throw new RuntimeException("The edge name '" + name + "' has to be registered in the graph"); } public void registerLocalItemName(String name) { int index = itemNames.size(); itemNames.put(name, index); reveresItemNames.put(index, name); } public void registerItemName(String name) { if (itemNames.containsKey(name)) return; try { DistributedExecutorService des = new DefaultExecutorService(CacheContainer.getCellCache()); GraphNamesManager graphNamesManager = new GraphNamesManager(name); List<Future<Boolean>> results = des.submitEverywhere(graphNamesManager); for (Future<Boolean> future : results) { if (!future.get(5, TimeUnit.MINUTES)) throw new RuntimeException("Error registering the new name"); } } catch (Exception e) { throw new RuntimeException(e); } } public int getItemNameIndex(String name) { if (!itemNames.containsKey(name)) return -1; return itemNames.get(name); } public String getItemName(int index) { return reveresItemNames.get(index); } public int getNumberOfEdgeNames() { return itemNames.size(); } public SteffiVertex addVertex(Long id, String vertexName) { SteffiVertex v = new SteffiVertex(id, vertexName); return v; } public void startTransaction() { if (! CellTransactionThread.isTransactionSet()) CellTransactionFactory.beginTransaction(); } public void commit() { stopTransaction(TransactionConclusion.COMMIT); } public void rollback() { stopTransaction(TransactionConclusion.ROLLBACK); } public void stopTransaction(TransactionConclusion transactionConclusion) { if (!CellTransactionThread.isTransactionSet()) return; if (transactionConclusion.equals(TransactionConclusion.COMMIT)) CellTransactionThread.get().commit(); else CellTransactionThread.get().rollback(); } public boolean storeSerializedCells() { return serializedCells; } public ImgIndex<SteffiVertex> getDefaultVertexIndex() { return vertexIndex; } public ImgIndex<SteffiEdge> getDefaultEdgeIndex() { return edgeIndex; } public SteffiVertex getVertex(long vertexId) { return (SteffiVertex) retrieveCell(vertexId); } public Cell retrieveCell(long cellId){ return retrieveCell(cellId, true); } public Cell retrieveRawCell(long cellId) { return retrieveCell(cellId, false); } private Cell retrieveCell(long cellId, boolean transactionSupport){ Cache<Long, Object> cellCache = CacheContainer.getCellCache(); Cell cell = null; Cell rawCell = null; if (CellTransactionThread.isTransactionSet()) { rawCell = CellTransactionThread.get().getCell(cellId); if (rawCell != null) return rawCell; } rawCell = (Cell) cellCache.get(cellId); if (rawCell != null) { if (transactionSupport && isLocalCell(cellId)) { cell = rawCell.clone(); } else { cell = rawCell; } } if (CellTransactionThread.isTransactionSet()) CellTransactionThread.get().addCell(cell); return cell; } public void removeCell(long cellId) { Cache<Long, Cell> cellCache = CacheContainer.getCellCache(); cellCache.remove(cellId); } public Iterable<Long> getCellIds() { throw new UnsupportedOperationException(); } public Iterable<SteffiVertex> getVertices() { throw new UnsupportedOperationException(); } public SteffiVertex getVertexByName(String vertexName) { Cache<Long, Cell> cellCache = CacheContainer.getCellCache(); for (Cell cell : cellCache.values()) if (cell.getName().equals(vertexName) && cell.getCellType().equals(CellType.VERTEX)) return (SteffiVertex) cell; return null; } protected void checkExistingVertex(String id) { if (CacheContainer.getCellCache().containsKey(id)) throw new RuntimeException("There is already a vertex with id: " + id); } public void removeAll() { CacheContainer.getCellCache().clear(); } public void removeIndex (String name) { Cache<Object, Map<Object, Boolean>> indexCache = CacheContainer.getCache(name); indexCache.clear(); } public void closeGraphClients() { if (nodeClients != null) nodeClients.close(); } public <T extends Cell> Iterator <ImgIndex<T>> getUserIndexes(final Class<T> indexClass) { final Iterator<String> nameIterator = CacheContainer.getIndexCacheNames().iterator(); Iterator<ImgIndex<T>> iterator = new Iterator<ImgIndex <T>>() { @Override public void remove() { } @Override public ImgIndex<T> next() { String nextName = nameIterator.next(); return SteffiGraph.getInstance().getIndex(nextName, indexClass); } @Override public boolean hasNext() { return nameIterator.hasNext(); } }; return iterator; } }