/* dCache - http://www.dcache.org/ * * Copyright (C) 2015 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.dcache.zookeeper.service; import com.google.common.base.Strings; import org.apache.zookeeper.server.DatadirCleanupManager; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SessionTrackerImpl; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import org.dcache.cells.AbstractCell; import org.dcache.util.Args; import org.dcache.util.Option; import static com.google.common.base.Preconditions.checkArgument; /** * Embedded standalone ZooKeeper as a dCache cell. */ public class ZooKeeperCell extends AbstractCell { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperCell.class); @Option(name = "data-log-dir", required = true) protected File dataLogDir; @Option(name = "data-dir", required = true) protected File dataDir; @Option(name = "tick-time", required = true) protected int tickTime; @Option(name = "tick-time-unit", required = true) protected TimeUnit tickTimeUnit; @Option(name = "min-session-timeout", required = false) protected int minSessionTimeout = -1; @Option(name = "min-session-timeout-unit", required = true) protected TimeUnit minSessionTimeoutUnit; @Option(name = "max-session-timeout", required = false) protected int maxSessionTimeout = -1; @Option(name = "max-session-timeout-unit", required = true) protected TimeUnit maxSessionTimeoutUnit; @Option(name = "max-client-connections", required = true) protected int maxClientConnections; @Option(name = "listen", required = false) protected String address; @Option(name = "port", required = true) protected int port; @Option(name = "autoPurgeRetainCount", required = true) protected int autoPurgeRetainCount; @Option(name = "autoPurgeInterval", required = true) protected int autoPurgeInterval; @Option(name = "autoPurgeIntervalUnit", required = true) protected TimeUnit autoPurgeIntervalUnit; private ServerCnxnFactory cnxnFactory; private FileTxnSnapLog txnLog; private ZooKeeperServer zkServer; public ZooKeeperCell(String cellName, String arguments) { super(cellName, "System", new Args(arguments)); } @Override protected void starting() throws Exception { super.starting(); InetSocketAddress socketAddress = Strings.isNullOrEmpty(address) ? new InetSocketAddress(port) : new InetSocketAddress(address, port); checkArgument(autoPurgeInterval > 0, "zookeeper.auto-purge.purge-interval must be non-negative."); zkServer = new ZooKeeperServer() { // Work around https://issues.apache.org/jira/browse/ZOOKEEPER-2515 @Override protected void createSessionTracker() { sessionTracker = new SessionTrackerImpl(this, getZKDatabase().getSessionWithTimeOuts(), tickTime, 1) { @Override public void shutdown() { synchronized (this) { running = false; notifyAll(); } super.shutdown(); } }; } }; txnLog = new FileTxnSnapLog(dataLogDir, dataDir); zkServer.setTxnLogFactory(txnLog); zkServer.setTickTime((int) tickTimeUnit.toMillis(tickTime)); zkServer.setMinSessionTimeout(minSessionTimeout == -1 ? -1 : (int) minSessionTimeoutUnit.toMillis(minSessionTimeout)); zkServer.setMaxSessionTimeout(maxSessionTimeout == -1 ? -1 : (int) maxSessionTimeoutUnit.toMillis(maxSessionTimeout)); ServerCnxnFactory cnxnFactory; cnxnFactory = new NIOServerCnxnFactory() { @Override protected void configureSaslLogin() throws IOException { // ZooKeeper gets confused by dCache configuring a JAAS configuration without a section for ZooKeeper, so // we disable the whole thing. Use a non-embedded ZooKeeper if you want security. } }; cnxnFactory.configure(socketAddress, maxClientConnections); this.cnxnFactory = cnxnFactory; this.cnxnFactory.startup(zkServer); // FileTxnSnapLog constructor creates dataDir and dataLogDir if they // don't already exist, but in a non-thread safe fashion. // Unfortunately, DatadirCleanupManager#start launches an asynchronous // task that runs immediately and creates a FileTxnSnapLog object. This // can creating a race between the constructor above. To avoid this, // we must call DatadirCleanupManager#start after the FileTxnSnapLog // object has been created. int purgeIntervalHours = (int) TimeUnit.HOURS.convert(autoPurgeInterval, autoPurgeIntervalUnit); DatadirCleanupManager purgeMgr = new DatadirCleanupManager(dataDir.getAbsolutePath(), dataLogDir.getAbsolutePath(), autoPurgeRetainCount, purgeIntervalHours); purgeMgr.start(); } @Override public void stopped() { if (zkServer != null) { zkServer.shutdown(); } // Must shutdown zkServer before shutting down cnxnFactory since // zkServer.shutdown flushes any pending messages while // cnxnFactory.shutdown closes the network sockets. if (cnxnFactory != null) { cnxnFactory.shutdown(); } if (txnLog != null) { try { txnLog.close(); } catch (IOException e) { LOG.error("Failed to close ZooKeeper transaction log: {}", e.toString()); } } super.stopped(); } @Override public void getInfo(PrintWriter printWriter) { printWriter.println("[ZooKeeper configuration]"); zkServer.dumpConf(printWriter); printWriter.println(); printWriter.println("[ZooKeeper statistics]"); printWriter.append(zkServer.serverStats().toString()); } }