001 /**
002 *
003 * Copyright 2004 Protique Ltd
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 **/
018
019 package org.activemq.broker.impl;
020
021 import java.util.ArrayList;
022 import java.util.HashMap;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.Map;
026 import javax.jms.InvalidClientIDException;
027 import javax.jms.InvalidDestinationException;
028 import javax.jms.JMSException;
029 import javax.jms.JMSSecurityException;
030 import javax.transaction.xa.XAException;
031 import org.apache.commons.logging.Log;
032 import org.apache.commons.logging.LogFactory;
033 import org.activemq.ActiveMQConnectionMetaData;
034 import org.activemq.broker.Broker;
035 import org.activemq.broker.BrokerClient;
036 import org.activemq.broker.BrokerConnector;
037 import org.activemq.broker.BrokerContainer;
038 import org.activemq.broker.BrokerContext;
039 import org.activemq.capacity.CapacityMonitorEvent;
040 import org.activemq.capacity.CapacityMonitorEventListener;
041 import org.activemq.io.WireFormat;
042 import org.activemq.io.impl.DefaultWireFormat;
043 import org.activemq.io.util.MemoryBoundedObjectManager;
044 import org.activemq.message.ActiveMQDestination;
045 import org.activemq.message.ActiveMQMessage;
046 import org.activemq.message.ActiveMQXid;
047 import org.activemq.message.ConnectionInfo;
048 import org.activemq.message.ConsumerInfo;
049 import org.activemq.message.DurableUnsubscribe;
050 import org.activemq.message.MessageAck;
051 import org.activemq.message.ProducerInfo;
052 import org.activemq.message.SessionInfo;
053 import org.activemq.security.SecurityAdapter;
054 import org.activemq.service.RedeliveryPolicy;
055 import org.activemq.service.Service;
056 import org.activemq.store.PersistenceAdapter;
057 import org.activemq.transport.DiscoveryAgent;
058 import org.activemq.transport.NetworkConnector;
059 import org.activemq.transport.TransportServerChannel;
060 import org.activemq.util.IdGenerator;
061 import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
062 import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;
063 import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
064 import java.util.Set;
065
066 /**
067 * Represents the ActiveMQ JMS Broker which typically has one or many connectors
068 *
069 * @version $Revision: 1.1.1.1 $
070 */
071 public class BrokerContainerImpl implements BrokerContainer, CapacityMonitorEventListener {
072 public static final String DISABLE_CLEAN_SHUTDOWN_PROPERTY = "activemq.broker.disable-clean-shutdown";
073 private static final Log log = LogFactory.getLog(BrokerContainerImpl.class);
074 private static final boolean useLoggingForShutdownErrors = false;
075
076 private BrokerContext context;
077 private Broker broker;
078 private Map clientIds;
079 private Map consumerInfos;
080 private Map producerInfos;
081 private List transportConnectors;
082 private Thread shutdownHook;
083 private boolean stopped;
084 private List networkConnectors;
085 private DiscoveryAgent discoveryAgent;
086 private Map localDiscoveryDetails;
087 private Set remoteClientIds;
088
089
090 public BrokerContainerImpl() {
091 this(new IdGenerator().generateId());
092 }
093
094 public BrokerContainerImpl(String brokerName) {
095 this(brokerName, BrokerContext.getInstance());
096 }
097
098 public BrokerContainerImpl(String brokerName, MemoryBoundedObjectManager memoryManager) {
099 this(brokerName, BrokerContext.getInstance(), memoryManager);
100 }
101
102 public BrokerContainerImpl(String brokerName,String clusterName) {
103 this(brokerName, clusterName,BrokerContext.getInstance());
104
105 }
106
107 public BrokerContainerImpl(String brokerName, PersistenceAdapter persistenceAdapter) {
108 this(brokerName, persistenceAdapter, BrokerContext.getInstance());
109 }
110
111 public BrokerContainerImpl(String brokerName, BrokerContext context) {
112 this(new DefaultBroker(brokerName), context);
113 }
114
115 public BrokerContainerImpl(String brokerName, BrokerContext context, MemoryBoundedObjectManager memoryManager) {
116 this(new DefaultBroker(brokerName,memoryManager), context);
117 }
118
119 public BrokerContainerImpl(String brokerName, String clusterName, BrokerContext context) {
120 this(new DefaultBroker(brokerName,clusterName), context);
121 }
122
123 public BrokerContainerImpl(String brokerName, PersistenceAdapter persistenceAdapter, BrokerContext context) {
124 this(new DefaultBroker(brokerName, persistenceAdapter), context);
125 }
126
127 public BrokerContainerImpl(String brokerName,String clusterName, PersistenceAdapter persistenceAdapter, BrokerContext context) {
128 this(new DefaultBroker(brokerName,clusterName, persistenceAdapter), context);
129 }
130
131 /**
132 * @param broker
133 */
134 public BrokerContainerImpl(Broker broker, BrokerContext context) {
135 this.broker = broker;
136 this.context = context;
137 this.clientIds = new ConcurrentHashMap();
138 this.consumerInfos = new ConcurrentHashMap();
139 this.producerInfos = new ConcurrentHashMap();
140 this.transportConnectors = new CopyOnWriteArrayList();
141 this.networkConnectors = new CopyOnWriteArrayList();
142 this.remoteClientIds = new CopyOnWriteArraySet();
143 this.broker.addCapacityEventListener(this);
144
145 // lets register ourselves with the context
146 context.registerContainer(broker.getBrokerName(), this);
147 //register ourselves for vm:// transports
148 context.registerContainer("vm://" + broker.getBrokerName(), this);
149 }
150
151 /**
152 * start the Container
153 *
154 * @throws JMSException
155 */
156 public void start() throws JMSException {
157 log.info("ActiveMQ "+ActiveMQConnectionMetaData.PROVIDER_VERSION+" JMS Message Broker (" + broker.getBrokerName() + ") is starting");
158 log.info("For help or more information please see: http://www.logicblaze.com");
159 broker.start();
160 addShutdownHook();
161
162 // TODO we might not need to copy the collections, as maybe the List might not
163 // throw concurrent modification exception? Couldn't tell from the docs
164 // but I don't think it does
165
166 for (Iterator iter = new ArrayList(networkConnectors).iterator(); iter.hasNext();) {
167 Service connector = (Service) iter.next();
168 connector.start();
169 }
170
171 for (Iterator iter = new ArrayList(transportConnectors).iterator(); iter.hasNext();) {
172 Service connector = (Service) iter.next();
173 connector.start();
174 }
175
176 if (discoveryAgent != null) {
177 discoveryAgent.start();
178
179 localDiscoveryDetails = createLocalDiscoveryDetails();
180 discoveryAgent.registerService(getLocalBrokerName(), localDiscoveryDetails);
181 }
182
183 log.info("ActiveMQ JMS Message Broker (" + broker.getBrokerName() + ") has started");
184 }
185
186 /**
187 * Stop the Container
188 *
189 * @throws JMSException
190 */
191 public synchronized void stop() throws JMSException {
192 if (!stopped) {
193 stopped = true;
194
195 log.info("ActiveMQ Message Broker (" + broker.getBrokerName() + ") is shutting down");
196
197 context.deregisterContainer(broker.getBrokerName(), this);
198
199 try {
200 Runtime.getRuntime().removeShutdownHook(shutdownHook);
201 }
202 catch (Exception e) {
203 log.debug("Caught exception, must be shutting down: " + e);
204 }
205
206 JMSException firstException = null;
207 if (discoveryAgent != null) {
208 try {
209 discoveryAgent.stop();
210 } catch (JMSException e) {
211 firstException = e;
212 log.warn("Could not close discovery agent: " + discoveryAgent + " due to: " + e, e);
213 }
214 }
215
216 for (Iterator iter = new ArrayList(transportConnectors).iterator(); iter.hasNext();) {
217 Service connector = (Service) iter.next();
218 try {
219 connector.stop();
220 }
221 catch (JMSException e) {
222 if (firstException == null) {
223 firstException = e;
224 }
225 log.warn("Could not close transport connector: " + connector + " due to: " + e, e);
226 }
227 }
228 transportConnectors.clear();
229
230 for (Iterator iter = new ArrayList(networkConnectors).iterator(); iter.hasNext();) {
231 Service connector = (Service) iter.next();
232 try {
233 connector.stop();
234 }
235 catch (JMSException e) {
236 if (firstException == null) {
237 firstException = e;
238 }
239 log.warn("Could not close network connector: " + connector + " due to: " + e, e);
240 }
241 }
242 networkConnectors.clear();
243
244
245
246 // lets close all the channels
247 // note that this Map implementation does not throw concurrent modification exception
248 for (Iterator iter = clientIds.values().iterator();iter.hasNext();) {
249 // should remove clients from parent container?
250 BrokerClient client = (BrokerClient) iter.next();
251 if (client != null) {
252 try {
253 client.stop();
254 }
255 catch (JMSException e) {
256 if (firstException == null) {
257 firstException = e;
258 }
259 log.warn("Could not close client: " + client + " due to: " + e, e);
260 }
261 }
262 }
263 clientIds.clear();
264
265 broker.removeCapacityEventListener(this);
266 broker.stop();
267
268 log.info("ActiveMQ JMS Message Broker (" + broker.getBrokerName() + ") stopped");
269
270 if (firstException != null) {
271 throw firstException;
272 }
273 }
274 }
275
276 /**
277 * registers a new Connection
278 *
279 * @param client
280 * @param info infomation about the client-side Connection
281 * @throws InvalidClientIDException if the ClientID of the Connection is a duplicate
282 */
283 synchronized public void registerConnection(BrokerClient client, ConnectionInfo info) throws JMSException {
284 String clientId = info.getClientId();
285 if (clientIds.containsKey(clientId)) {
286 int timeout = 5000;
287 log.info("Got duplicate client with id: " + clientId + ". Giving the existing client " + timeout + " millis to prove it's alive.");
288
289 // Assert that the existing client is alive
290 BrokerClient existingClient = (BrokerClient) clientIds.get(clientId);
291 JMSException ex = null;
292 boolean isValid = true;
293 try {
294 existingClient.validateConnection(timeout);
295 } catch (JMSException e) {
296 isValid = false;
297 ex = e;
298 }
299 if (isValid) {
300 // The existing client is valid, so kick the new client
301 log.info("Client: " + clientId + " on transport: " + existingClient.getChannel()
302 + "' is alive, rejecting new client on transport: " + client.getChannel());
303 throw new InvalidClientIDException("Duplicate clientId: " + info);
304 } else {
305 // A transport error occured or the existing client did not
306 // respond in time, so kick it and let the new client connect.
307 log.info("Replacing client: " + clientId + " on transport: " + existingClient.getChannel() + " ("
308 + ex.getMessage() + ") with client on transport: " + client.getChannel());
309
310 // @TODO: Not sure this is the proper way to close the existing client
311 existingClient.cleanUp();
312 existingClient.stop();
313 }
314
315 }
316 getBroker().addClient(client, info);
317 log.info("Adding new client: " + clientId + " on transport: " + client.getChannel());
318 clientIds.put(clientId, client);
319 }
320
321 /**
322 * un-registers a Connection
323 *
324 * @param client
325 * @param info infomation about the client-side Connection
326 * @throws JMSException
327 */
328 public void deregisterConnection(BrokerClient client, ConnectionInfo info) throws JMSException {
329 String clientId = client.getClientID();
330 if (clientId != null) {
331 Object answer = clientIds.remove(clientId);
332 if (answer != null) {
333 log.info("Removing client: " + clientId + " on transport: " + client.getChannel());
334 getBroker().removeClient(client, info);
335 }
336 else {
337 log.warn("Got duplicate deregisterConnection for client: " + clientId);
338 }
339 }
340 else {
341 log.warn("No clientID available for client: " + client);
342 }
343 }
344
345 /**
346 * Registers a MessageConsumer
347 *
348 * @param client
349 * @param info
350 * @throws JMSException
351 * @throws JMSSecurityException if client authentication fails for the Destination the Consumer applies for
352 */
353 public void registerMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
354 consumerInfos.put(info, client);
355 getBroker().addMessageConsumer(client, info);
356 }
357
358 /**
359 * De-register a MessageConsumer from the Broker
360 *
361 * @param client
362 * @param info
363 * @throws JMSException
364 */
365 public void deregisterMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
366 consumerInfos.remove(info);
367 getBroker().removeMessageConsumer(client, info);
368 }
369
370 /**
371 * Registers a MessageProducer
372 *
373 * @param client
374 * @param info
375 * @throws JMSException
376 * @throws JMSSecurityException if client authentication fails for the Destination the Consumer applies for
377 */
378 public void registerMessageProducer(BrokerClient client, ProducerInfo info) throws JMSException {
379 ActiveMQDestination dest = info.getDestination();
380 checkTempDestinationExistance(dest);
381 getBroker().addMessageProducer(client, info);
382
383 producerInfos.put(info, client);
384 }
385
386 /**
387 * De-register a MessageProducer from the Broker
388 *
389 * @param client
390 * @param info
391 * @throws JMSException
392 */
393 public void deregisterMessageProducer(BrokerClient client, ProducerInfo info) throws JMSException {
394 getBroker().removeMessageProducer(client, info);
395
396 producerInfos.remove(info);
397 }
398
399 /**
400 * Register a client-side Session (used for Monitoring)
401 *
402 * @param client
403 * @param info
404 * @throws JMSException
405 */
406 public void registerSession(BrokerClient client, SessionInfo info) throws JMSException {
407 }
408
409 /**
410 * De-register a client-side Session from the Broker (used for monitoring)
411 *
412 * @param client
413 * @param info
414 * @throws JMSException
415 */
416 public void deregisterSession(BrokerClient client, SessionInfo info) throws JMSException {
417 }
418
419 /**
420 * Start a transaction from the Client session
421 *
422 * @param client
423 * @param transactionId
424 * @throws JMSException
425 */
426 public void startTransaction(BrokerClient client, String transactionId) throws JMSException {
427 getBroker().startTransaction(client, transactionId);
428 }
429
430 /**
431 * Rollback a transacton
432 *
433 * @param client
434 * @param transactionId
435 * @throws JMSException
436 */
437 public void rollbackTransaction(BrokerClient client, String transactionId) throws JMSException {
438 getBroker().rollbackTransaction(client, transactionId);
439 }
440
441 /**
442 * Commit a transaction
443 *
444 * @param client
445 * @param transactionId
446 * @throws JMSException
447 */
448 public void commitTransaction(BrokerClient client, String transactionId) throws JMSException {
449 getBroker().commitTransaction(client, transactionId);
450 }
451
452 /**
453 * Send a non-transacted message to the Broker
454 *
455 * @param client
456 * @param message
457 * @throws JMSException
458 */
459 public void sendMessage(BrokerClient client, ActiveMQMessage message) throws JMSException {
460 ActiveMQDestination dest = message.getJMSActiveMQDestination();
461 checkTempDestinationExistance(dest);
462 broker.sendMessage(client, message);
463 }
464
465 /**
466 * register a remote clientID
467 * @param remoteClientID
468 */
469
470 public void registerRemoteClientID(String remoteClientID){
471 remoteClientIds.add(remoteClientID);
472 }
473
474 /**
475 * deregister a remote clientID
476 * @param remoteClientID
477 */
478 public void deregisterRemoteClientID(String remoteClientID){
479 remoteClientIds.remove(remoteClientID);
480 }
481
482 /**
483 * @param dest
484 * @throws InvalidDestinationException
485 */
486 private void checkTempDestinationExistance(ActiveMQDestination dest) throws InvalidDestinationException {
487 if (dest != null && dest.isTemporary()) {
488 //check to see if the client that is the target for the temporary destination still exists
489 String clientId = ActiveMQDestination.getClientId(dest);
490 if (clientId == null) {
491 throw new InvalidDestinationException("Destination " + dest.getPhysicalName()
492 + " is a temporary destination with null clientId");
493 }
494 if (!clientIds.containsKey(clientId) && !remoteClientIds.contains(clientId)) {
495 throw new InvalidDestinationException("Destination " + dest.getPhysicalName()
496 + " is no longer valid because the client " + clientId + " no longer exists");
497 }
498 }
499 }
500
501 /**
502 * Acknowledge reciept of a message
503 *
504 * @param client
505 * @param ack
506 * @throws JMSException
507 */
508 public void acknowledgeMessage(BrokerClient client, MessageAck ack) throws JMSException {
509 getBroker().acknowledgeMessage(client, ack);
510 }
511
512 /**
513 * Command to delete a durable topic subscription
514 *
515 * @param client
516 * @param ds
517 * @throws JMSException
518 */
519 public void durableUnsubscribe(BrokerClient client, DurableUnsubscribe ds) throws JMSException {
520 getBroker().deleteSubscription(ds.getClientId(), ds.getSubscriberName());
521 }
522
523 /**
524 * Start an XA transaction.
525 *
526 * @param client
527 * @param xid
528 */
529 public void startTransaction(BrokerClient client, ActiveMQXid xid) throws XAException {
530 getBroker().startTransaction(client, xid);
531 }
532
533 /**
534 * Gets the prepared XA transactions.
535 *
536 * @param client
537 * @return
538 */
539 public ActiveMQXid[] getPreparedTransactions(BrokerClient client) throws XAException {
540 return getBroker().getPreparedTransactions(client);
541 }
542
543 /**
544 * Prepare an XA transaction.
545 *
546 * @param client
547 * @param xid
548 */
549 public int prepareTransaction(BrokerClient client, ActiveMQXid xid) throws XAException {
550 return getBroker().prepareTransaction(client, xid);
551 }
552
553 /**
554 * Rollback an XA transaction.
555 *
556 * @param client
557 * @param xid
558 */
559 public void rollbackTransaction(BrokerClient client, ActiveMQXid xid) throws XAException {
560 getBroker().rollbackTransaction(client, xid);
561 }
562
563 /**
564 * Commit an XA transaction.
565 *
566 * @param client
567 * @param xid
568 * @param onePhase
569 */
570 public void commitTransaction(BrokerClient client, ActiveMQXid xid, boolean onePhase) throws XAException {
571 getBroker().commitTransaction(client, xid, onePhase);
572 }
573
574
575 /**
576 * Update any message producers about our capacity to handle messages
577 *
578 * @param event
579 */
580 public void capacityChanged(CapacityMonitorEvent event) {
581 //only send to producers
582 for (Iterator i = producerInfos.values().iterator(); i.hasNext();) {
583 BrokerClient client = (BrokerClient) i.next();
584 client.updateBrokerCapacity(event.getCapacity());
585 }
586 }
587
588
589 // Properties
590 //-------------------------------------------------------------------------
591
592 public List getTransportConnectors() {
593 return transportConnectors;
594 }
595
596 public void setTransportConnectors(List transportConnectors) {
597 this.transportConnectors = new CopyOnWriteArrayList(transportConnectors);
598 }
599
600 public void addConnector(BrokerConnector connector) {
601 if( !transportConnectors.contains(connector) ) {
602 transportConnectors.add(connector);
603 context.registerConnector(connector.getServerChannel().getUrl(), connector);
604 }
605 }
606
607 public void removeConnector(BrokerConnector connector) {
608 transportConnectors.remove(connector);
609 context.deregisterConnector(connector.getServerChannel().getUrl());
610 }
611
612
613 public void addConnector(String bindAddress) throws JMSException {
614 addConnector(bindAddress, new DefaultWireFormat());
615 }
616
617 public void addConnector(String bindAddress, WireFormat wireFormat) throws JMSException {
618 addConnector(new BrokerConnectorImpl(this, bindAddress, wireFormat));
619 }
620
621 public void addConnector(TransportServerChannel transportConnector) {
622 addConnector(new BrokerConnectorImpl(this, transportConnector));
623 }
624
625 public List getNetworkConnectors() {
626 return networkConnectors;
627 }
628
629 public void setNetworkConnectors(List networkConnectors) {
630 this.networkConnectors = new CopyOnWriteArrayList(networkConnectors);
631 }
632
633 public NetworkConnector addNetworkConnector(String uri) throws JMSException {
634 NetworkConnector connector = addNetworkConnector();
635 connector.addNetworkChannel(uri);
636 return connector;
637 }
638
639 public NetworkConnector addNetworkConnector() {
640 NetworkConnector connector = new NetworkConnector(this);
641 addNetworkConnector(connector);
642 return connector;
643 }
644
645 public void addNetworkConnector(NetworkConnector connector) {
646 networkConnectors.add(connector);
647 }
648
649 public void removeNetworkConnector(NetworkConnector connector) {
650 networkConnectors.remove(connector);
651 }
652
653
654 public Broker getBroker() {
655 return broker;
656 }
657
658 public PersistenceAdapter getPersistenceAdapter() {
659 return broker != null ? broker.getPersistenceAdapter() : null;
660 }
661
662 public void setPersistenceAdapter(PersistenceAdapter persistenceAdapter) {
663 checkBrokerSet();
664 broker.setPersistenceAdapter(persistenceAdapter);
665 }
666
667 public DiscoveryAgent getDiscoveryAgent() {
668 return discoveryAgent;
669 }
670
671 public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) {
672 this.discoveryAgent = discoveryAgent;
673 }
674
675 public SecurityAdapter getSecurityAdapter() {
676 return broker != null ? broker.getSecurityAdapter() : null;
677 }
678
679 public void setSecurityAdapter(SecurityAdapter securityAdapter) {
680 checkBrokerSet();
681 broker.setSecurityAdapter(securityAdapter);
682 }
683
684 public RedeliveryPolicy getRedeliveryPolicy() {
685 return broker != null ? broker.getRedeliveryPolicy() : null;
686 }
687
688 public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
689 checkBrokerSet();
690 broker.setRedeliveryPolicy(redeliveryPolicy);
691 }
692
693 // Implementation methods
694 //-------------------------------------------------------------------------
695
696 protected void checkBrokerSet() {
697 if (broker == null) {
698 throw new IllegalStateException("Cannot set this property as we don't have a broker yet");
699 }
700 }
701
702 protected Map createLocalDiscoveryDetails() {
703 Map map = new HashMap();
704 map.put("brokerName", getLocalBrokerName());
705 map.put("connectURL", getLocalConnectionURL());
706 return map;
707 }
708
709 protected String getLocalBrokerName() {
710 return getBroker().getBrokerName();
711 }
712
713 protected String getLocalConnectionURL() {
714 StringBuffer buffer = new StringBuffer("reliable:");
715 List list = getTransportConnectors();
716 boolean first = true;
717 for (Iterator iter = list.iterator(); iter.hasNext();) {
718 BrokerConnector brokerConnector = (BrokerConnector) iter.next();
719 TransportServerChannel connector = brokerConnector.getServerChannel();
720 String url = connector.getUrl();
721 if (first) {
722 first = false;
723 }
724 else {
725 buffer.append(",");
726 }
727 buffer.append(url);
728 }
729 return buffer.toString();
730 }
731
732 protected void addShutdownHook() {
733 if(System.getProperty(DISABLE_CLEAN_SHUTDOWN_PROPERTY,"false").equals("true"))
734 return;
735
736 shutdownHook = new Thread("ActiveMQ ShutdownHook") {
737 public void run() {
738 containerShutdown();
739 }
740 };
741 Runtime.getRuntime().addShutdownHook(shutdownHook);
742 }
743
744 /**
745 * Causes a clean shutdown of the container when the VM is being shut down
746 */
747 protected void containerShutdown() {
748 try {
749 stop();
750 }
751 catch (JMSException e) {
752 Exception linkedException = e.getLinkedException();
753 if (linkedException != null) {
754 if (useLoggingForShutdownErrors) {
755 log.error("Failed to shut down: " + e + ". Reason: " + linkedException, linkedException);
756 }
757 else {
758 System.err.println("Failed to shut down: " + e + ". Reason: " + linkedException);
759 }
760 }
761 else {
762 if (useLoggingForShutdownErrors) {
763 log.error("Failed to shut down: " + e);
764 }
765 else {
766 System.err.println("Failed to shut down: " + e);
767 }
768 }
769 if (!useLoggingForShutdownErrors) {
770 e.printStackTrace(System.err);
771 }
772 }
773 catch (Exception e) {
774 if (useLoggingForShutdownErrors) {
775 log.error("Failed to shut down: " + e, e);
776 }
777 else {
778 System.err.println("Failed to shut down: " + e);
779 e.printStackTrace(System.err);
780 }
781 }
782 }
783 }