001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.server;
006:
007: import org.apache.commons.lang.StringUtils;
008: import org.mortbay.jetty.Server;
009: import org.mortbay.jetty.servlet.ServletHandler;
010: import org.mortbay.jetty.servlet.ServletHolder;
011: import org.mortbay.jetty.webapp.WebAppContext;
012:
013: import com.tc.async.api.ConfigurationContext;
014: import com.tc.async.api.SEDA;
015: import com.tc.async.api.Sink;
016: import com.tc.async.api.Stage;
017: import com.tc.async.api.StageManager;
018: import com.tc.capabilities.AbstractCapabilitiesFactory;
019: import com.tc.config.schema.L2Info;
020: import com.tc.config.schema.NewCommonL2Config;
021: import com.tc.config.schema.NewSystemConfig;
022: import com.tc.config.schema.messaging.http.ConfigServlet;
023: import com.tc.config.schema.setup.ConfigurationSetupException;
024: import com.tc.config.schema.setup.L2TVSConfigurationSetupManager;
025: import com.tc.l2.state.StateManager;
026: import com.tc.lang.StartupHelper;
027: import com.tc.lang.TCThreadGroup;
028: import com.tc.lang.ThrowableHandler;
029: import com.tc.lang.StartupHelper.StartupAction;
030: import com.tc.logging.CustomerLogging;
031: import com.tc.logging.TCLogger;
032: import com.tc.logging.TCLogging;
033: import com.tc.management.beans.L2MBeanNames;
034: import com.tc.management.beans.L2State;
035: import com.tc.management.beans.TCServerInfo;
036: import com.tc.net.protocol.transport.ConnectionPolicy;
037: import com.tc.net.protocol.transport.ConnectionPolicyImpl;
038: import com.tc.objectserver.core.impl.ServerManagementContext;
039: import com.tc.objectserver.impl.DistributedObjectServer;
040: import com.tc.stats.DSO;
041: import com.tc.stats.DSOMBean;
042: import com.tc.util.Assert;
043:
044: import java.util.Date;
045:
046: import javax.management.InstanceAlreadyExistsException;
047: import javax.management.MBeanRegistrationException;
048: import javax.management.MBeanServer;
049: import javax.management.NotCompliantMBeanException;
050:
051: public class TCServerImpl extends SEDA implements TCServer {
052:
053: private static final TCLogger logger = TCLogging
054: .getLogger(TCServer.class);
055: private static final TCLogger consoleLogger = CustomerLogging
056: .getConsoleLogger();
057:
058: private volatile long startTime = -1;
059: private volatile long activateTime = -1;
060:
061: private DistributedObjectServer dsoServer;
062: private Server httpServer;
063: private TerracottaConnector terracottaConnector;
064:
065: private final Object stateLock = new Object();
066: private final L2State state = new L2State();
067:
068: private final L2TVSConfigurationSetupManager configurationSetupManager;
069: private final ConnectionPolicy connectionPolicy;
070:
071: /**
072: * This should only be used for tests.
073: */
074: public TCServerImpl(
075: L2TVSConfigurationSetupManager configurationSetupManager) {
076: this (configurationSetupManager, new TCThreadGroup(
077: new ThrowableHandler(TCLogging
078: .getLogger(TCServer.class))));
079: }
080:
081: public TCServerImpl(
082: L2TVSConfigurationSetupManager configurationSetupManager,
083: TCThreadGroup threadGroup) {
084: this (configurationSetupManager, threadGroup,
085: new ConnectionPolicyImpl(Integer.MAX_VALUE));
086: }
087:
088: public TCServerImpl(L2TVSConfigurationSetupManager manager,
089: TCThreadGroup group, ConnectionPolicy connectionPolicy) {
090: super (group);
091: this .connectionPolicy = connectionPolicy;
092: Assert.assertNotNull(manager);
093: this .configurationSetupManager = manager;
094: }
095:
096: public L2Info[] infoForAllL2s() {
097: String[] allKnownL2s = this .configurationSetupManager
098: .allCurrentlyKnownServers();
099: L2Info[] out = new L2Info[allKnownL2s.length];
100:
101: for (int i = 0; i < out.length; ++i) {
102: try {
103: NewCommonL2Config config = this .configurationSetupManager
104: .commonL2ConfigFor(allKnownL2s[i]);
105:
106: String name = allKnownL2s[i];
107: if (name == null)
108: name = L2Info.IMPLICIT_L2_NAME;
109:
110: String host = config.host().getString();
111: if (StringUtils.isBlank(host))
112: host = name;
113:
114: out[i] = new L2Info(name, host, config.jmxPort()
115: .getInt());
116: } catch (ConfigurationSetupException cse) {
117: throw Assert.failure("This should be impossible here",
118: cse);
119: }
120: }
121:
122: return out;
123: }
124:
125: public String getDescriptionOfCapabilities() {
126: return AbstractCapabilitiesFactory.getCapabilitiesManager()
127: .describe();
128: }
129:
130: /**
131: * I realize this is wrong, since the server can still be starting but we'll have to deal with the whole stopping
132: * issue later, and there's the TCStop feature which should be removed.
133: */
134: public void stop() {
135: synchronized (stateLock) {
136: if (!state.isStartState()) {
137: stopServer();
138: logger.info("Server stopped.");
139: } else {
140: logger.warn("Server in incorrect state ("
141: + state.getState() + ") to be stopped.");
142: }
143: }
144:
145: }
146:
147: public void start() {
148: synchronized (stateLock) {
149: if (state.isStartState()) {
150: try {
151: startServer();
152: } catch (Throwable t) {
153: if (t instanceof RuntimeException) {
154: throw (RuntimeException) t;
155: }
156: throw new RuntimeException(t);
157: }
158: } else {
159: logger.warn("Server in incorrect state ("
160: + state.getState() + ") to be started.");
161: }
162: }
163: }
164:
165: public boolean canShutdown() {
166: return (!state.isStartState() || (dsoServer != null && dsoServer
167: .isBlocking()))
168: && !state.isStopState();
169: }
170:
171: public synchronized void shutdown() {
172: if (canShutdown()) {
173: state.setState(StateManager.STOP_STATE);
174: stopServer();
175: consoleLogger.info("Server exiting...");
176: System.exit(0);
177: } else {
178: logger.warn("Server in incorrect state ("
179: + state.getState() + ") to be shutdown.");
180: }
181: }
182:
183: public long getStartTime() {
184: return startTime;
185: }
186:
187: public void updateActivateTime() {
188: if (activateTime == -1) {
189: activateTime = System.currentTimeMillis();
190: }
191: }
192:
193: public long getActivateTime() {
194: return activateTime;
195: }
196:
197: public int getDSOListenPort() {
198: if (dsoServer != null) {
199: return dsoServer.getListenPort();
200: }
201: throw new IllegalStateException("DSO Server not running");
202: }
203:
204: public DistributedObjectServer getDSOServer() {
205: return dsoServer;
206: }
207:
208: public boolean isStarted() {
209: return !state.isStartState();
210: }
211:
212: public boolean isActive() {
213: return state.isActiveCoordinator();
214: }
215:
216: public boolean isStopped() {
217: // XXX:: introduce a new state when stop is officially supported.
218: return state.isStartState();
219: }
220:
221: public String toString() {
222: StringBuffer buf = new StringBuffer();
223: buf.append("Server: ").append(super .toString()).append("\n");
224: if (isActive()) {
225: buf.append("Active since ")
226: .append(new Date(getStartTime())).append("\n");
227: } else if (isStarted()) {
228: buf.append("Started at ").append(new Date(getStartTime()))
229: .append("\n");
230: } else {
231: buf.append("Server is stopped").append("\n");
232: }
233:
234: return buf.toString();
235: }
236:
237: private void stopServer() {
238: // XXX: I have no idea if order of operations is correct here?
239:
240: if (logger.isDebugEnabled()) {
241: consoleLogger.debug("Stopping TC server...");
242: }
243:
244: if (terracottaConnector != null) {
245: try {
246: terracottaConnector.shutdown();
247: } catch (Exception e) {
248: logger.error(
249: "Error shutting down terracotta connector", e);
250: } finally {
251: terracottaConnector = null;
252: }
253: }
254:
255: try {
256: getStageManager().stopAll();
257: } catch (Exception e) {
258: logger.error("Error shutting down stage manager", e);
259: }
260:
261: if (httpServer != null) {
262: if (logger.isDebugEnabled()) {
263: logger.debug("Shutting down HTTP server...");
264: }
265:
266: try {
267: httpServer.stop();
268: } catch (Exception e) {
269: logger.error("Error shutting down HTTP server", e);
270: } finally {
271: httpServer = null;
272: }
273: }
274:
275: // this stops the jmx server then dso server
276: if (dsoServer != null) {
277: try {
278: dsoServer.quickStop();
279: } catch (Exception e) {
280: logger.error("Error shutting down DSO server", e);
281: } finally {
282: dsoServer = null;
283: }
284: }
285:
286: }
287:
288: private class StartAction implements StartupAction {
289: public void execute() throws Throwable {
290: if (logger.isDebugEnabled()) {
291: logger.debug("Starting Terracotta server...");
292: }
293:
294: startTime = System.currentTimeMillis();
295:
296: NewSystemConfig systemConfig = TCServerImpl.this .configurationSetupManager
297: .systemConfig();
298: terracottaConnector = new TerracottaConnector();
299: startHTTPServer(systemConfig, terracottaConnector);
300:
301: Stage stage = getStageManager().createStage(
302: "dso-http-bridge",
303: new HttpConnectionHandler(terracottaConnector), 1,
304: 100);
305: getStageManager().startAll(
306: new NullContext(getStageManager()));
307:
308: // the following code starts the jmx server as well
309: startDSOServer(stage.getSink());
310:
311: updateActivateTime();
312:
313: if (activationListener != null) {
314: activationListener.serverActivated();
315: }
316:
317: if (updateCheckEnabled()) {
318: UpdateCheckAction.start(TCServerImpl.this ,
319: updateCheckPeriodDays());
320: }
321: }
322: }
323:
324: private boolean updateCheckEnabled() {
325: String s = System.getenv("TC_UPDATE_CHECK_ENABLED");
326: boolean checkEnabled = (s == null) || Boolean.parseBoolean(s);
327: return checkEnabled
328: && configurationSetupManager.updateCheckConfig()
329: .isEnabled().getBoolean();
330: }
331:
332: private int updateCheckPeriodDays() {
333: return configurationSetupManager.updateCheckConfig()
334: .periodDays().getInt();
335: }
336:
337: protected void startServer() throws Exception {
338: new StartupHelper(getThreadGroup(), new StartAction())
339: .startUp();
340: }
341:
342: private void startDSOServer(Sink httpSink) throws Exception {
343: Assert.assertTrue(state.isStartState());
344: dsoServer = new DistributedObjectServer(
345: configurationSetupManager, getThreadGroup(),
346: connectionPolicy, httpSink, new TCServerInfo(this ,
347: state), state);
348: dsoServer.start();
349: registerDSOServer();
350: }
351:
352: private void startHTTPServer(NewSystemConfig systemConfig,
353: TerracottaConnector tcConnector) throws Exception {
354: httpServer = new Server();
355: httpServer.addConnector(tcConnector);
356:
357: WebAppContext context = new WebAppContext("", "/");
358: ServletHandler servletHandler = new ServletHandler();
359:
360: /**
361: * We don't serve up any files, just hook in a few servlets. It's required the ResourceBase be non-null.
362: */
363: context.setResourceBase(System.getProperty("user.dir"));
364:
365: ServletHolder holder;
366: holder = servletHandler.addServletWithMapping(
367: VersionServlet.class.getName(), "/version");
368: servletHandler.addServlet(holder);
369:
370: context.setAttribute(ConfigServlet.CONFIG_ATTRIBUTE,
371: this .configurationSetupManager);
372: holder = servletHandler.addServletWithMapping(
373: ConfigServlet.class.getName(), "/config");
374: servletHandler.addServlet(holder);
375:
376: context.setServletHandler(servletHandler);
377: httpServer.addHandler(context);
378:
379: try {
380: httpServer.start();
381: } catch (Exception e) {
382: consoleLogger.warn("Couldn't start HTTP server", e);
383: throw e;
384: }
385: }
386:
387: public void dump() {
388: if (dsoServer != null) {
389: dsoServer.dump();
390: }
391: }
392:
393: private void registerDSOServer()
394: throws InstanceAlreadyExistsException,
395: MBeanRegistrationException, NotCompliantMBeanException,
396: NullPointerException {
397:
398: ServerManagementContext mgmtContext = dsoServer
399: .getManagementContext();
400: MBeanServer mBeanServer = dsoServer.getMBeanServer();
401: DSOMBean dso = new DSO(mgmtContext, mBeanServer);
402: mBeanServer.registerMBean(dso, L2MBeanNames.DSO);
403: mBeanServer.registerMBean(mgmtContext.getDSOAppEventsMBean(),
404: L2MBeanNames.DSO_APP_EVENTS);
405: }
406:
407: // TODO: check that this is not needed then remove
408: private TCServerActivationListener activationListener;
409:
410: public void setActivationListener(
411: TCServerActivationListener listener) {
412: activationListener = listener;
413: }
414:
415: private static class NullContext implements ConfigurationContext {
416:
417: private final StageManager manager;
418:
419: public NullContext(StageManager manager) {
420: this .manager = manager;
421: }
422:
423: public TCLogger getLogger(Class clazz) {
424: return TCLogging.getLogger(clazz);
425: }
426:
427: public Stage getStage(String name) {
428: return manager.getStage(name);
429: }
430:
431: }
432:
433: public void startBeanShell(int port) {
434: if (dsoServer != null) {
435: dsoServer.startBeanShell(port);
436: }
437: }
438:
439: }
|