001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.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:JoramComponent.java 1022 2006-08-04 11:02:28Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.component.joram;
025:
026: import java.net.ConnectException;
027: import java.util.ArrayList;
028: import java.util.List;
029: import java.util.Properties;
030:
031: import javax.jms.ConnectionFactory;
032: import javax.jms.QueueConnectionFactory;
033: import javax.jms.TopicConnectionFactory;
034: import javax.naming.InitialContext;
035: import javax.naming.NamingException;
036: import javax.resource.ResourceException;
037: import javax.resource.spi.ActivationSpec;
038: import javax.resource.spi.BootstrapContext;
039: import javax.resource.spi.ResourceAdapter;
040: import javax.resource.spi.ResourceAdapterInternalException;
041: import javax.resource.spi.XATerminator;
042: import javax.transaction.TransactionManager;
043: import javax.transaction.xa.XAException;
044:
045: import org.objectweb.joram.client.connector.ActivationSpecImpl;
046: import org.objectweb.joram.client.connector.JoramAdapter;
047: import org.objectweb.joram.client.jms.ConnectionMetaData;
048: import org.objectweb.joram.client.jms.admin.AdminException;
049: import org.objectweb.joram.client.jms.admin.AdminModule;
050: import org.objectweb.joram.client.jms.tcp.QueueTcpConnectionFactory;
051: import org.objectweb.joram.client.jms.tcp.TcpConnectionFactory;
052: import org.objectweb.joram.client.jms.tcp.TopicTcpConnectionFactory;
053: import org.objectweb.joram.mom.proxies.ConnectionManager;
054: import org.objectweb.joram.mom.proxies.tcp.TcpProxyService;
055: import org.objectweb.jotm.Current;
056: import org.objectweb.util.monolog.Monolog;
057: import org.ow2.easybeans.component.api.EZBComponentException;
058: import org.ow2.easybeans.component.itf.JMSComponent;
059: import org.ow2.easybeans.jca.workmanager.ResourceWorkManager;
060: import org.ow2.easybeans.transaction.JTransactionManager;
061: import org.ow2.util.log.Log;
062: import org.ow2.util.log.LogFactory;
063:
064: import fr.dyade.aaa.agent.AgentServer;
065: import fr.dyade.aaa.agent.ServerConfigHelper;
066: import fr.dyade.aaa.util.NullTransaction;
067:
068: /**
069: * Class that start/stop a simple collocated JORAM server.
070: * It also creates some initial Topics/Queues.
071: * @author Florent Benoit
072: */
073: public class JoramComponent implements JMSComponent {
074:
075: /**
076: * Logger.
077: */
078: private static Log logger = LogFactory.getLog(JoramComponent.class);
079:
080: /**
081: * Queue connection factory (for external components).
082: */
083: private static final String QUEUE_CONN_FACT_NAME = "JQCF";
084:
085: /**
086: * Topic connection factory (for external components).
087: */
088: private static final String TOPIC_CONN_FACT_NAME = "JTCF";
089:
090: /**
091: * Connection factory (for external components).
092: */
093: private static final String CONN_FACT_NAME = "JCF";
094:
095: /**
096: * Default port number.
097: */
098: private static final int DEFAULT_PORT_NUMBER = 16010;
099:
100: /**
101: * Port number.
102: */
103: private int port = DEFAULT_PORT_NUMBER;
104:
105: /**
106: * Default hostname.
107: */
108: private static final String DEFAULT_HOST_NAME = "localhost";
109:
110: /**
111: * Host.
112: */
113: private String host = DEFAULT_HOST_NAME;
114:
115: /**
116: * ID of the JORAM server.
117: */
118: private static final short ID = 0;
119:
120: /**
121: * Transaction property (Set to remove persistence settings : transient). It
122: * avoids the creation of a directory.
123: */
124: private static final String TRANSACTION_PROPERTY = "Transaction";
125:
126: /**
127: * Default name of the persistence directory (won't be used).
128: */
129: private static final String DEFAULT_PERSISTENCE_DIRECTORY = "joram-persistence-s"
130: + ID;
131:
132: /**
133: * Server is started ?
134: */
135: private boolean started = false;
136:
137: /**
138: * Minimum threads.
139: */
140: private static final int MIN_THREADS = 1;
141:
142: /**
143: * Maximum threads.
144: */
145: private static final int MAX_THREADS = 2;
146:
147: /**
148: * Thread timeout.
149: */
150: private static final int THREAD_TIMEOUT = 20;
151:
152: /**
153: * Instance of the resource adapter.
154: */
155: private JoramAdapter joramAdapter = null;
156:
157: /**
158: * List of topics to create.
159: */
160: private List<String> topics = null;
161:
162: /**
163: * List of queues to create.
164: */
165: private List<String> queues = null;
166:
167: /**
168: * WorkManager used by this component.
169: */
170: private ResourceWorkManager workManager = null;
171:
172: /**
173: * Initial Context.
174: */
175: private InitialContext ictx = null;
176:
177: /**
178: * Default constructor.
179: */
180: public JoramComponent() {
181: topics = new ArrayList<String>();
182: queues = new ArrayList<String>();
183: }
184:
185: /**
186: * Init method.<br/>
187: * This method is called before the start method.
188: * @throws EZBComponentException if the initialization has failed.
189: */
190: public void init() throws EZBComponentException {
191:
192: }
193:
194: /**
195: * Starts a Joram Server without persistence.
196: * @throws EZBComponentException if start fails
197: */
198: public void start() throws EZBComponentException {
199:
200: // Initialize Monolog
201: Properties properties = new Properties();
202: properties
203: .put(Monolog.MONOLOG_CLASS_NAME,
204: "org.objectweb.util.monolog.wrapper.javaLog.LoggerFactory");
205: properties.put("logger.org.objectweb.joram.level", "ERROR");
206: properties.put("logger.com.scalagent.level", "ERROR");
207: properties.put("logger.fr.dyade.aaa.level", "ERROR");
208: Monolog.init(properties);
209:
210: // Build initial context
211: try {
212: ictx = new InitialContext();
213: } catch (NamingException e) {
214: throw new EZBComponentException(
215: "Cannot create an initial context.", e);
216: }
217:
218: TransactionManager transactionManager = JTransactionManager
219: .getTransactionManager();
220: workManager = new ResourceWorkManager(transactionManager,
221: MIN_THREADS, MAX_THREADS, THREAD_TIMEOUT);
222:
223: // Create BootstrapContext
224: XATerminator xaTerminator = null;
225: try {
226: xaTerminator = ((Current) transactionManager)
227: .getXATerminator();
228: } catch (XAException e) {
229: throw new EZBComponentException(
230: "Cannot get the XA terminator", e);
231: }
232:
233: BootstrapContext bootstrapContext = new JoramBootstrapContext(
234: workManager, xaTerminator);
235:
236: // Create JORAM adapter
237: joramAdapter = new JoramAdapter();
238:
239: // Set host/port
240: joramAdapter.setHostName(host);
241: joramAdapter.setServerPort(Integer.valueOf(port));
242:
243: // configure it (in fact the server will be collocated but started by this service and not by the resource adapter).
244: joramAdapter.setCollocatedServer(Boolean.FALSE);
245:
246: // Set properties (transient)
247: System.setProperty(TRANSACTION_PROPERTY, NullTransaction.class
248: .getName());
249:
250: // Init
251: try {
252: AgentServer.init(ID, DEFAULT_PERSISTENCE_DIRECTORY, null);
253: } catch (Exception e) {
254: throw new EZBComponentException(
255: "Cannot initialize a new collocated Joram server",
256: e);
257: }
258:
259: // start Agent
260: try {
261: AgentServer.start();
262: } catch (Exception e) {
263: throw new EZBComponentException(
264: "Cannot start collocated Joram server", e);
265: }
266:
267: // Add services
268: try {
269: new ServerConfigHelper(false).addService(ID,
270: ConnectionManager.class.getName(), "root root");
271: } catch (Exception e) {
272: throw new EZBComponentException(
273: "Cannot add connection manager service", e);
274: }
275: // To enable TCP listener
276: try {
277: new ServerConfigHelper(false).addService(ID,
278: TcpProxyService.class.getName(), String
279: .valueOf(port));
280: } catch (Exception e) {
281: throw new EZBComponentException(
282: "Cannot add TcpProxy service", e);
283: }
284:
285: // connect to the collocated server
286: try {
287: connectToCollocated();
288: } catch (JoramException e) {
289: throw new EZBComponentException(
290: "Cannot connect to the collocated server", e);
291: }
292:
293: // Create anonymous user
294: try {
295: joramAdapter.createUser("anonymous", "anonymous");
296: } catch (AdminException e) {
297: throw new EZBComponentException(
298: "Cannot create anonymous user", e);
299:
300: }
301:
302: // start resource adapter
303: try {
304: joramAdapter.start(bootstrapContext);
305: } catch (ResourceAdapterInternalException e) {
306: throw new EZBComponentException(
307: "Cannot start the resource adapter of JORAM", e);
308: }
309:
310: // create factories
311: try {
312: createConnectionFactories();
313: } catch (JoramException e) {
314: throw new EZBComponentException(
315: "Cannot create connection factories", e);
316: }
317:
318: // initial topics and queues
319: try {
320: createInitialTopics();
321: } catch (JoramException e) {
322: throw new EZBComponentException(
323: "Cannot create initial topics", e);
324: }
325:
326: try {
327: createInitialQueues();
328: } catch (JoramException e) {
329: throw new EZBComponentException(
330: "Cannot create initial queues", e);
331: }
332:
333: // Create and bind ActivationSpec
334: ActivationSpec activationSpec = new ActivationSpecImpl();
335: try {
336: activationSpec.setResourceAdapter(joramAdapter);
337: } catch (ResourceException e) {
338: throw new EZBComponentException(
339: "Cannot set resource adapter on activation spec object",
340: e);
341: }
342: try {
343: ictx.rebind("joramActivationSpec", activationSpec);
344: } catch (NamingException e) {
345: throw new EZBComponentException(
346: "Cannot bind activation spec object", e);
347: }
348:
349: logger.info("Joram version ''{0}'' started on {1}:{2}.",
350: ConnectionMetaData.providerVersion, host, String
351: .valueOf(port));
352: started = true;
353: }
354:
355: /**
356: * Stops the JORAM server (if started).
357: * @throws EZBComponentException if stop is failing
358: */
359: public void stop() throws EZBComponentException {
360: if (!started) {
361: throw new IllegalStateException(
362: "Cannot stop a server as it was not started");
363: }
364:
365: // Stop adapter
366: joramAdapter.stop();
367:
368: // disconnect
369: disconnectFromCollocated();
370:
371: // stop
372: AgentServer.stop();
373:
374: // Stop threads of the workmanager
375: workManager.stopThreads();
376: }
377:
378: /**
379: * Connect to the collocated server to performg administration tasks. It
380: * needs to be called before any admin task.
381: * @throws JoramException if the connection to the collocated server fails
382: */
383: private void connectToCollocated() throws JoramException {
384: try {
385: AdminModule.collocatedConnect("root", "root");
386: } catch (ConnectException e) {
387: throw new JoramException(
388: "Cannot connect to the collocated server for the administration",
389: e);
390: } catch (AdminException e) {
391: throw new JoramException(
392: "Cannot connect to the collocated server for the administration",
393: e);
394: }
395: }
396:
397: /**
398: * Disconnect from the collocated server. It needs to be called when
399: * stopping to use admin task.
400: * @throws AdminException
401: * @throws ConnectException
402: */
403: private void disconnectFromCollocated() {
404: AdminModule.disconnect();
405: }
406:
407: /**
408: * Create connection factories.
409: * @throws JoramException if factories are not created.
410: */
411: private void createConnectionFactories() throws JoramException {
412: // managed
413: joramAdapter.createCF("CF");
414: joramAdapter.createQCF("QCF");
415: joramAdapter.createTCF("TCF");
416:
417: // Create connection factories that will be used by a pure JMS Client
418: ConnectionFactory jcf = null;
419: TopicConnectionFactory jtcf = null;
420: QueueConnectionFactory jqcf = null;
421:
422: String name = CONN_FACT_NAME;
423: try {
424: jcf = TcpConnectionFactory.create(host, port);
425: ictx.rebind(name, jcf);
426: } catch (NamingException e) {
427: throw new JoramException(
428: "Cannot create a factory with the name '" + name
429: + "'.", e);
430: }
431:
432: name = QUEUE_CONN_FACT_NAME;
433: try {
434: jqcf = QueueTcpConnectionFactory.create(host, port);
435: ictx.rebind(name, jqcf);
436: } catch (NamingException e) {
437: throw new JoramException(
438: "Cannot create a factory with the name '" + name
439: + "'.", e);
440: }
441:
442: name = TOPIC_CONN_FACT_NAME;
443: try {
444: jtcf = TopicTcpConnectionFactory.create(host, port);
445: ictx.rebind(name, jtcf);
446: } catch (NamingException e) {
447: throw new JoramException(
448: "Cannot create a factory with the name '" + name
449: + "'.", e);
450: }
451: }
452:
453: /**
454: * Creates a topic with a given name.
455: * @param name the topic's name.
456: * @throws JoramException if the topic can't be created
457: */
458: private void createTopic(final String name) throws JoramException {
459: try {
460: joramAdapter.createTopic(name);
461: } catch (AdminException e) {
462: throw new JoramException(
463: "Cannot create a topic with the name '" + name
464: + "'.", e);
465: }
466: }
467:
468: /**
469: * Creates a queue with a given name.
470: * @param name the topic's name.
471: * @throws JoramException if the queue can't be created
472: */
473: private void createQueue(final String name) throws JoramException {
474: try {
475: joramAdapter.createQueue(name);
476: } catch (AdminException e) {
477: throw new JoramException(
478: "Cannot create a queue with the name '" + name
479: + "'.", e);
480: }
481: }
482:
483: /**
484: * Create the list of the defined topics.
485: * @throws JoramException if topics can't be created
486: */
487: private void createInitialTopics() throws JoramException {
488: for (String topic : topics) {
489: createTopic(topic);
490: }
491: }
492:
493: /**
494: * Create the list of the defined queues.
495: * @throws JoramException if queues can't be created
496: */
497: private void createInitialQueues() throws JoramException {
498: for (String queue : queues) {
499: createQueue(queue);
500: }
501: }
502:
503: /**
504: * Sets the initial queues of the Joram server.
505: * @param queues the list of the name of the queues.
506: */
507: public void setQueues(final List<String> queues) {
508: this .queues = queues;
509: }
510:
511: /**
512: * Gets the initial queues of the Joram server.
513: * @return the initial queues of the Joram server.
514: */
515: public List<String> getQueues() {
516: return this .queues;
517: }
518:
519: /**
520: * Sets the initial topics of the Joram server.
521: * @param topics the list of the name of the topics.
522: */
523: public void setTopics(final List<String> topics) {
524: this .topics = topics;
525: }
526:
527: /**
528: * Gets the initial topics of the Joram server.
529: * @return the initial topics of the Joram server.
530: */
531: public List<String> getTopics() {
532: return topics;
533: }
534:
535: /**
536: * Gets the resource adapter instance.
537: * @return resource adapter instance.
538: */
539: public ResourceAdapter getResourceAdapter() {
540: return joramAdapter;
541: }
542:
543: /**
544: * Sets the hostname to use.
545: * @param host the host to use.
546: */
547: public void setHostname(final String host) {
548: this .host = host;
549: }
550:
551: /**
552: * Sets the port number to use.
553: * @param port the given port.
554: */
555: public void setPort(final int port) {
556: this.port = port;
557: }
558:
559: }
|