001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.jbi.nmr.flow.jca;
018
019 import java.io.Serializable;
020 import java.util.Map;
021 import java.util.Set;
022 import java.util.Timer;
023 import java.util.concurrent.ConcurrentHashMap;
024 import java.util.concurrent.CopyOnWriteArraySet;
025 import java.util.concurrent.atomic.AtomicBoolean;
026
027 import javax.jbi.JBIException;
028 import javax.jbi.messaging.MessageExchange;
029 import javax.jbi.messaging.MessageExchange.Role;
030 import javax.jbi.messaging.MessagingException;
031 import javax.jbi.servicedesc.ServiceEndpoint;
032 import javax.jms.Connection;
033 import javax.jms.ConnectionFactory;
034 import javax.jms.DeliveryMode;
035 import javax.jms.Destination;
036 import javax.jms.JMSException;
037 import javax.jms.Message;
038 import javax.jms.MessageListener;
039 import javax.jms.MessageProducer;
040 import javax.jms.ObjectMessage;
041 import javax.jms.Session;
042 import javax.resource.ResourceException;
043 import javax.resource.spi.BootstrapContext;
044 import javax.resource.spi.ConnectionManager;
045 import javax.resource.spi.UnavailableException;
046 import javax.resource.spi.XATerminator;
047 import javax.resource.spi.endpoint.MessageEndpointFactory;
048 import javax.resource.spi.work.WorkManager;
049 import javax.transaction.HeuristicMixedException;
050 import javax.transaction.HeuristicRollbackException;
051 import javax.transaction.InvalidTransactionException;
052 import javax.transaction.NotSupportedException;
053 import javax.transaction.RollbackException;
054 import javax.transaction.Status;
055 import javax.transaction.SystemException;
056 import javax.transaction.Transaction;
057 import javax.transaction.TransactionManager;
058
059 import org.apache.activemq.ActiveMQConnectionFactory;
060 import org.apache.activemq.advisory.AdvisorySupport;
061 import org.apache.activemq.command.ActiveMQDestination;
062 import org.apache.activemq.command.ActiveMQMessage;
063 import org.apache.activemq.command.ActiveMQQueue;
064 import org.apache.activemq.command.ActiveMQTopic;
065 import org.apache.activemq.command.ConsumerId;
066 import org.apache.activemq.command.ConsumerInfo;
067 import org.apache.activemq.command.RemoveInfo;
068 import org.apache.activemq.ra.ActiveMQActivationSpec;
069 import org.apache.activemq.ra.ActiveMQManagedConnectionFactory;
070 import org.apache.activemq.ra.ActiveMQResourceAdapter;
071 import org.apache.geronimo.transaction.manager.NamedXAResource;
072 import org.apache.geronimo.transaction.manager.RecoverableTransactionManager;
073 import org.apache.servicemix.JbiConstants;
074 import org.apache.servicemix.executors.Executor;
075 import org.apache.servicemix.executors.ExecutorFactory;
076 import org.apache.servicemix.executors.WorkManagerWrapper;
077 import org.apache.servicemix.jbi.event.ComponentAdapter;
078 import org.apache.servicemix.jbi.event.ComponentEvent;
079 import org.apache.servicemix.jbi.event.ComponentListener;
080 import org.apache.servicemix.jbi.event.EndpointAdapter;
081 import org.apache.servicemix.jbi.event.EndpointEvent;
082 import org.apache.servicemix.jbi.event.EndpointListener;
083 import org.apache.servicemix.jbi.messaging.MessageExchangeImpl;
084 import org.apache.servicemix.jbi.nmr.Broker;
085 import org.apache.servicemix.jbi.nmr.flow.AbstractFlow;
086 import org.apache.servicemix.jbi.servicedesc.EndpointSupport;
087 import org.apache.servicemix.jbi.servicedesc.InternalEndpoint;
088 import org.jencks.SingletonEndpointFactory;
089 import org.jencks.factory.ConnectionManagerFactoryBean;
090
091 /**
092 * Use for message routing among a network of containers. All
093 * routing/registration happens automatically.
094 *
095 * @version $Revision: 681324 $
096 * @org.apache.xbean.XBean element="jcaFlow"
097 */
098 public class JCAFlow extends AbstractFlow implements MessageListener {
099
100 private static final String INBOUND_PREFIX = "org.apache.servicemix.jca.";
101
102 private String jmsURL = "tcp://localhost:61616";
103 private ActiveMQConnectionFactory connectionFactory;
104 private ConnectionFactory managedConnectionFactory;
105 private String broadcastDestinationName = "org.apache.servicemix.JCAFlow";
106 private ActiveMQTopic broadcastTopic;
107 private Map<String, Connector> connectorMap = new ConcurrentHashMap<String, Connector>();
108 private AtomicBoolean started = new AtomicBoolean(false);
109 private Set<String> subscriberSet = new CopyOnWriteArraySet<String>();
110 private ConnectionManager connectionManager;
111 private Connector containerConnector;
112 private Connector broadcastConnector;
113 private Connector advisoryConnector;
114 private ActiveMQTopic advisoryTopic;
115 private EndpointListener endpointListener;
116 private ComponentListener componentListener;
117
118 public JCAFlow() {
119 }
120
121 public JCAFlow(String jmsURL) {
122 this.jmsURL = jmsURL;
123 }
124
125 /**
126 * The type of Flow
127 *
128 * @return the type
129 */
130 public String getDescription() {
131 return "jca";
132 }
133
134 /**
135 * Returns the JMS URL for this flow
136 *
137 * @return Returns the jmsURL.
138 */
139 public String getJmsURL() {
140 return jmsURL;
141 }
142
143 /**
144 * Sets the JMS URL for this flow
145 *
146 * @param jmsURL
147 * The jmsURL to set.
148 */
149 public void setJmsURL(String jmsURL) {
150 this.jmsURL = jmsURL;
151 }
152
153 /**
154 * Returns the ConnectionFactory for this flow
155 *
156 * @return Returns the connectionFactory.
157 */
158 public ActiveMQConnectionFactory getConnectionFactory() {
159 return connectionFactory;
160 }
161
162 /**
163 * Sets the ConnectionFactory for this flow
164 *
165 * @param connectionFactory
166 * The connectionFactory to set.
167 */
168 public void setConnectionFactory(ActiveMQConnectionFactory connectionFactory) {
169 this.connectionFactory = connectionFactory;
170 }
171
172 /**
173 * Returns the Broadcast Destination Name for this flow
174 *
175 * @return Returns the broadcastDestinationName.
176 */
177 public String getBroadcastDestinationName() {
178 return broadcastDestinationName;
179 }
180
181 /**
182 * Sets the Broadcast Destination Name for this flow
183 *
184 * @param broadcastDestinationName
185 * The broadcastDestinationName to set.
186 */
187 public void setBroadcastDestinationName(String broadcastDestinationName) {
188 this.broadcastDestinationName = broadcastDestinationName;
189 }
190
191 public TransactionManager getTransactionManager() {
192 return (TransactionManager) broker.getContainer().getTransactionManager();
193 }
194
195 /**
196 * Initialize the Region
197 *
198 * @param broker
199 * @throws JBIException
200 */
201 public void init(Broker broker) throws JBIException {
202 log.debug(broker.getContainer().getName() + ": Initializing jca flow");
203 super.init(broker);
204 // Create and register endpoint listener
205 endpointListener = new EndpointAdapter() {
206 public void internalEndpointRegistered(EndpointEvent event) {
207 onInternalEndpointRegistered(event, true);
208 }
209
210 public void internalEndpointUnregistered(EndpointEvent event) {
211 onInternalEndpointUnregistered(event, true);
212 }
213 };
214 broker.getContainer().addListener(endpointListener);
215 // Create and register component listener
216 componentListener = new ComponentAdapter() {
217 public void componentStarted(ComponentEvent event) {
218 onComponentStarted(event);
219 }
220
221 public void componentStopped(ComponentEvent event) {
222 onComponentStopped(event);
223 }
224 };
225 broker.getContainer().addListener(componentListener);
226 try {
227 if (connectionFactory == null) {
228 connectionFactory = new ActiveMQConnectionFactory(jmsURL);
229 }
230
231 // Inbound connector
232 ActiveMQDestination dest = new ActiveMQQueue(INBOUND_PREFIX + broker.getContainer().getName());
233 containerConnector = new Connector(dest, this, true);
234 containerConnector.start();
235
236 // Outbound connector
237 ActiveMQResourceAdapter outboundRa = new ActiveMQResourceAdapter();
238 outboundRa.setConnectionFactory(connectionFactory);
239 //
240 // We need to explicitly set the server url unless we use the
241 // default jms url, so set it.
242 //
243 if (outboundRa.getInfo().getServerUrl() == null) {
244 log.info("ActiveMQResourceAdapter server url was null. Setting it to: " + jmsURL);
245 outboundRa.getInfo().setServerUrl(jmsURL);
246 }
247 ActiveMQManagedConnectionFactory mcf = new ActiveMQManagedConnectionFactory();
248 mcf.setResourceAdapter(outboundRa);
249 managedConnectionFactory = (ConnectionFactory) mcf.createConnectionFactory(getConnectionManager());
250
251 // Inbound broadcast
252 broadcastTopic = new ActiveMQTopic(broadcastDestinationName);
253 advisoryTopic = AdvisorySupport.getConsumerAdvisoryTopic((ActiveMQDestination) broadcastTopic);
254 } catch (Exception e) {
255 log.error("Failed to initialize JCAFlow", e);
256 throw new JBIException(e);
257 }
258 }
259
260 /**
261 * start the flow
262 *
263 * @throws JBIException
264 */
265 public void start() throws JBIException {
266 if (started.compareAndSet(false, true)) {
267 super.start();
268 try {
269 // Inbound broadcast
270 MessageListener listener = new MessageListener() {
271 public void onMessage(Message message) {
272 try {
273 Object obj = ((ObjectMessage) message).getObject();
274 if (obj instanceof EndpointEvent) {
275 EndpointEvent event = (EndpointEvent) obj;
276 String container = ((InternalEndpoint) event.getEndpoint()).getComponentNameSpace().getContainerName();
277 if (!getBroker().getContainer().getName().equals(container)) {
278 if (event.getEventType() == EndpointEvent.INTERNAL_ENDPOINT_REGISTERED) {
279 onRemoteEndpointRegistered(event);
280 } else if (event.getEventType() == EndpointEvent.INTERNAL_ENDPOINT_UNREGISTERED) {
281 onRemoteEndpointUnregistered(event);
282 }
283 }
284 }
285 } catch (Exception e) {
286 log.error("Error processing incoming broadcast message", e);
287 }
288 }
289 };
290 broadcastConnector = new Connector(broadcastTopic, listener, false);
291 broadcastConnector.start();
292
293 listener = new MessageListener() {
294 public void onMessage(Message message) {
295 if (started.get()) {
296 onAdvisoryMessage(((ActiveMQMessage) message).getDataStructure());
297 }
298 }
299 };
300 advisoryConnector = new Connector(advisoryTopic, listener, false);
301 advisoryConnector.start();
302 } catch (Exception e) {
303 throw new JBIException("JMSException caught in start: " + e.getMessage(), e);
304 }
305 }
306 }
307
308 /**
309 * stop the flow
310 *
311 * @throws JBIException
312 */
313 public void stop() throws JBIException {
314 if (started.compareAndSet(true, false)) {
315 super.stop();
316 try {
317 broadcastConnector.stop();
318 } catch (Exception e) {
319 log.debug("Error closing jca connector", e);
320 }
321 try {
322 advisoryConnector.stop();
323 } catch (Exception e) {
324 log.debug("Error closing jca connector", e);
325 }
326 }
327 }
328
329 public void shutDown() throws JBIException {
330 super.shutDown();
331 stop();
332 // Remove endpoint listener
333 broker.getContainer().removeListener(endpointListener);
334 // Remove component listener
335 broker.getContainer().removeListener(componentListener);
336 // Destroy connectors
337 while (!connectorMap.isEmpty()) {
338 Connector connector = connectorMap.remove(connectorMap.keySet().iterator().next());
339 try {
340 connector.stop();
341 } catch (Exception e) {
342 log.debug("Error closing jca connector", e);
343 }
344 }
345 try {
346 containerConnector.stop();
347 } catch (Exception e) {
348 log.debug("Error closing jca connector", e);
349 }
350 }
351
352 /**
353 * useful for testing
354 *
355 * @return number of containers in the network
356 */
357 public int numberInNetwork() {
358 return subscriberSet.size();
359 }
360
361 /**
362 * Check if the flow can support the requested QoS for this exchange
363 *
364 * @param me
365 * the exchange to check
366 * @return true if this flow can handle the given exchange
367 */
368 public boolean canHandle(MessageExchange me) {
369 if (isSynchronous(me)) {
370 return false;
371 }
372 return true;
373 }
374
375 public void onInternalEndpointRegistered(EndpointEvent event, boolean broadcast) {
376 if (!started.get()) {
377 return;
378 }
379 try {
380 String key = EndpointSupport.getKey(event.getEndpoint());
381 if (!connectorMap.containsKey(key)) {
382 ActiveMQDestination dest = new ActiveMQQueue(INBOUND_PREFIX + key);
383 Connector connector = new Connector(dest, this, true);
384 connector.start();
385 connectorMap.put(key, connector);
386 }
387 // broadcast change to the network
388 if (broadcast) {
389 log.debug(broker.getContainer().getName() + ": broadcasting info for " + event);
390 sendJmsMessage(broadcastTopic, event, false, false);
391 }
392 } catch (Exception e) {
393 log.error("Cannot create consumer for " + event.getEndpoint(), e);
394 }
395 }
396
397 public void onInternalEndpointUnregistered(EndpointEvent event, boolean broadcast) {
398 try {
399 String key = EndpointSupport.getKey(event.getEndpoint());
400 Connector connector = connectorMap.remove(key);
401 if (connector != null) {
402 connector.stop();
403 }
404 // broadcast change to the network
405 if (broadcast) {
406 log.debug(broker.getContainer().getName() + ": broadcasting info for " + event);
407 sendJmsMessage(broadcastTopic, event, false, false);
408 }
409 } catch (Exception e) {
410 log.error("Cannot destroy consumer for " + event, e);
411 }
412 }
413
414 public void onComponentStarted(ComponentEvent event) {
415 if (!started.get()) {
416 return;
417 }
418 try {
419 String key = event.getComponent().getName();
420 if (!connectorMap.containsKey(key)) {
421 ActiveMQDestination dest = new ActiveMQQueue(INBOUND_PREFIX + key);
422 Connector connector = new Connector(dest, this, true);
423 connector.start();
424 connectorMap.put(key, connector);
425 }
426 } catch (Exception e) {
427 log.error("Cannot create consumer for component " + event.getComponent().getName(), e);
428 }
429 }
430
431 public void onComponentStopped(ComponentEvent event) {
432 try {
433 String key = event.getComponent().getName();
434 Connector connector = connectorMap.remove(key);
435 if (connector != null) {
436 connector.stop();
437 }
438 } catch (Exception e) {
439 log.error("Cannot destroy consumer for component " + event.getComponent().getName(), e);
440 }
441 }
442
443 public void onRemoteEndpointRegistered(EndpointEvent event) {
444 log.debug(broker.getContainer().getName() + ": adding remote endpoint: " + event.getEndpoint());
445 broker.getContainer().getRegistry().registerRemoteEndpoint(event.getEndpoint());
446 }
447
448 public void onRemoteEndpointUnregistered(EndpointEvent event) {
449 log.debug(broker.getContainer().getName() + ": removing remote endpoint: " + event.getEndpoint());
450 broker.getContainer().getRegistry().unregisterRemoteEndpoint(event.getEndpoint());
451 }
452
453 /**
454 * Distribute an ExchangePacket
455 *
456 * @param me
457 * @throws MessagingException
458 */
459 protected void doSend(MessageExchangeImpl me) throws MessagingException {
460 doRouting(me);
461 }
462
463 /**
464 * Distribute an ExchangePacket
465 *
466 * @param me
467 * @throws MessagingException
468 */
469 public void doRouting(final MessageExchangeImpl me) throws MessagingException {
470 // let ActiveMQ do the routing ...
471 try {
472 String destination;
473 if (me.getRole() == Role.PROVIDER) {
474 if (me.getDestinationId() == null) {
475 destination = INBOUND_PREFIX + EndpointSupport.getKey(me.getEndpoint());
476 } else if (Boolean.TRUE.equals(me.getProperty(JbiConstants.STATELESS_PROVIDER)) && !isSynchronous(me)) {
477 destination = INBOUND_PREFIX + me.getDestinationId().getName();
478 } else {
479 destination = INBOUND_PREFIX + me.getDestinationId().getContainerName();
480 }
481 } else {
482 if (me.getSourceId() == null) {
483 throw new IllegalStateException("No sourceId set on the exchange");
484 } else if (Boolean.TRUE.equals(me.getProperty(JbiConstants.STATELESS_CONSUMER)) && !isSynchronous(me)) {
485 // If the consumer is stateless and has specified a sender
486 // endpoint,
487 // this exchange will be sent to the given endpoint queue,
488 // so that
489 // This property must have been created using
490 // EndpointSupport.getKey
491 // fail-over and load-balancing can be achieved
492 if (me.getProperty(JbiConstants.SENDER_ENDPOINT) != null) {
493 destination = INBOUND_PREFIX + me.getProperty(JbiConstants.SENDER_ENDPOINT);
494 } else {
495 destination = INBOUND_PREFIX + me.getSourceId().getName();
496 }
497 } else {
498 destination = INBOUND_PREFIX + me.getSourceId().getContainerName();
499 }
500 }
501 if (me.isTransacted()) {
502 me.setTxState(MessageExchangeImpl.TX_STATE_ENLISTED);
503 }
504 sendJmsMessage(new ActiveMQQueue(destination), me, isPersistent(me), me.isTransacted());
505 } catch (JMSException e) {
506 log.error("Failed to send exchange: " + me + " internal JMS Network", e);
507 throw new MessagingException(e);
508 } catch (SystemException e) {
509 log.error("Failed to send exchange: " + me + " transaction problem", e);
510 throw new MessagingException(e);
511 }
512 }
513
514 /**
515 * MessageListener implementation
516 *
517 * @param message
518 */
519 public void onMessage(Message message) {
520 try {
521 if (message != null && started.get()) {
522 ObjectMessage objMsg = (ObjectMessage) message;
523 final MessageExchangeImpl me = (MessageExchangeImpl) objMsg.getObject();
524 // Hack for redelivery: AMQ is too optimized and the object is
525 // the same upon redelivery
526 // so that there are side effect (the exchange state may have
527 // been modified)
528 // See http://jira.activemq.org/jira/browse/AMQ-519
529 // me = (MessageExchangeImpl) ((ActiveMQObjectMessage)
530 // ((ActiveMQObjectMessage) message).copy()).getObject();
531 TransactionManager tm = (TransactionManager) getTransactionManager();
532 if (tm != null) {
533 me.setTransactionContext(tm.getTransaction());
534 }
535 if (me.getDestinationId() == null) {
536 ServiceEndpoint se = me.getEndpoint();
537 se = broker.getContainer().getRegistry().getInternalEndpoint(se.getServiceName(), se.getEndpointName());
538 me.setEndpoint(se);
539 me.setDestinationId(((InternalEndpoint) se).getComponentNameSpace());
540 }
541 super.doRouting(me);
542 }
543 } catch (JMSException jmsEx) {
544 log.error("Caught an exception unpacking JMS Message: ", jmsEx);
545 } catch (MessagingException e) {
546 log.error("Caught an exception routing ExchangePacket: ", e);
547 } catch (SystemException e) {
548 log.error("Caught an exception acessing transaction context: ", e);
549 }
550 }
551
552 protected void onAdvisoryMessage(Object obj) {
553 if (obj instanceof ConsumerInfo) {
554 ConsumerInfo info = (ConsumerInfo) obj;
555 subscriberSet.add(info.getConsumerId().getConnectionId());
556 ServiceEndpoint[] endpoints = broker.getContainer().getRegistry().getEndpointsForInterface(null);
557 for (int i = 0; i < endpoints.length; i++) {
558 if (endpoints[i] instanceof InternalEndpoint && ((InternalEndpoint) endpoints[i]).isLocal()) {
559 onInternalEndpointRegistered(new EndpointEvent(endpoints[i], EndpointEvent.INTERNAL_ENDPOINT_REGISTERED), true);
560 }
561 }
562 } else if (obj instanceof RemoveInfo) {
563 ConsumerId id = (ConsumerId) ((RemoveInfo) obj).getObjectId();
564 subscriberSet.remove(id.getConnectionId());
565 removeAllPackets(id.getConnectionId());
566 }
567 }
568
569 private void removeAllPackets(String containerName) {
570 // TODO: broker.getRegistry().unregisterRemoteEndpoints(containerName);
571 }
572
573 public ConnectionManager getConnectionManager() throws Exception {
574 if (connectionManager == null) {
575 ConnectionManagerFactoryBean cmfb = new ConnectionManagerFactoryBean();
576 TransactionManager txmgr = (TransactionManager) broker.getContainer().getTransactionManager();
577 if (!(txmgr instanceof RecoverableTransactionManager)) {
578 txmgr = new RecoverableTransactionManagerWrapper(txmgr);
579 }
580 cmfb.setTransactionManager((RecoverableTransactionManager) txmgr);
581 cmfb.setTransaction("xa");
582 cmfb.afterPropertiesSet();
583 connectionManager = (ConnectionManager) cmfb.getObject();
584 }
585 return connectionManager;
586 }
587
588 public void setConnectionManager(ConnectionManager connectionManager) {
589 this.connectionManager = connectionManager;
590 }
591
592 public String toString() {
593 return broker.getContainer().getName() + " JCAFlow";
594 }
595
596 private void sendJmsMessage(Destination dest, Serializable object, boolean persistent, boolean transacted) throws JMSException,
597 SystemException {
598 if (transacted) {
599 TransactionManager tm = (TransactionManager) getBroker().getContainer().getTransactionManager();
600 if (tm.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
601 return;
602 }
603 }
604 Connection connection = managedConnectionFactory.createConnection();
605 try {
606 Session session = connection.createSession(transacted, transacted ? Session.SESSION_TRANSACTED : Session.AUTO_ACKNOWLEDGE);
607 ObjectMessage msg = session.createObjectMessage(object);
608 MessageProducer producer = session.createProducer(dest);
609 producer.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
610 producer.send(msg);
611 } finally {
612 connection.close();
613 }
614 }
615
616 class Connector {
617 private ActiveMQResourceAdapter ra;
618
619 private MessageEndpointFactory endpointFactory;
620
621 private ActiveMQActivationSpec spec;
622
623 private Executor executor;
624
625 public Connector(ActiveMQDestination destination, MessageListener listener, boolean transacted) {
626 ra = new ActiveMQResourceAdapter();
627 ra.setConnectionFactory(connectionFactory);
628 SingletonEndpointFactory ef = new SingletonEndpointFactory(listener, transacted ? getTransactionManager() : null);
629 ef.setName(INBOUND_PREFIX + broker.getContainer().getName());
630 endpointFactory = ef;
631 spec = new ActiveMQActivationSpec();
632 spec.setActiveMQDestination(destination);
633 }
634
635 public void start() throws ResourceException {
636 ExecutorFactory factory = broker.getContainer().getExecutorFactory();
637 executor = factory.createExecutor("flow.jca." + spec.getDestination());
638 BootstrapContext context = new SimpleBootstrapContext(new WorkManagerWrapper(executor));
639 ra.start(context);
640 spec.setResourceAdapter(ra);
641 ra.endpointActivation(endpointFactory, spec);
642 }
643
644 public void stop() {
645 ra.endpointDeactivation(endpointFactory, spec);
646 ra.stop();
647 executor.shutdown();
648 }
649 }
650
651 class SimpleBootstrapContext implements BootstrapContext {
652 private final WorkManager workManager;
653
654 public SimpleBootstrapContext(WorkManager workManager) {
655 this.workManager = workManager;
656 }
657
658 public Timer createTimer() throws UnavailableException {
659 throw new UnsupportedOperationException();
660 }
661
662 public WorkManager getWorkManager() {
663 return workManager;
664 }
665
666 public XATerminator getXATerminator() {
667 throw new UnsupportedOperationException();
668 }
669
670 }
671
672 public static class RecoverableTransactionManagerWrapper implements RecoverableTransactionManager {
673 private final TransactionManager txMgr;
674
675 public RecoverableTransactionManagerWrapper(TransactionManager txMgr) {
676 this.txMgr = txMgr;
677 }
678
679 public void begin() throws NotSupportedException, SystemException {
680 txMgr.begin();
681 }
682
683 public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
684 RollbackException, SecurityException, SystemException {
685 txMgr.commit();
686 }
687
688 public int getStatus() throws SystemException {
689 return txMgr.getStatus();
690 }
691
692 public Transaction getTransaction() throws SystemException {
693 return txMgr.getTransaction();
694 }
695
696 public void resume(Transaction transaction) throws IllegalStateException, InvalidTransactionException, SystemException {
697 txMgr.resume(transaction);
698 }
699
700 public void rollback() throws IllegalStateException, SecurityException, SystemException {
701 txMgr.rollback();
702 }
703
704 public void setRollbackOnly() throws IllegalStateException, SystemException {
705 txMgr.setRollbackOnly();
706 }
707
708 public void setTransactionTimeout(int i) throws SystemException {
709 txMgr.setTransactionTimeout(i);
710 }
711
712 public Transaction suspend() throws SystemException {
713 return txMgr.suspend();
714 }
715
716 public void recoveryError(Exception e) {
717 throw new UnsupportedOperationException();
718 }
719
720 public void recoverResourceManager(NamedXAResource namedXAResource) {
721 throw new UnsupportedOperationException();
722 }
723 }
724
725 }