/* * Copyright (c) 2008-2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.coordinator.client.service.impl; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.coordinator.client.service.DistributedDataManager; import com.emc.storageos.coordinator.common.impl.ZkConnection; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.state.ConnectionStateListener; /** * DistributedDataManager implementation which allows unrestricted access to the * ZK tree. * * This implementation is only approved for use by the Controller workflow processing. * By using it, that processing takes responsibility for limiting the consumption of * ZK resources in order to prevent an OOM condition. * */ public class WorkflowDataManagerImpl implements DistributedDataManager { private static final Logger _log = LoggerFactory.getLogger(DistributedDataManagerImpl.class); private final CuratorFramework _zkClient; private CuratorListener _listener; private ConnectionStateListener _connectionStateListener; public WorkflowDataManagerImpl(ZkConnection conn) { _zkClient = conn.curator(); _log.info("Unlimited Manager constructed by {}", getCaller()); } @Override public Stat checkExists(String path) { try { Stat stat = _zkClient.checkExists().forPath(path); return stat; } catch (Exception ex) { return null; } } @Override public void createNode(String path, boolean watch) throws Exception { Stat stat = checkExists(path); if (stat == null) { _zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path); } if (_listener != null && watch) { stat = _zkClient.checkExists().watched().forPath(path); } } @Override public void removeNode(String path) throws Exception { Stat stat = checkExists(path); if (stat != null) { List<String> children = getChildren(path); for (String child : children) { _zkClient.delete().guaranteed().forPath(path + "/" + child); } _zkClient.delete().guaranteed().forPath(path); } } @Override public void removeNode(String path, boolean recursive) throws Exception { if (recursive) { Stat stat = checkExists(path); if (stat != null) { _zkClient.delete().deletingChildrenIfNeeded().forPath(path); } } else { removeNode(path); } } @Override public void putData(String path, Object object) throws Exception { Stat stat = checkExists(path); byte[] data = GenericSerializer.serialize(object, path, true); if (stat == null) { _zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, data); } else { _zkClient.setData().forPath(path, data); } } @Override public Object getData(String path, boolean watch) throws Exception { Stat stat = checkExists(path); if (stat == null) { return null; } byte[] bytes = null; if (watch) { bytes = _zkClient.getData().watched().forPath(path); } else { bytes = _zkClient.getData().forPath(path); } if (bytes == null || bytes.length == 0) { return null; } Object obj = GenericSerializer.deserialize(bytes); return obj; } @Override public void setListener(CuratorListener listener) throws Exception { if (_listener != null) { _zkClient.getCuratorListenable().removeListener(_listener); } if (listener != null) { _zkClient.getCuratorListenable().addListener(listener); } _listener = listener; } @Override public void setConnectionStateListener(ConnectionStateListener listener) throws Exception { if (_connectionStateListener != null) { _zkClient.getConnectionStateListenable().removeListener(_connectionStateListener); } if (listener != null) { _zkClient.getConnectionStateListenable().addListener(listener); } _connectionStateListener = listener; } @Override public List<String> getChildren(String path) throws Exception { List<String> children = _zkClient.getChildren().forPath(path); return children; } @Override public void close() { if (_zkClient != null) { try { removeListener(); removeConnectionStateListener(); } catch (Exception ex) { _log.error("Fail to close workflowDataManager, " + "due to remove listener/connectionStateListener failed with exception: {}", ex.getMessage()); } } _log.info("WorkflowDataManager closed."); } private void removeListener() throws Exception { if (_listener != null) { _zkClient.getCuratorListenable().removeListener(_listener); _listener = null; _log.info("Listener removed successfully."); } } private void removeConnectionStateListener() throws Exception { if (_connectionStateListener != null) { _zkClient.getConnectionStateListenable().removeListener(_connectionStateListener); _connectionStateListener = null; _log.info("ConnectionStateListener removed successfully."); } } /** * Identify the class outside of the coordinator package which instantiated this * * @return caller class */ private String getCaller() { String myPackage = "com.emc.storageos.coordinator"; String myClassName = this.getClass().getName(); boolean myClassFound = false; String caller = "unknown"; for (StackTraceElement element : Thread.currentThread().getStackTrace()) { if (!myClassFound && element.getClassName().equals(myClassName)) { myClassFound = true; } else if (myClassFound) { if (!element.getClassName().startsWith(myPackage)) { caller = element.toString(); break; } } } return caller; } }