001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: DiscoveryServiceImpl.java 9838 2006-11-14 15:35:24Z danesa $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.discovery;
025:
026: import java.util.ArrayList;
027:
028: import javax.management.JMException;
029: import javax.management.MBeanServer;
030: import javax.management.remote.JMXServiceURL;
031: import javax.naming.Context;
032: import javax.naming.NamingException;
033:
034: import org.objectweb.jonas.common.Log;
035: import org.objectweb.jonas.jmx.JmxService;
036: import org.objectweb.jonas.jmx.JonasObjectName;
037: import org.objectweb.jonas.service.AbsServiceImpl;
038: import org.objectweb.jonas.service.ServiceException;
039: import org.objectweb.jonas.service.ServiceManager;
040:
041: import org.objectweb.util.monolog.api.BasicLevel;
042: import org.objectweb.util.monolog.api.Logger;
043:
044: /**
045: *
046: * @author Adriana Danes
047: *
048: * The discovery service creates and starts at least a DiscoveryManager which is a MBean
049: * that multicasts <code>discovery information</code> to all the servers who joined the
050: * <code>discovery multicast group</code>.
051: * <p>
052: * The discovery service may also create a Enroller and a DiscoveryClient, and in this case
053: * the current server becomes a <code>discovery server</code>.
054: * <p>
055: * <code>Discovery information</code> contains information allowing to remotely manage a server in the group.
056: * <p>
057: * <code>Discovery multicast group</code> is a group of servers which can be managed remotely by a discovery server.
058: * <p>
059: * <code>Discovery server</code> is the server in the group who detains the discovery information concerning all the
060: * servers in the group.
061: */
062: public class DiscoveryServiceImpl extends AbsServiceImpl implements
063: DiscoveryService, DiscoveryServiceImplMBean {
064:
065: /**
066: * The default port where responses to discovery events should be sent.
067: */
068: private static final String DISCOVERY_SOURCE_PORT_DEFAULT = "9888";
069: /**
070: * The default port where responses to greeting messages should be sent.
071: */
072: private static final String DISCOVERY_GREETING_PORT_DEFAULT = "9899";
073: /**
074: * The default timeout duration that server should wait to receive
075: * responses to it's greeting message before continuing.
076: */
077: private static final String DISCOVERY_GREETING_TIMEOUT_DEFAULT = "1000";
078: /**
079: * String form of the IP Address where multicast messages are sent.
080: */
081: private String listeningIp = null;
082: /**
083: * The port to listen to for multicast messages.
084: */
085: private int listeningPort;
086: /**
087: * The port to listen to for responses to greeting message.
088: */
089: private int greetingListeningPort;
090: /**
091: * The time period in miliseconds to listen for greeting responses.
092: */
093: private int greetingAckTimeOut;
094: /**
095: * The port where discovery events are sent.
096: */
097: private int sourcePort;
098:
099: /**
100: * TimeToLive value
101: */
102: private static final int DISCOVERY_TTL_DEFAULT = 1;
103: private int ttl = DISCOVERY_TTL_DEFAULT;
104:
105: /**
106: * set to true if enroller and discoveryClient started
107: */
108: private boolean isDiscoveryMaster = false;
109: /**
110: * Reference to a MBean server.
111: */
112: private MBeanServer mbeanServer = null;
113:
114: /**
115: * jmx service reference
116: */
117: private JmxService jmxService = null;
118: /**
119: * Logger for this service.
120: */
121: private static Logger logger = null;
122: /**
123: * Reference to the Enroller MBean instance
124: * The Enrooler can be used by the J2EEDomain MBean to sent a discovery notification to the JMX service
125: * in order to add a server to the domain.
126: */
127: private Enroller enroller = null;
128:
129: /**
130: * @return the multicast group IP address used by the discovery service
131: */
132: public String getMulticastAddress() {
133: return listeningIp;
134: }
135:
136: /**
137: * @return the multicast group port number used by the discovery service
138: */
139: public String getMulticastPort() {
140: return String.valueOf(listeningPort);
141: }
142:
143: /**
144: * @return true if the current server is a discovery server
145: */
146: public Boolean getIsDiscoveryMaster() {
147: return new Boolean(isDiscoveryMaster);
148: }
149:
150: /**
151: * Management operation allowing to make the current server become a
152: * master if its not already.
153: * @throws JMException a JMX exception occured when trying to make current server a discovery master
154: */
155: public void startDiscoveryMaster() throws JMException {
156: if (!isDiscoveryMaster) {
157: // TODO This code will not work (no DomainMonitor)
158: createEnroller();
159: createDiscClient();
160: isDiscoveryMaster = true;
161: }
162: }
163:
164: /**
165: * Initialize the discovery service
166: * @param ctx context containing initialization parameters
167: * @exception discovery service could not be correctly initialized
168: */
169: protected void doInit(Context ctx) throws ServiceException {
170: //Init the logger
171: logger = Log.getLogger(Log.JONAS_DISCOVERY_PREFIX);
172:
173: // Get service configuration
174: try {
175: listeningIp = (String) ctx
176: .lookup("jonas.service.discovery.multicast.address");
177: String sListeningPort = (String) ctx
178: .lookup("jonas.service.discovery.multicast.port");
179: listeningPort = (Integer.valueOf(sListeningPort))
180: .intValue();
181: } catch (NamingException ne) {
182: String err = "Cannot read initializations arguments in service context";
183: logger.log(BasicLevel.ERROR, err);
184: throw new ServiceException(err, ne);
185: }
186:
187: // Get the ttl value
188: try {
189: String sttl = (String) ctx
190: .lookup("jonas.service.discovery.ttl");
191: if (sttl != null) {
192: ttl = (Integer.valueOf(sttl)).intValue();
193: logger.log(BasicLevel.DEBUG, "discovery TTL set to "
194: + ttl);
195: }
196: } catch (NamingException ne) {
197: // keep the default value for ttl
198: }
199:
200: // Get info allowing to see if this is a discovery master
201: try {
202: String sMaster = (String) ctx
203: .lookup("jonas.service.discovery.master");
204: if (sMaster != null && sMaster.equals("true")) {
205: isDiscoveryMaster = true;
206: }
207: } catch (NamingException ne) {
208: // isDiscoveryMaster rests false
209: }
210:
211: // Temporary strings to store string forms of ports and timeout.
212: String sSourcePort = null;
213: String greetListeningPort = null;
214: String greetAckTimeOut = null;
215:
216: // Check if the source port number has been defined else use default
217: try {
218: sSourcePort = (String) ctx
219: .lookup("jonas.service.discovery.source.port");
220: } catch (NamingException ne) {
221: sSourcePort = DISCOVERY_SOURCE_PORT_DEFAULT;
222: }
223: sourcePort = (Integer.valueOf(sSourcePort)).intValue();
224:
225: // Check if the greeting port has been defined else use default
226: try {
227: greetListeningPort = (String) ctx
228: .lookup("jonas.service.discovery.greeting.port");
229: } catch (NamingException e1) {
230: greetListeningPort = DISCOVERY_GREETING_PORT_DEFAULT;
231: }
232: greetingListeningPort = (Integer.valueOf(greetListeningPort))
233: .intValue();
234:
235: // Check if the greeting timeout has been defined else use default
236: try {
237: greetAckTimeOut = (String) ctx
238: .lookup("jonas.service.discovery.greeting.timeout");
239: } catch (NamingException e1) {
240: greetAckTimeOut = DISCOVERY_GREETING_TIMEOUT_DEFAULT;
241: }
242: greetingAckTimeOut = (Integer.valueOf(greetAckTimeOut))
243: .intValue();
244:
245: // Get JOnAS references allowing to start execution
246: ServiceManager sm = null;
247: try {
248: sm = ServiceManager.getInstance();
249: } catch (Exception e) {
250: String err = "Cannot get ServiceManager instance";
251: logger.log(BasicLevel.ERROR, err);
252: throw new ServiceException(err, e);
253: }
254: jmxService = ((JmxService) sm.getJmxService());
255: try {
256: mbeanServer = jmxService.getJmxServer();
257: } catch (ServiceException e) {
258: // the JMX service may not be started
259: mbeanServer = null;
260: String err = "Cannot get MBeanServer reference";
261: logger.log(BasicLevel.ERROR, err);
262: throw new ServiceException(err, e);
263: }
264: }
265:
266: /**
267: * Start the discovery service
268: *
269: * @throws ServiceException
270: * An error occured when starting the service
271: */
272: protected void doStart() throws ServiceException {
273: // Create discovery manager
274: DiscoveryManager dm = new DiscoveryManager(this
275: .getJonasServerName(), listeningPort, listeningIp,
276: greetingListeningPort, greetingAckTimeOut);
277: dm.setDomainName(getDomainName());
278: dm.setJonasName(jmxService.getJonasServerName());
279: dm.setTimeToLive(ttl);
280: JMXServiceURL[] connectorServerURLs = jmxService
281: .getConnectorServerURLs();
282: ArrayList urlsList = new ArrayList();
283: for (int i = 0; i < connectorServerURLs.length; i++) {
284: // The connectorServerURLs may contain null
285: // if the list of protocols in Carol contain
286: // other protocols than the standard ones (JRMP, JEREMIE, IIOP, CMI)
287: if (connectorServerURLs[i] != null) {
288: urlsList.add(connectorServerURLs[i].toString());
289: }
290: }
291: String[] urls = new String[urlsList.size()];
292: for (int i = 0; i < urls.length; i++) {
293: urls[i] = (String) urlsList.get(i);
294: }
295: dm.setUrls(urls);
296: try {
297: // Register DiscoveryManager MBean
298: if (mbeanServer != null) {
299: mbeanServer.registerMBean(dm, JonasObjectName
300: .discoveryManager());
301: }
302: } catch (JMException e) {
303: throw new ServiceException(
304: "Problem when starting the Discovery Service: ", e);
305: }
306: // Start discovery manager
307: try {
308: dm.start();
309: } catch (DuplicateServerNameException e) {
310: // Server with the same name already exists in domain.
311: logger
312: .log(
313: BasicLevel.ERROR,
314: "Discovery manager failed to start due to a pre-existing"
315: + " server in the domain with the same name.",
316: e);
317: try {
318: // Discovery manager failed to start so remove the mbean.
319: mbeanServer.unregisterMBean(JonasObjectName
320: .discoveryManager());
321: } catch (JMException e1) {
322: logger.log(BasicLevel.ERROR,
323: "Problem trying to unregister "
324: + "DiscoveryManager: ", e);
325: }
326: // Discovery service had a problem.
327: throw new ServiceException(
328: "Problem when starting the Discovery Service:", e);
329: }
330:
331: if (isDiscoveryMaster) {
332: // Create enroller
333: try {
334: createEnroller();
335: } catch (JMException e) {
336: throw new ServiceException(
337: "Problem when starting the Discovery Service: ",
338: e);
339: }
340:
341: // Create the discovery client
342: try {
343: createDiscClient();
344: } catch (JMException e) {
345: throw new ServiceException(
346: "Problem when starting the Discovery Service: ",
347: e);
348: }
349: }
350:
351: // Create and register the service MBean
352: try {
353: if (mbeanServer != null) {
354: mbeanServer.registerMBean(this , JonasObjectName
355: .discoveryService());
356: }
357: } catch (JMException e) {
358: throw new ServiceException(
359: "Problem when starting the Discovery Service: ", e);
360: }
361: }
362:
363: /**
364: * Create the Enroller MBean
365: * @throws JMException
366: */
367: private void createEnroller() throws JMException {
368: enroller = new Enroller(listeningPort, listeningIp);
369: enroller.setTimeToLive(ttl);
370: if (mbeanServer != null) {
371: mbeanServer.registerMBean(enroller, JonasObjectName
372: .discoveryEnroller());
373: }
374: }
375:
376: /**
377: * Create the DiscoveryClient MBean
378: * @throws JMException
379: */
380: private void createDiscClient() throws JMException {
381: DiscoveryClient dc = new DiscoveryClient(listeningPort,
382: listeningIp, sourcePort);
383: dc.setTimeToLive(ttl);
384: // Register MBean
385: if (mbeanServer != null) {
386: mbeanServer.registerMBean(dc, JonasObjectName
387: .discoveryClient());
388: }
389: }
390:
391: protected void doStop() throws ServiceException {
392: // TO DO
393: // ....
394: // Unregister discovery MBeans, cluster MBeans etc ..
395:
396: // Unegister the service MBean
397: try {
398: if (mbeanServer != null) {
399: mbeanServer.unregisterMBean(JonasObjectName
400: .discoveryService());
401: }
402: } catch (JMException e) {
403: throw new ServiceException(
404: "Problem when stoping the Discovery Service: ", e);
405: }
406: }
407:
408: /**
409: * @return Returns the enroller.
410: */
411: public Enroller getEnroller() {
412: return enroller;
413: }
414:
415: /**
416: * Create a 'fake' DiscEvent object containing info to establish a JMX connection with a new server
417: * in the domain
418: * @param serverName the OBJECT_NAME of the server MBean corresponding to the server to connect
419: * @param domainName the JOnAS server's domain name
420: * @param connectorURLs the urls of the connector server of the server to connect
421: * @param state the state of the server (RUNNING in case the DiscEvent is used to add a server; could be STOPPING
422: * if the DiscEvent is used to remove a server)
423: * @return a new DiscEvent object
424: */
425: public DiscEvent getDiscEvent(String serverName, String domainName,
426: String[] connectorURLs, String state) {
427: String sourceAddress = null;
428: int sourcePort = 0;
429: String serverId = null;
430: DiscEvent fakeMessage = new DiscEvent(sourceAddress,
431: sourcePort, serverName, domainName, serverId,
432: connectorURLs);
433: fakeMessage.setState(state);
434: return fakeMessage;
435: }
436:
437: /**
438: * @return the discovery protocol version number
439: */
440: public String getDiscoveryProtocolVersion() {
441: return DiscMessage.DISCOVERY_PROTOCOL_VERSION;
442: }
443:
444: public String getDiscoveryTtl() {
445: // TODO Auto-generated method stub
446: return (new Integer(ttl)).toString();
447: }
448: }
|