1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.zookeeper.server;
20 
21 import java.io.IOException;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.TimeUnit;
24 import javax.management.JMException;
25 import org.apache.yetus.audience.InterfaceAudience;
26 import org.apache.zookeeper.audit.ZKAuditProvider;
27 import org.apache.zookeeper.jmx.ManagedUtil;
28 import org.apache.zookeeper.metrics.MetricsProvider;
29 import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException;
30 import org.apache.zookeeper.metrics.impl.MetricsProviderBootstrap;
31 import org.apache.zookeeper.server.admin.AdminServer;
32 import org.apache.zookeeper.server.admin.AdminServer.AdminServerException;
33 import org.apache.zookeeper.server.admin.AdminServerFactory;
34 import org.apache.zookeeper.server.auth.ProviderRegistry;
35 import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
36 import org.apache.zookeeper.server.persistence.FileTxnSnapLog.DatadirException;
37 import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
38 import org.apache.zookeeper.server.util.JvmPauseMonitor;
39 import org.apache.zookeeper.util.ServiceUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42 
43 /**
44  * This class starts and runs a standalone ZooKeeperServer.
45  */
46 @InterfaceAudience.Public
47 public class ZooKeeperServerMain {
48 
49     private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerMain.class);
50 
51     private static final String USAGE = "Usage: ZooKeeperServerMain configfile | port datadir [ticktime] [maxcnxns]";
52 
53     // ZooKeeper server supports two kinds of connection: unencrypted and encrypted.
54     private ServerCnxnFactory cnxnFactory;
55     private ServerCnxnFactory secureCnxnFactory;
56     private ContainerManager containerManager;
57     private MetricsProvider metricsProvider;
58     private AdminServer adminServer;
59 
60     /*
61      * Start up the ZooKeeper server.
62      *
63      * @param args the configfile or the port datadir [ticktime]
64      */
main(String[] args)65     public static void main(String[] args) {
66         ZooKeeperServerMain main = new ZooKeeperServerMain();
67         try {
68             main.initializeAndRun(args);
69         } catch (IllegalArgumentException e) {
70             LOG.error("Invalid arguments, exiting abnormally", e);
71             LOG.info(USAGE);
72             System.err.println(USAGE);
73             ZKAuditProvider.addServerStartFailureAuditLog();
74             ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
75         } catch (ConfigException e) {
76             LOG.error("Invalid config, exiting abnormally", e);
77             System.err.println("Invalid config, exiting abnormally");
78             ZKAuditProvider.addServerStartFailureAuditLog();
79             ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue());
80         } catch (DatadirException e) {
81             LOG.error("Unable to access datadir, exiting abnormally", e);
82             System.err.println("Unable to access datadir, exiting abnormally");
83             ZKAuditProvider.addServerStartFailureAuditLog();
84             ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue());
85         } catch (AdminServerException e) {
86             LOG.error("Unable to start AdminServer, exiting abnormally", e);
87             System.err.println("Unable to start AdminServer, exiting abnormally");
88             ZKAuditProvider.addServerStartFailureAuditLog();
89             ServiceUtils.requestSystemExit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue());
90         } catch (Exception e) {
91             LOG.error("Unexpected exception, exiting abnormally", e);
92             ZKAuditProvider.addServerStartFailureAuditLog();
93             ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
94         }
95         LOG.info("Exiting normally");
96         ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue());
97     }
98 
initializeAndRun(String[] args)99     protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException {
100         try {
101             ManagedUtil.registerLog4jMBeans();
102         } catch (JMException e) {
103             LOG.warn("Unable to register log4j JMX control", e);
104         }
105 
106         ServerConfig config = new ServerConfig();
107         if (args.length == 1) {
108             config.parse(args[0]);
109         } else {
110             config.parse(args);
111         }
112 
113         runFromConfig(config);
114     }
115 
116     /**
117      * Run from a ServerConfig.
118      * @param config ServerConfig to use.
119      * @throws IOException
120      * @throws AdminServerException
121      */
runFromConfig(ServerConfig config)122     public void runFromConfig(ServerConfig config) throws IOException, AdminServerException {
123         LOG.info("Starting server");
124         FileTxnSnapLog txnLog = null;
125         try {
126             try {
127                 metricsProvider = MetricsProviderBootstrap.startMetricsProvider(
128                     config.getMetricsProviderClassName(),
129                     config.getMetricsProviderConfiguration());
130             } catch (MetricsProviderLifeCycleException error) {
131                 throw new IOException("Cannot boot MetricsProvider " + config.getMetricsProviderClassName(), error);
132             }
133             ServerMetrics.metricsProviderInitialized(metricsProvider);
134             ProviderRegistry.initialize();
135             // Note that this thread isn't going to be doing anything else,
136             // so rather than spawning another thread, we will just call
137             // run() in this thread.
138             // create a file logger url from the command line args
139             txnLog = new FileTxnSnapLog(config.dataLogDir, config.dataDir);
140             JvmPauseMonitor jvmPauseMonitor = null;
141             if (config.jvmPauseMonitorToRun) {
142                 jvmPauseMonitor = new JvmPauseMonitor(config);
143             }
144             final ZooKeeperServer zkServer = new ZooKeeperServer(jvmPauseMonitor, txnLog, config.tickTime, config.minSessionTimeout, config.maxSessionTimeout, config.listenBacklog, null, config.initialConfig);
145             txnLog.setServerStats(zkServer.serverStats());
146 
147             // Registers shutdown handler which will be used to know the
148             // server error or shutdown state changes.
149             final CountDownLatch shutdownLatch = new CountDownLatch(1);
150             zkServer.registerServerShutdownHandler(new ZooKeeperServerShutdownHandler(shutdownLatch));
151 
152             // Start Admin server
153             adminServer = AdminServerFactory.createAdminServer();
154             adminServer.setZooKeeperServer(zkServer);
155             adminServer.start();
156 
157             boolean needStartZKServer = true;
158             if (config.getClientPortAddress() != null) {
159                 cnxnFactory = ServerCnxnFactory.createFactory();
160                 cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), false);
161                 cnxnFactory.startup(zkServer);
162                 // zkServer has been started. So we don't need to start it again in secureCnxnFactory.
163                 needStartZKServer = false;
164             }
165             if (config.getSecureClientPortAddress() != null) {
166                 secureCnxnFactory = ServerCnxnFactory.createFactory();
167                 secureCnxnFactory.configure(config.getSecureClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), true);
168                 secureCnxnFactory.startup(zkServer, needStartZKServer);
169             }
170 
171             containerManager = new ContainerManager(
172                 zkServer.getZKDatabase(),
173                 zkServer.firstProcessor,
174                 Integer.getInteger("znode.container.checkIntervalMs", (int) TimeUnit.MINUTES.toMillis(1)),
175                 Integer.getInteger("znode.container.maxPerMinute", 10000),
176                 Long.getLong("znode.container.maxNeverUsedIntervalMs", 0)
177             );
178             containerManager.start();
179             ZKAuditProvider.addZKStartStopAuditLog();
180 
181             // Watch status of ZooKeeper server. It will do a graceful shutdown
182             // if the server is not running or hits an internal error.
183             shutdownLatch.await();
184 
185             shutdown();
186 
187             if (cnxnFactory != null) {
188                 cnxnFactory.join();
189             }
190             if (secureCnxnFactory != null) {
191                 secureCnxnFactory.join();
192             }
193             if (zkServer.canShutdown()) {
194                 zkServer.shutdown(true);
195             }
196         } catch (InterruptedException e) {
197             // warn, but generally this is ok
198             LOG.warn("Server interrupted", e);
199         } finally {
200             if (txnLog != null) {
201                 txnLog.close();
202             }
203             if (metricsProvider != null) {
204                 try {
205                     metricsProvider.stop();
206                 } catch (Throwable error) {
207                     LOG.warn("Error while stopping metrics", error);
208                 }
209             }
210         }
211     }
212 
213     /**
214      * Shutdown the serving instance
215      */
shutdown()216     protected void shutdown() {
217         if (containerManager != null) {
218             containerManager.stop();
219         }
220         if (cnxnFactory != null) {
221             cnxnFactory.shutdown();
222         }
223         if (secureCnxnFactory != null) {
224             secureCnxnFactory.shutdown();
225         }
226         try {
227             if (adminServer != null) {
228                 adminServer.shutdown();
229             }
230         } catch (AdminServerException e) {
231             LOG.warn("Problem stopping AdminServer", e);
232         }
233     }
234 
235     // VisibleForTesting
getCnxnFactory()236     ServerCnxnFactory getCnxnFactory() {
237         return cnxnFactory;
238     }
239 
240     // VisibleForTesting
getSecureCnxnFactory()241     ServerCnxnFactory getSecureCnxnFactory() {
242         return secureCnxnFactory;
243     }
244 
245     /**
246      * Shutdowns properly the service, this method is not a public API.
247      */
close()248     public void close() {
249         ServerCnxnFactory primaryCnxnFactory = this.cnxnFactory;
250         if (primaryCnxnFactory == null) {
251             // in case of pure TLS we can hook into secureCnxnFactory
252             primaryCnxnFactory = secureCnxnFactory;
253         }
254         if (primaryCnxnFactory == null || primaryCnxnFactory.getZooKeeperServer() == null) {
255             return;
256         }
257         ZooKeeperServerShutdownHandler zkShutdownHandler = primaryCnxnFactory.getZooKeeperServer().getZkShutdownHandler();
258         zkShutdownHandler.handle(ZooKeeperServer.State.SHUTDOWN);
259         try {
260             // ServerCnxnFactory will call the shutdown
261             primaryCnxnFactory.join();
262         } catch (InterruptedException ex) {
263             Thread.currentThread().interrupt();
264         }
265     }
266 
267 }
268