/* * Copyright 2012 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.netflix.exhibitor.core; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.netflix.exhibitor.core.activity.ActivityLog; import com.netflix.exhibitor.core.activity.ActivityQueue; import com.netflix.exhibitor.core.activity.QueueGroups; import com.netflix.exhibitor.core.activity.RepeatingActivity; import com.netflix.exhibitor.core.activity.RepeatingActivityImpl; import com.netflix.exhibitor.core.automanage.AutomaticInstanceManagement; import com.netflix.exhibitor.core.automanage.RemoteInstanceRequestClient; import com.netflix.exhibitor.core.automanage.RemoteInstanceRequestClientImpl; import com.netflix.exhibitor.core.automanage.RemoteInstanceRequestHttpsClientImpl; import com.netflix.exhibitor.core.backup.BackupManager; import com.netflix.exhibitor.core.backup.BackupProvider; import com.netflix.exhibitor.core.config.ConfigListener; import com.netflix.exhibitor.core.config.ConfigManager; import com.netflix.exhibitor.core.config.ConfigProvider; import com.netflix.exhibitor.core.config.IntConfigs; import com.netflix.exhibitor.core.config.JQueryStyle; import com.netflix.exhibitor.core.controlpanel.ControlPanelValues; import com.netflix.exhibitor.core.controlpanel.FileBasedPreferences; import com.netflix.exhibitor.core.index.IndexCache; import com.netflix.exhibitor.core.processes.ProcessMonitor; import com.netflix.exhibitor.core.processes.ProcessOperations; import com.netflix.exhibitor.core.processes.StandardProcessOperations; import com.netflix.exhibitor.core.rest.UITab; import com.netflix.exhibitor.core.servo.GetMonitorData; import com.netflix.exhibitor.core.servo.ZookeeperMonitoredData; import com.netflix.exhibitor.core.state.CleanupManager; import com.netflix.exhibitor.core.state.ManifestVersion; import com.netflix.exhibitor.core.state.MonitorRunningInstance; import com.netflix.servo.monitor.CompositeMonitor; import com.netflix.servo.monitor.Monitors; import jsr166y.ForkJoinPool; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.utils.CloseableUtils; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.concurrent.atomic.AtomicReference; import java.util.prefs.Preferences; public class Exhibitor implements Closeable { private final ActivityLog log; private final ActivityQueue activityQueue = new ActivityQueue(); private final MonitorRunningInstance monitorRunningInstance; private final Collection<UITab> additionalUITabs; private final ProcessOperations processOperations; private final CleanupManager cleanupManager; private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT); private final IndexCache indexCache; private final ControlPanelValues controlPanelValues; private final BackupManager backupManager; private final ConfigManager configManager; private final ExhibitorArguments arguments; private final ProcessMonitor processMonitor; private final RepeatingActivity autoInstanceManagement; private final RepeatingActivity servoMonitoring; private final CompositeMonitor<?> servoCompositeMonitor; private final ManifestVersion manifestVersion = new ManifestVersion(); private final ForkJoinPool forkJoinPool = new ForkJoinPool(); private final RemoteInstanceRequestClient remoteInstanceRequestClient; public static final int AUTO_INSTANCE_MANAGEMENT_PERIOD_MS = 60000; private CuratorFramework localConnection; // protected by synchronization private enum State { LATENT, STARTED, STOPPED } /** * Return this VM's hostname if possible * * @return hostname */ public static String getHostname() { String host = "unknown"; try { return InetAddress.getLocalHost().getHostName(); } catch ( UnknownHostException e ) { // ignore } return host; } /** * @param configProvider config source * @param additionalUITabs any additional tabs in the UI (can be null) * @param backupProvider backup provider or null * @param arguments startup arguments * @throws IOException errors */ public Exhibitor(ConfigProvider configProvider, Collection<? extends UITab> additionalUITabs, BackupProvider backupProvider, ExhibitorArguments arguments) throws Exception { System.out.println(getVersion()); this.arguments = arguments; log = new ActivityLog(arguments.logWindowSizeLines); this.configManager = new ConfigManager(this, configProvider, arguments.configCheckMs); this.additionalUITabs = (additionalUITabs != null) ? ImmutableList.copyOf(additionalUITabs) : ImmutableList.<UITab>of(); this.processOperations = new StandardProcessOperations(this); monitorRunningInstance = new MonitorRunningInstance(this); cleanupManager = new CleanupManager(this); indexCache = new IndexCache(log); processMonitor = new ProcessMonitor(this); autoInstanceManagement = new RepeatingActivityImpl(log, activityQueue, QueueGroups.MAIN, new AutomaticInstanceManagement(this), getAutoInstanceManagementPeriod()); if(arguments.httpsConfiguration.getServerKeystorePath() != null) { remoteInstanceRequestClient = new RemoteInstanceRequestHttpsClientImpl(arguments.remoteConnectionConfiguration, arguments.httpsConfiguration); } else { remoteInstanceRequestClient = new RemoteInstanceRequestClientImpl(arguments.remoteConnectionConfiguration); } AtomicReference<CompositeMonitor<?>> theMonitor = new AtomicReference<CompositeMonitor<?>>(); servoMonitoring = initServo(this, log, activityQueue, arguments, theMonitor); servoCompositeMonitor = theMonitor.get(); controlPanelValues = new ControlPanelValues(getPreferences()); this.backupManager = new BackupManager(this, backupProvider); } public String getVersion() { return manifestVersion.getVersion(); } /** * @return logging manager */ public ActivityLog getLog() { return log; } /** * @return cache of indexed log files */ public IndexCache getIndexCache() { return indexCache; } /** * Start the app * * @throws Exception errors */ public void start() throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED)); activityQueue.start(); configManager.start(); monitorRunningInstance.start(); cleanupManager.start(); backupManager.start(); autoInstanceManagement.start(); if ( servoMonitoring != null ) { servoMonitoring.start(); } configManager.addConfigListener ( new ConfigListener() { @Override public void configUpdated() { try { resetLocalConnection(); } catch ( IOException e ) { log.add(ActivityLog.Type.ERROR, "Resetting connection", e); } } } ); } public String getExtraHeadingText() { return arguments.extraHeadingText; } @Override public void close() throws IOException { Preconditions.checkState(state.compareAndSet(State.STARTED, State.STOPPED)); if ( (arguments.servoRegistration != null) && (servoCompositeMonitor != null) ) { arguments.servoRegistration.getMonitorRegistry().unregister(servoCompositeMonitor); } CloseableUtils.closeQuietly(servoMonitoring); CloseableUtils.closeQuietly(autoInstanceManagement); CloseableUtils.closeQuietly(processMonitor); CloseableUtils.closeQuietly(indexCache); CloseableUtils.closeQuietly(backupManager); CloseableUtils.closeQuietly(cleanupManager); CloseableUtils.closeQuietly(monitorRunningInstance); CloseableUtils.closeQuietly(configManager); CloseableUtils.closeQuietly(activityQueue); CloseableUtils.closeQuietly(remoteInstanceRequestClient); closeLocalConnection(); } /** * @return any additionally configured tabs */ public Collection<UITab> getAdditionalUITabs() { return additionalUITabs; } public JQueryStyle getJQueryStyle() { return arguments.jQueryStyle; } public ConfigManager getConfigManager() { return configManager; } public ActivityQueue getActivityQueue() { return activityQueue; } public ProcessOperations getProcessOperations() { return processOperations; } /** * Return the configured ZK connection timeout in ms * * @return timeout */ public int getConnectionTimeOutMs() { return arguments.connectionTimeOutMs; } public String getThisJVMHostname() { return arguments.thisJVMHostname; } public boolean nodeMutationsAllowed() { return arguments.allowNodeMutations; } /** * Closes/resets the ZK connection or does nothing if it hasn't been opened yet * * @throws IOException errors */ public synchronized void resetLocalConnection() throws IOException { closeLocalConnection(); } /** * Return a connection ot the ZK instance (creating it if needed) * * @return connection * @throws IOException errors */ public synchronized CuratorFramework getLocalConnection() throws IOException { if ( localConnection == null ) { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString("localhost:" + configManager.getConfig().getInt(IntConfigs.CLIENT_PORT)) .sessionTimeoutMs(arguments.connectionTimeOutMs * 10) .connectionTimeoutMs(arguments.connectionTimeOutMs) .retryPolicy(new ExponentialBackoffRetry(1000, 3)); if ( arguments.aclProvider != null ) { builder = builder.aclProvider(arguments.aclProvider); } localConnection = builder.build(); localConnection.start(); } return localConnection; } public ControlPanelValues getControlPanelValues() { return controlPanelValues; } public BackupManager getBackupManager() { return backupManager; } public ProcessMonitor getProcessMonitor() { return processMonitor; } public MonitorRunningInstance getMonitorRunningInstance() { return monitorRunningInstance; } public int getRestPort() { return arguments.restPort; } public String getRestPath() { return arguments.restPath; } public String getRestScheme() { return arguments.restScheme; } public Runnable getShutdownProc() { return arguments.shutdownProc; } public RemoteInstanceRequestClient getRemoteInstanceRequestClient() { return remoteInstanceRequestClient; } public HttpsConfiguration getHttpsConfiguration() { return arguments.httpsConfiguration; } public ExhibitorArguments.LogDirection getLogDirection() { return arguments.logDirection; } public ForkJoinPool getForkJoinPool() { return forkJoinPool; } private Preferences getPreferences() throws IOException { if ( arguments.preferencesPath != null ) { return new FileBasedPreferences(new File(arguments.preferencesPath)); } return Preferences.userRoot(); } private synchronized void closeLocalConnection() { CloseableUtils.closeQuietly(localConnection); localConnection = null; } private static int getAutoInstanceManagementPeriod() { return AUTO_INSTANCE_MANAGEMENT_PERIOD_MS + (int)(AUTO_INSTANCE_MANAGEMENT_PERIOD_MS * Math.random()); // add some randomness to avoid overlap with other Exhibitors } private static RepeatingActivity initServo(Exhibitor exhibitor, ActivityLog log, ActivityQueue activityQueue, ExhibitorArguments arguments, AtomicReference<CompositeMonitor<?>> theMonitor) { theMonitor.set(null); RepeatingActivity localServoMonitoring = null; if ( arguments.servoRegistration != null ) { ZookeeperMonitoredData zookeeperMonitoredData = new ZookeeperMonitoredData(); CompositeMonitor<?> compositeMonitor = Monitors.newObjectMonitor(zookeeperMonitoredData); GetMonitorData getMonitorData = new GetMonitorData(exhibitor, zookeeperMonitoredData); localServoMonitoring = new RepeatingActivityImpl(log, activityQueue, QueueGroups.IO, getMonitorData, arguments.servoRegistration.getZookeeperPollMs()); arguments.servoRegistration.getMonitorRegistry().register(compositeMonitor); theMonitor.set(compositeMonitor); } return localServoMonitoring; } }