package org.talend.esb.locator.server.init.internal; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import static org.talend.esb.locator.server.init.internal.RootNodeACLs.LOCATOR_ROOT_ACLS; import static org.talend.esb.locator.server.init.internal.RootNodeACLs.ZK_ROOT_ACLS; public class RootNodeInitializer implements Watcher { private static final Charset UTF8_CHAR_SET = Charset.forName("UTF-8"); private static final String ZK_ROOT_NODE_PATH = "/"; private static final String ROOT_NODE_PATH = "/cxf-locator"; private static final Logger LOG = Logger.getLogger(RootNodeInitializer.class.getName()); private int timeout = 10000; private String locatorEndpoints = "localhost:2181"; private String version = "5.2.0"; private boolean authentication; private ZooKeeper zk; private CountDownLatch finishedLatch = new CountDownLatch(1); public void setLocatorEndpoints(String endpoints) { locatorEndpoints = endpoints; if (LOG.isLoggable(Level.FINE)) { LOG.fine("Locator endpoints set to " + locatorEndpoints); } } public void setLocatorPort(String port) { locatorEndpoints = "localhost:" + port; if (LOG.isLoggable(Level.FINE)) { LOG.fine("Locator endpoint set to " + locatorEndpoints); } } public void setVersion(String versionNumber) { version = versionNumber; if (LOG.isLoggable(Level.FINE)) { LOG.fine("Version set to " + version); } } public void setAuthentication(boolean auth) { authentication = auth; if (LOG.isLoggable(Level.FINE)) { LOG.fine("authentication is " + authentication); } } public void initialize() throws InterruptedException { try { zk = new ZooKeeper(locatorEndpoints, 5000, this); boolean finished = finishedLatch.await(timeout, TimeUnit.MILLISECONDS); if (! finished && LOG.isLoggable(Level.SEVERE)) { LOG.log(Level.SEVERE, "Failed to connect to ZooKeeper and initialize the root node within " + timeout + " ms."); } } catch (IOException e) { if (LOG.isLoggable(Level.SEVERE)) { LOG.log(Level.SEVERE, "Failed to create ZooKeeper client", e); } } finally { stop(); } } public void stop() throws InterruptedException { if (zk != null) { zk.close(); zk = null; } } @Override public void process(WatchedEvent event) { KeeperState eventState = event.getState(); if (eventState == KeeperState.SyncConnected) { createRootNode(); } else { if (LOG.isLoggable(Level.INFO)) { LOG.log(Level.INFO, "Connect to ZooKeeper failed. ZooKeeper client returned state " + eventState); } } } private void createRootNode() { try { Stat stat = zk.exists(ROOT_NODE_PATH, false); if (stat == null) { zk.create(ROOT_NODE_PATH, getContent(), getLocatorRootACLs(), CreateMode.PERSISTENT); zk.setACL(ZK_ROOT_NODE_PATH, getZKRootACLs(), -1); } else { try { byte[] oldContent = zk.getData(ROOT_NODE_PATH, false, new Stat()); if (contentNeedsUpdate(oldContent)) { zk.setData(ROOT_NODE_PATH, getContent(), -1); zk.setACL(ROOT_NODE_PATH, getLocatorRootACLs(), -1); zk.setACL(ZK_ROOT_NODE_PATH, getZKRootACLs(), -1); } } catch (KeeperException e) { if (e.code().equals(KeeperException.Code.NOAUTH)) { if (LOG.isLoggable(Level.INFO)) { LOG.log(Level.INFO, "Service Locator already requires authentication. Configuration settings" + " for the Service Locator root cannot be applied anymore."); } else { throw e; } } } } } catch (KeeperException e) { if (LOG.isLoggable(Level.SEVERE)) { LOG.log(Level.SEVERE, "Failed to create RootNode", e); } } catch (InterruptedException e) { if (LOG.isLoggable(Level.SEVERE)) { LOG.log(Level.SEVERE, "Thread got interrupted when wating for root node to be created.", e); } } finishedLatch.countDown(); } private byte [] getContent() { String contentAsStr = version + "," + Boolean.toString(authentication); return contentAsStr.getBytes(UTF8_CHAR_SET); } private boolean contentNeedsUpdate(byte[] oldContent) { String oldVersion= null; boolean oldAuthentication = false; String contentStr = new String(oldContent, UTF8_CHAR_SET); String[] parts = contentStr.split(","); if (parts.length == 2) { oldVersion = parts[0]; oldAuthentication = Boolean.parseBoolean(parts[1]); return (! oldAuthentication && authentication) || ! oldVersion.equals(version); } else { return false; } } private List<ACL> getLocatorRootACLs() { return authentication ? LOCATOR_ROOT_ACLS : Ids.OPEN_ACL_UNSAFE; } private List<ACL> getZKRootACLs() { return authentication ? ZK_ROOT_ACLS : Ids.OPEN_ACL_UNSAFE; } }