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 */ 017package org.apache.activemq.broker; 018 019import java.io.EOFException; 020import java.io.IOException; 021import java.net.SocketException; 022import java.net.URI; 023import java.util.Collection; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029import java.util.Properties; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.CopyOnWriteArrayList; 032import java.util.concurrent.CountDownLatch; 033import java.util.concurrent.TimeUnit; 034import java.util.concurrent.atomic.AtomicBoolean; 035import java.util.concurrent.atomic.AtomicInteger; 036import java.util.concurrent.atomic.AtomicReference; 037import java.util.concurrent.locks.ReentrantReadWriteLock; 038 039import javax.transaction.xa.XAResource; 040 041import org.apache.activemq.advisory.AdvisorySupport; 042import org.apache.activemq.broker.region.ConnectionStatistics; 043import org.apache.activemq.broker.region.RegionBroker; 044import org.apache.activemq.command.ActiveMQDestination; 045import org.apache.activemq.command.BrokerInfo; 046import org.apache.activemq.command.BrokerSubscriptionInfo; 047import org.apache.activemq.command.Command; 048import org.apache.activemq.command.CommandTypes; 049import org.apache.activemq.command.ConnectionControl; 050import org.apache.activemq.command.ConnectionError; 051import org.apache.activemq.command.ConnectionId; 052import org.apache.activemq.command.ConnectionInfo; 053import org.apache.activemq.command.ConsumerControl; 054import org.apache.activemq.command.ConsumerId; 055import org.apache.activemq.command.ConsumerInfo; 056import org.apache.activemq.command.ControlCommand; 057import org.apache.activemq.command.DataArrayResponse; 058import org.apache.activemq.command.DestinationInfo; 059import org.apache.activemq.command.ExceptionResponse; 060import org.apache.activemq.command.FlushCommand; 061import org.apache.activemq.command.IntegerResponse; 062import org.apache.activemq.command.KeepAliveInfo; 063import org.apache.activemq.command.Message; 064import org.apache.activemq.command.MessageAck; 065import org.apache.activemq.command.MessageDispatch; 066import org.apache.activemq.command.MessageDispatchNotification; 067import org.apache.activemq.command.MessagePull; 068import org.apache.activemq.command.ProducerAck; 069import org.apache.activemq.command.ProducerId; 070import org.apache.activemq.command.ProducerInfo; 071import org.apache.activemq.command.RemoveInfo; 072import org.apache.activemq.command.RemoveSubscriptionInfo; 073import org.apache.activemq.command.Response; 074import org.apache.activemq.command.SessionId; 075import org.apache.activemq.command.SessionInfo; 076import org.apache.activemq.command.ShutdownInfo; 077import org.apache.activemq.command.TransactionId; 078import org.apache.activemq.command.TransactionInfo; 079import org.apache.activemq.command.WireFormatInfo; 080import org.apache.activemq.network.DemandForwardingBridge; 081import org.apache.activemq.network.MBeanNetworkListener; 082import org.apache.activemq.network.NetworkBridgeConfiguration; 083import org.apache.activemq.network.NetworkBridgeFactory; 084import org.apache.activemq.network.NetworkConnector; 085import org.apache.activemq.security.MessageAuthorizationPolicy; 086import org.apache.activemq.state.CommandVisitor; 087import org.apache.activemq.state.ConnectionState; 088import org.apache.activemq.state.ConsumerState; 089import org.apache.activemq.state.ProducerState; 090import org.apache.activemq.state.SessionState; 091import org.apache.activemq.state.TransactionState; 092import org.apache.activemq.thread.Task; 093import org.apache.activemq.thread.TaskRunner; 094import org.apache.activemq.thread.TaskRunnerFactory; 095import org.apache.activemq.transaction.Transaction; 096import org.apache.activemq.transport.DefaultTransportListener; 097import org.apache.activemq.transport.ResponseCorrelator; 098import org.apache.activemq.transport.TransmitCallback; 099import org.apache.activemq.transport.Transport; 100import org.apache.activemq.transport.TransportDisposedIOException; 101import org.apache.activemq.util.IntrospectionSupport; 102import org.apache.activemq.util.MarshallingSupport; 103import org.apache.activemq.util.NetworkBridgeUtils; 104import org.apache.activemq.util.SubscriptionKey; 105import org.slf4j.Logger; 106import org.slf4j.LoggerFactory; 107import org.slf4j.MDC; 108 109public class TransportConnection implements Connection, Task, CommandVisitor { 110 private static final Logger LOG = LoggerFactory.getLogger(TransportConnection.class); 111 private static final Logger TRANSPORTLOG = LoggerFactory.getLogger(TransportConnection.class.getName() + ".Transport"); 112 private static final Logger SERVICELOG = LoggerFactory.getLogger(TransportConnection.class.getName() + ".Service"); 113 // Keeps track of the broker and connector that created this connection. 114 protected final Broker broker; 115 protected final BrokerService brokerService; 116 protected final TransportConnector connector; 117 // Keeps track of the state of the connections. 118 // protected final ConcurrentHashMap localConnectionStates=new 119 // ConcurrentHashMap(); 120 protected final Map<ConnectionId, ConnectionState> brokerConnectionStates; 121 // The broker and wireformat info that was exchanged. 122 protected BrokerInfo brokerInfo; 123 protected final List<Command> dispatchQueue = new LinkedList<>(); 124 protected TaskRunner taskRunner; 125 protected final AtomicReference<Throwable> transportException = new AtomicReference<>(); 126 protected AtomicBoolean dispatchStopped = new AtomicBoolean(false); 127 private final Transport transport; 128 private MessageAuthorizationPolicy messageAuthorizationPolicy; 129 private WireFormatInfo wireFormatInfo; 130 // Used to do async dispatch.. this should perhaps be pushed down into the 131 // transport layer.. 132 private boolean inServiceException; 133 private final ConnectionStatistics statistics = new ConnectionStatistics(); 134 private boolean manageable; 135 private boolean slow; 136 private boolean markedCandidate; 137 private boolean blockedCandidate; 138 private boolean blocked; 139 private boolean connected; 140 private boolean active; 141 142 // state management around pending stop 143 private static final int NEW = 0; 144 private static final int STARTING = 1; 145 private static final int STARTED = 2; 146 private static final int PENDING_STOP = 3; 147 private final AtomicInteger status = new AtomicInteger(NEW); 148 149 private long timeStamp; 150 private final AtomicBoolean stopping = new AtomicBoolean(false); 151 private final CountDownLatch stopped = new CountDownLatch(1); 152 private final AtomicBoolean asyncException = new AtomicBoolean(false); 153 private final Map<ProducerId, ProducerBrokerExchange> producerExchanges = new HashMap<>(); 154 private final Map<ConsumerId, ConsumerBrokerExchange> consumerExchanges = new HashMap<>(); 155 private final CountDownLatch dispatchStoppedLatch = new CountDownLatch(1); 156 private ConnectionContext context; 157 private boolean networkConnection; 158 private boolean faultTolerantConnection; 159 private final AtomicInteger protocolVersion = new AtomicInteger(CommandTypes.PROTOCOL_VERSION); 160 private DemandForwardingBridge duplexBridge; 161 private final TaskRunnerFactory taskRunnerFactory; 162 private final TaskRunnerFactory stopTaskRunnerFactory; 163 private TransportConnectionStateRegister connectionStateRegister = new SingleTransportConnectionStateRegister(); 164 private final ReentrantReadWriteLock serviceLock = new ReentrantReadWriteLock(); 165 private String duplexNetworkConnectorId; 166 167 /** 168 * @param taskRunnerFactory - can be null if you want direct dispatch to the transport 169 * else commands are sent async. 170 * @param stopTaskRunnerFactory - can <b>not</b> be null, used for stopping this connection. 171 */ 172 public TransportConnection(TransportConnector connector, final Transport transport, Broker broker, 173 TaskRunnerFactory taskRunnerFactory, TaskRunnerFactory stopTaskRunnerFactory) { 174 this.connector = connector; 175 this.broker = broker; 176 this.brokerService = broker.getBrokerService(); 177 178 RegionBroker rb = (RegionBroker) broker.getAdaptor(RegionBroker.class); 179 brokerConnectionStates = rb.getConnectionStates(); 180 if (connector != null) { 181 this.statistics.setParent(connector.getStatistics()); 182 this.messageAuthorizationPolicy = connector.getMessageAuthorizationPolicy(); 183 } 184 this.taskRunnerFactory = taskRunnerFactory; 185 this.stopTaskRunnerFactory = stopTaskRunnerFactory; 186 this.transport = transport; 187 if( this.transport instanceof BrokerServiceAware ) { 188 ((BrokerServiceAware)this.transport).setBrokerService(brokerService); 189 } 190 this.transport.setTransportListener(new DefaultTransportListener() { 191 @Override 192 public void onCommand(Object o) { 193 serviceLock.readLock().lock(); 194 try { 195 if (!(o instanceof Command)) { 196 throw new RuntimeException("Protocol violation - Command corrupted: " + o.toString()); 197 } 198 Command command = (Command) o; 199 if (!brokerService.isStopping()) { 200 Response response = service(command); 201 if (response != null && !brokerService.isStopping()) { 202 dispatchSync(response); 203 } 204 } else { 205 throw new BrokerStoppedException("Broker " + brokerService + " is being stopped"); 206 } 207 } finally { 208 serviceLock.readLock().unlock(); 209 } 210 } 211 212 @Override 213 public void onException(IOException exception) { 214 serviceLock.readLock().lock(); 215 try { 216 serviceTransportException(exception); 217 } finally { 218 serviceLock.readLock().unlock(); 219 } 220 } 221 }); 222 connected = true; 223 } 224 225 /** 226 * Returns the number of messages to be dispatched to this connection 227 * 228 * @return size of dispatch queue 229 */ 230 @Override 231 public int getDispatchQueueSize() { 232 synchronized (dispatchQueue) { 233 return dispatchQueue.size(); 234 } 235 } 236 237 public void serviceTransportException(IOException e) { 238 if (!stopping.get() && status.get() != PENDING_STOP) { 239 transportException.set(e); 240 if (TRANSPORTLOG.isDebugEnabled()) { 241 TRANSPORTLOG.debug(this + " failed: " + e, e); 242 } else if (TRANSPORTLOG.isWarnEnabled() && !suppressed(e)) { 243 TRANSPORTLOG.warn(this + " failed: " + e); 244 } 245 stopAsync(e); 246 } 247 } 248 249 private boolean suppressed(IOException e) { 250 return !connector.isWarnOnRemoteClose() && ((e instanceof SocketException && e.getMessage().indexOf("reset") != -1) || e instanceof EOFException); 251 } 252 253 /** 254 * Calls the serviceException method in an async thread. Since handling a 255 * service exception closes a socket, we should not tie up broker threads 256 * since client sockets may hang or cause deadlocks. 257 */ 258 @Override 259 public void serviceExceptionAsync(final IOException e) { 260 if (asyncException.compareAndSet(false, true)) { 261 new Thread("Async Exception Handler") { 262 @Override 263 public void run() { 264 serviceException(e); 265 } 266 }.start(); 267 } 268 } 269 270 /** 271 * Closes a clients connection due to a detected error. Errors are ignored 272 * if: the client is closing or broker is closing. Otherwise, the connection 273 * error transmitted to the client before stopping it's transport. 274 */ 275 @Override 276 public void serviceException(Throwable e) { 277 // are we a transport exception such as not being able to dispatch 278 // synchronously to a transport 279 if (e instanceof IOException) { 280 serviceTransportException((IOException) e); 281 } else if (e.getClass() == BrokerStoppedException.class) { 282 // Handle the case where the broker is stopped 283 // But the client is still connected. 284 if (!stopping.get()) { 285 SERVICELOG.debug("Broker has been stopped. Notifying client and closing his connection."); 286 ConnectionError ce = new ConnectionError(); 287 ce.setException(e); 288 dispatchSync(ce); 289 // Record the error that caused the transport to stop 290 transportException.set(e); 291 // Wait a little bit to try to get the output buffer to flush 292 // the exception notification to the client. 293 try { 294 Thread.sleep(500); 295 } catch (InterruptedException ie) { 296 Thread.currentThread().interrupt(); 297 } 298 // Worst case is we just kill the connection before the 299 // notification gets to him. 300 stopAsync(); 301 } 302 } else if (!stopping.get() && !inServiceException) { 303 inServiceException = true; 304 try { 305 if (SERVICELOG.isDebugEnabled()) { 306 SERVICELOG.debug("Async error occurred: " + e, e); 307 } else { 308 SERVICELOG.warn("Async error occurred: " + e); 309 } 310 ConnectionError ce = new ConnectionError(); 311 ce.setException(e); 312 if (status.get() == PENDING_STOP) { 313 dispatchSync(ce); 314 } else { 315 dispatchAsync(ce); 316 } 317 } finally { 318 inServiceException = false; 319 } 320 } 321 } 322 323 @Override 324 public Response service(Command command) { 325 MDC.put("activemq.connector", connector.getUri().toString()); 326 Response response = null; 327 boolean responseRequired = command.isResponseRequired(); 328 int commandId = command.getCommandId(); 329 try { 330 if (status.get() != PENDING_STOP) { 331 response = command.visit(this); 332 } else { 333 response = new ExceptionResponse(transportException.get()); 334 } 335 } catch (Throwable e) { 336 if (SERVICELOG.isDebugEnabled() && e.getClass() != BrokerStoppedException.class) { 337 SERVICELOG.debug("Error occured while processing " + (responseRequired ? "sync" : "async") 338 + " command: " + command + ", exception: " + e, e); 339 } 340 341 if (e instanceof SuppressReplyException || (e.getCause() instanceof SuppressReplyException)) { 342 LOG.info("Suppressing reply to: " + command + " on: " + e + ", cause: " + e.getCause()); 343 responseRequired = false; 344 } 345 346 if (responseRequired) { 347 if (e instanceof SecurityException || e.getCause() instanceof SecurityException) { 348 SERVICELOG.warn("Security Error occurred on connection to: {}, {}", 349 transport.getRemoteAddress(), e.getMessage()); 350 } 351 response = new ExceptionResponse(e); 352 } else { 353 forceRollbackOnlyOnFailedAsyncTransactionOp(e, command); 354 serviceException(e); 355 } 356 } 357 if (responseRequired) { 358 if (response == null) { 359 response = new Response(); 360 } 361 response.setCorrelationId(commandId); 362 } 363 // The context may have been flagged so that the response is not 364 // sent. 365 if (context != null) { 366 if (context.isDontSendReponse()) { 367 context.setDontSendReponse(false); 368 response = null; 369 } 370 context = null; 371 } 372 MDC.remove("activemq.connector"); 373 return response; 374 } 375 376 private void forceRollbackOnlyOnFailedAsyncTransactionOp(Throwable e, Command command) { 377 if (brokerService.isRollbackOnlyOnAsyncException() && !(e instanceof IOException) && isInTransaction(command)) { 378 Transaction transaction = getActiveTransaction(command); 379 if (transaction != null && !transaction.isRollbackOnly()) { 380 LOG.debug("on async exception, force rollback of transaction for: " + command, e); 381 transaction.setRollbackOnly(e); 382 } 383 } 384 } 385 386 private Transaction getActiveTransaction(Command command) { 387 Transaction transaction = null; 388 try { 389 if (command instanceof Message) { 390 Message messageSend = (Message) command; 391 ProducerId producerId = messageSend.getProducerId(); 392 ProducerBrokerExchange producerExchange = getProducerBrokerExchange(producerId); 393 transaction = producerExchange.getConnectionContext().getTransactions().get(messageSend.getTransactionId()); 394 } else if (command instanceof MessageAck) { 395 MessageAck messageAck = (MessageAck) command; 396 ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(messageAck.getConsumerId()); 397 if (consumerExchange != null) { 398 transaction = consumerExchange.getConnectionContext().getTransactions().get(messageAck.getTransactionId()); 399 } 400 } 401 } catch(Exception ignored){ 402 LOG.trace("failed to find active transaction for command: " + command, ignored); 403 } 404 return transaction; 405 } 406 407 private boolean isInTransaction(Command command) { 408 return command instanceof Message && ((Message)command).isInTransaction() 409 || command instanceof MessageAck && ((MessageAck)command).isInTransaction(); 410 } 411 412 @Override 413 public Response processKeepAlive(KeepAliveInfo info) throws Exception { 414 return null; 415 } 416 417 @Override 418 public Response processRemoveSubscription(RemoveSubscriptionInfo info) throws Exception { 419 broker.removeSubscription(lookupConnectionState(info.getConnectionId()).getContext(), info); 420 return null; 421 } 422 423 @Override 424 public Response processWireFormat(WireFormatInfo info) throws Exception { 425 wireFormatInfo = info; 426 protocolVersion.set(info.getVersion()); 427 return null; 428 } 429 430 @Override 431 public Response processShutdown(ShutdownInfo info) throws Exception { 432 stopAsync(); 433 return null; 434 } 435 436 @Override 437 public Response processFlush(FlushCommand command) throws Exception { 438 return null; 439 } 440 441 @Override 442 public Response processBeginTransaction(TransactionInfo info) throws Exception { 443 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 444 context = null; 445 if (cs != null) { 446 context = cs.getContext(); 447 } 448 if (cs == null) { 449 throw new NullPointerException("Context is null"); 450 } 451 // Avoid replaying dup commands 452 if (cs.getTransactionState(info.getTransactionId()) == null) { 453 cs.addTransactionState(info.getTransactionId()); 454 broker.beginTransaction(context, info.getTransactionId()); 455 } 456 return null; 457 } 458 459 @Override 460 public int getActiveTransactionCount() { 461 int rc = 0; 462 for (TransportConnectionState cs : connectionStateRegister.listConnectionStates()) { 463 rc += cs.getTransactionStates().size(); 464 } 465 return rc; 466 } 467 468 @Override 469 public Long getOldestActiveTransactionDuration() { 470 TransactionState oldestTX = null; 471 for (TransportConnectionState cs : connectionStateRegister.listConnectionStates()) { 472 Collection<TransactionState> transactions = cs.getTransactionStates(); 473 for (TransactionState transaction : transactions) { 474 if( oldestTX ==null || oldestTX.getCreatedAt() < transaction.getCreatedAt() ) { 475 oldestTX = transaction; 476 } 477 } 478 } 479 if( oldestTX == null ) { 480 return null; 481 } 482 return System.currentTimeMillis() - oldestTX.getCreatedAt(); 483 } 484 485 @Override 486 public Response processEndTransaction(TransactionInfo info) throws Exception { 487 // No need to do anything. This packet is just sent by the client 488 // make sure he is synced with the server as commit command could 489 // come from a different connection. 490 return null; 491 } 492 493 @Override 494 public Response processPrepareTransaction(TransactionInfo info) throws Exception { 495 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 496 context = null; 497 if (cs != null) { 498 context = cs.getContext(); 499 } 500 if (cs == null) { 501 throw new NullPointerException("Context is null"); 502 } 503 TransactionState transactionState = cs.getTransactionState(info.getTransactionId()); 504 if (transactionState == null) { 505 throw new IllegalStateException("Cannot prepare a transaction that had not been started or previously returned XA_RDONLY: " 506 + info.getTransactionId()); 507 } 508 // Avoid dups. 509 if (!transactionState.isPrepared()) { 510 transactionState.setPrepared(true); 511 int result = broker.prepareTransaction(context, info.getTransactionId()); 512 transactionState.setPreparedResult(result); 513 if (result == XAResource.XA_RDONLY) { 514 // we are done, no further rollback or commit from TM 515 cs.removeTransactionState(info.getTransactionId()); 516 } 517 IntegerResponse response = new IntegerResponse(result); 518 return response; 519 } else { 520 IntegerResponse response = new IntegerResponse(transactionState.getPreparedResult()); 521 return response; 522 } 523 } 524 525 @Override 526 public Response processCommitTransactionOnePhase(TransactionInfo info) throws Exception { 527 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 528 context = cs.getContext(); 529 cs.removeTransactionState(info.getTransactionId()); 530 broker.commitTransaction(context, info.getTransactionId(), true); 531 return null; 532 } 533 534 @Override 535 public Response processCommitTransactionTwoPhase(TransactionInfo info) throws Exception { 536 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 537 context = cs.getContext(); 538 cs.removeTransactionState(info.getTransactionId()); 539 broker.commitTransaction(context, info.getTransactionId(), false); 540 return null; 541 } 542 543 @Override 544 public Response processRollbackTransaction(TransactionInfo info) throws Exception { 545 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 546 context = cs.getContext(); 547 cs.removeTransactionState(info.getTransactionId()); 548 broker.rollbackTransaction(context, info.getTransactionId()); 549 return null; 550 } 551 552 @Override 553 public Response processForgetTransaction(TransactionInfo info) throws Exception { 554 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 555 context = cs.getContext(); 556 broker.forgetTransaction(context, info.getTransactionId()); 557 return null; 558 } 559 560 @Override 561 public Response processRecoverTransactions(TransactionInfo info) throws Exception { 562 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 563 context = cs.getContext(); 564 TransactionId[] preparedTransactions = broker.getPreparedTransactions(context); 565 return new DataArrayResponse(preparedTransactions); 566 } 567 568 @Override 569 public Response processMessage(Message messageSend) throws Exception { 570 ProducerId producerId = messageSend.getProducerId(); 571 ProducerBrokerExchange producerExchange = getProducerBrokerExchange(producerId); 572 if (producerExchange.canDispatch(messageSend)) { 573 broker.send(producerExchange, messageSend); 574 } 575 return null; 576 } 577 578 @Override 579 public Response processMessageAck(MessageAck ack) throws Exception { 580 ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(ack.getConsumerId()); 581 if (consumerExchange != null) { 582 broker.acknowledge(consumerExchange, ack); 583 } else if (ack.isInTransaction()) { 584 LOG.warn("no matching consumer {}, ignoring ack {}", consumerExchange, ack); 585 } 586 return null; 587 } 588 589 @Override 590 public Response processMessagePull(MessagePull pull) throws Exception { 591 return broker.messagePull(lookupConnectionState(pull.getConsumerId()).getContext(), pull); 592 } 593 594 @Override 595 public Response processMessageDispatchNotification(MessageDispatchNotification notification) throws Exception { 596 broker.processDispatchNotification(notification); 597 return null; 598 } 599 600 @Override 601 public Response processAddDestination(DestinationInfo info) throws Exception { 602 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 603 broker.addDestinationInfo(cs.getContext(), info); 604 if (info.getDestination().isTemporary()) { 605 cs.addTempDestination(info); 606 } 607 return null; 608 } 609 610 @Override 611 public Response processRemoveDestination(DestinationInfo info) throws Exception { 612 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 613 broker.removeDestinationInfo(cs.getContext(), info); 614 if (info.getDestination().isTemporary()) { 615 cs.removeTempDestination(info.getDestination()); 616 } 617 return null; 618 } 619 620 @Override 621 public Response processAddProducer(ProducerInfo info) throws Exception { 622 SessionId sessionId = info.getProducerId().getParentId(); 623 ConnectionId connectionId = sessionId.getParentId(); 624 TransportConnectionState cs = lookupConnectionState(connectionId); 625 if (cs == null) { 626 throw new IllegalStateException("Cannot add a producer to a connection that had not been registered: " 627 + connectionId); 628 } 629 SessionState ss = cs.getSessionState(sessionId); 630 if (ss == null) { 631 throw new IllegalStateException("Cannot add a producer to a session that had not been registered: " 632 + sessionId); 633 } 634 // Avoid replaying dup commands 635 if (!ss.getProducerIds().contains(info.getProducerId())) { 636 ActiveMQDestination destination = info.getDestination(); 637 // Do not check for null here as it would cause the count of max producers to exclude 638 // anonymous producers. The isAdvisoryTopic method checks for null so it is safe to 639 // call it from here with a null Destination value. 640 if (!AdvisorySupport.isAdvisoryTopic(destination)) { 641 if (getProducerCount(connectionId) >= connector.getMaximumProducersAllowedPerConnection()){ 642 throw new IllegalStateException("Can't add producer on connection " + connectionId + ": at maximum limit: " + connector.getMaximumProducersAllowedPerConnection()); 643 } 644 } 645 broker.addProducer(cs.getContext(), info); 646 try { 647 ss.addProducer(info); 648 } catch (IllegalStateException e) { 649 broker.removeProducer(cs.getContext(), info); 650 } 651 652 } 653 return null; 654 } 655 656 @Override 657 public Response processRemoveProducer(ProducerId id) throws Exception { 658 SessionId sessionId = id.getParentId(); 659 ConnectionId connectionId = sessionId.getParentId(); 660 TransportConnectionState cs = lookupConnectionState(connectionId); 661 SessionState ss = cs.getSessionState(sessionId); 662 if (ss == null) { 663 throw new IllegalStateException("Cannot remove a producer from a session that had not been registered: " 664 + sessionId); 665 } 666 ProducerState ps = ss.removeProducer(id); 667 if (ps == null) { 668 throw new IllegalStateException("Cannot remove a producer that had not been registered: " + id); 669 } 670 removeProducerBrokerExchange(id); 671 broker.removeProducer(cs.getContext(), ps.getInfo()); 672 return null; 673 } 674 675 @Override 676 public Response processAddConsumer(ConsumerInfo info) throws Exception { 677 SessionId sessionId = info.getConsumerId().getParentId(); 678 ConnectionId connectionId = sessionId.getParentId(); 679 TransportConnectionState cs = lookupConnectionState(connectionId); 680 if (cs == null) { 681 throw new IllegalStateException("Cannot add a consumer to a connection that had not been registered: " 682 + connectionId); 683 } 684 SessionState ss = cs.getSessionState(sessionId); 685 if (ss == null) { 686 throw new IllegalStateException(broker.getBrokerName() 687 + " Cannot add a consumer to a session that had not been registered: " + sessionId); 688 } 689 // Avoid replaying dup commands 690 if (!ss.getConsumerIds().contains(info.getConsumerId())) { 691 ActiveMQDestination destination = info.getDestination(); 692 if (destination != null && !AdvisorySupport.isAdvisoryTopic(destination)) { 693 if (getConsumerCount(connectionId) >= connector.getMaximumConsumersAllowedPerConnection()){ 694 throw new IllegalStateException("Can't add consumer on connection " + connectionId + ": at maximum limit: " + connector.getMaximumConsumersAllowedPerConnection()); 695 } 696 } 697 698 broker.addConsumer(cs.getContext(), info); 699 try { 700 ss.addConsumer(info); 701 addConsumerBrokerExchange(cs, info.getConsumerId()); 702 } catch (IllegalStateException e) { 703 broker.removeConsumer(cs.getContext(), info); 704 } 705 706 } 707 return null; 708 } 709 710 @Override 711 public Response processRemoveConsumer(ConsumerId id, long lastDeliveredSequenceId) throws Exception { 712 SessionId sessionId = id.getParentId(); 713 ConnectionId connectionId = sessionId.getParentId(); 714 TransportConnectionState cs = lookupConnectionState(connectionId); 715 if (cs == null) { 716 throw new IllegalStateException("Cannot remove a consumer from a connection that had not been registered: " 717 + connectionId); 718 } 719 SessionState ss = cs.getSessionState(sessionId); 720 if (ss == null) { 721 throw new IllegalStateException("Cannot remove a consumer from a session that had not been registered: " 722 + sessionId); 723 } 724 ConsumerState consumerState = ss.removeConsumer(id); 725 if (consumerState == null) { 726 throw new IllegalStateException("Cannot remove a consumer that had not been registered: " + id); 727 } 728 ConsumerInfo info = consumerState.getInfo(); 729 info.setLastDeliveredSequenceId(lastDeliveredSequenceId); 730 broker.removeConsumer(cs.getContext(), consumerState.getInfo()); 731 removeConsumerBrokerExchange(id); 732 return null; 733 } 734 735 @Override 736 public Response processAddSession(SessionInfo info) throws Exception { 737 ConnectionId connectionId = info.getSessionId().getParentId(); 738 TransportConnectionState cs = lookupConnectionState(connectionId); 739 // Avoid replaying dup commands 740 if (cs != null && !cs.getSessionIds().contains(info.getSessionId())) { 741 broker.addSession(cs.getContext(), info); 742 try { 743 cs.addSession(info); 744 } catch (IllegalStateException e) { 745 LOG.warn("Failed to add session: {}", info.getSessionId(), e); 746 broker.removeSession(cs.getContext(), info); 747 } 748 } 749 return null; 750 } 751 752 @Override 753 public Response processRemoveSession(SessionId id, long lastDeliveredSequenceId) throws Exception { 754 ConnectionId connectionId = id.getParentId(); 755 TransportConnectionState cs = lookupConnectionState(connectionId); 756 if (cs == null) { 757 throw new IllegalStateException("Cannot remove session from connection that had not been registered: " + connectionId); 758 } 759 SessionState session = cs.getSessionState(id); 760 if (session == null) { 761 throw new IllegalStateException("Cannot remove session that had not been registered: " + id); 762 } 763 // Don't let new consumers or producers get added while we are closing 764 // this down. 765 session.shutdown(); 766 // Cascade the connection stop to the consumers and producers. 767 for (ConsumerId consumerId : session.getConsumerIds()) { 768 try { 769 processRemoveConsumer(consumerId, lastDeliveredSequenceId); 770 } catch (Throwable e) { 771 LOG.warn("Failed to remove consumer: {}", consumerId, e); 772 } 773 } 774 for (ProducerId producerId : session.getProducerIds()) { 775 try { 776 processRemoveProducer(producerId); 777 } catch (Throwable e) { 778 LOG.warn("Failed to remove producer: {}", producerId, e); 779 } 780 } 781 cs.removeSession(id); 782 broker.removeSession(cs.getContext(), session.getInfo()); 783 return null; 784 } 785 786 @Override 787 public Response processAddConnection(ConnectionInfo info) throws Exception { 788 // Older clients should have been defaulting this field to true.. but 789 // they were not. 790 if (wireFormatInfo != null && wireFormatInfo.getVersion() <= 2) { 791 info.setClientMaster(true); 792 } 793 TransportConnectionState state; 794 // Make sure 2 concurrent connections by the same ID only generate 1 795 // TransportConnectionState object. 796 synchronized (brokerConnectionStates) { 797 state = (TransportConnectionState) brokerConnectionStates.get(info.getConnectionId()); 798 if (state == null) { 799 state = new TransportConnectionState(info, this); 800 brokerConnectionStates.put(info.getConnectionId(), state); 801 } 802 state.incrementReference(); 803 } 804 // If there are 2 concurrent connections for the same connection id, 805 // then last one in wins, we need to sync here 806 // to figure out the winner. 807 synchronized (state.getConnectionMutex()) { 808 if (state.getConnection() != this) { 809 LOG.debug("Killing previous stale connection: {}", state.getConnection().getRemoteAddress()); 810 state.getConnection().stop(); 811 LOG.debug("Connection {} taking over previous connection: {}", getRemoteAddress(), state.getConnection().getRemoteAddress()); 812 state.setConnection(this); 813 state.reset(info); 814 } 815 } 816 registerConnectionState(info.getConnectionId(), state); 817 LOG.debug("Setting up new connection id: {}, address: {}, info: {}", new Object[]{ info.getConnectionId(), getRemoteAddress(), info }); 818 this.faultTolerantConnection = info.isFaultTolerant(); 819 // Setup the context. 820 String clientId = info.getClientId(); 821 context = new ConnectionContext(); 822 context.setBroker(broker); 823 context.setClientId(clientId); 824 context.setClientMaster(info.isClientMaster()); 825 context.setConnection(this); 826 context.setConnectionId(info.getConnectionId()); 827 context.setConnector(connector); 828 context.setMessageAuthorizationPolicy(getMessageAuthorizationPolicy()); 829 context.setNetworkConnection(networkConnection); 830 context.setFaultTolerant(faultTolerantConnection); 831 context.setTransactions(new ConcurrentHashMap<TransactionId, Transaction>()); 832 context.setUserName(info.getUserName()); 833 context.setWireFormatInfo(wireFormatInfo); 834 context.setReconnect(info.isFailoverReconnect()); 835 this.manageable = info.isManageable(); 836 context.setConnectionState(state); 837 state.setContext(context); 838 state.setConnection(this); 839 if (info.getClientIp() == null) { 840 info.setClientIp(getRemoteAddress()); 841 } 842 843 try { 844 broker.addConnection(context, info); 845 } catch (Exception e) { 846 synchronized (brokerConnectionStates) { 847 brokerConnectionStates.remove(info.getConnectionId()); 848 } 849 unregisterConnectionState(info.getConnectionId()); 850 LOG.warn("Failed to add Connection id={}, clientId={}, clientIP={} due to {}", info.getConnectionId(), clientId, info.getClientIp(), e.getLocalizedMessage()); 851 //AMQ-6561 - stop for all exceptions on addConnection 852 // close this down - in case the peer of this transport doesn't play nice 853 delayedStop(2000, "Failed with SecurityException: " + e.getLocalizedMessage(), e); 854 throw e; 855 } 856 if (info.isManageable()) { 857 // send ConnectionCommand 858 ConnectionControl command = this.connector.getConnectionControl(); 859 command.setFaultTolerant(broker.isFaultTolerantConfiguration()); 860 if (info.isFailoverReconnect()) { 861 command.setRebalanceConnection(false); 862 } 863 dispatchAsync(command); 864 } 865 return null; 866 } 867 868 @Override 869 public synchronized Response processRemoveConnection(ConnectionId id, long lastDeliveredSequenceId) 870 throws InterruptedException { 871 LOG.debug("remove connection id: {}", id); 872 TransportConnectionState cs = lookupConnectionState(id); 873 if (cs != null) { 874 // Don't allow things to be added to the connection state while we 875 // are shutting down. 876 cs.shutdown(); 877 // Cascade the connection stop to the sessions. 878 for (SessionId sessionId : cs.getSessionIds()) { 879 try { 880 processRemoveSession(sessionId, lastDeliveredSequenceId); 881 } catch (Throwable e) { 882 SERVICELOG.warn("Failed to remove session {}", sessionId, e); 883 } 884 } 885 // Cascade the connection stop to temp destinations. 886 for (Iterator<DestinationInfo> iter = cs.getTempDestinations().iterator(); iter.hasNext(); ) { 887 DestinationInfo di = iter.next(); 888 try { 889 broker.removeDestination(cs.getContext(), di.getDestination(), 0); 890 } catch (Throwable e) { 891 SERVICELOG.warn("Failed to remove tmp destination {}", di.getDestination(), e); 892 } 893 iter.remove(); 894 } 895 try { 896 broker.removeConnection(cs.getContext(), cs.getInfo(), transportException.get()); 897 } catch (Throwable e) { 898 SERVICELOG.warn("Failed to remove connection {}", cs.getInfo(), e); 899 } 900 TransportConnectionState state = unregisterConnectionState(id); 901 if (state != null) { 902 synchronized (brokerConnectionStates) { 903 // If we are the last reference, we should remove the state 904 // from the broker. 905 if (state.decrementReference() == 0) { 906 brokerConnectionStates.remove(id); 907 } 908 } 909 } 910 } 911 return null; 912 } 913 914 @Override 915 public Response processProducerAck(ProducerAck ack) throws Exception { 916 // A broker should not get ProducerAck messages. 917 return null; 918 } 919 920 @Override 921 public Connector getConnector() { 922 return connector; 923 } 924 925 @Override 926 public void dispatchSync(Command message) { 927 try { 928 processDispatch(message); 929 } catch (IOException e) { 930 serviceExceptionAsync(e); 931 } 932 } 933 934 @Override 935 public void dispatchAsync(Command message) { 936 if (!stopping.get()) { 937 if (taskRunner == null) { 938 dispatchSync(message); 939 } else { 940 synchronized (dispatchQueue) { 941 dispatchQueue.add(message); 942 } 943 try { 944 taskRunner.wakeup(); 945 } catch (InterruptedException e) { 946 Thread.currentThread().interrupt(); 947 } 948 } 949 } else { 950 if (message.isMessageDispatch()) { 951 MessageDispatch md = (MessageDispatch) message; 952 TransmitCallback sub = md.getTransmitCallback(); 953 broker.postProcessDispatch(md); 954 if (sub != null) { 955 sub.onFailure(); 956 } 957 } 958 } 959 } 960 961 protected void processDispatch(Command command) throws IOException { 962 MessageDispatch messageDispatch = (MessageDispatch) (command.isMessageDispatch() ? command : null); 963 try { 964 if (!stopping.get()) { 965 if (messageDispatch != null) { 966 try { 967 broker.preProcessDispatch(messageDispatch); 968 } catch (RuntimeException convertToIO) { 969 throw new IOException(convertToIO); 970 } 971 } 972 dispatch(command); 973 } 974 } catch (IOException e) { 975 if (messageDispatch != null) { 976 TransmitCallback sub = messageDispatch.getTransmitCallback(); 977 broker.postProcessDispatch(messageDispatch); 978 if (sub != null) { 979 sub.onFailure(); 980 } 981 messageDispatch = null; 982 throw e; 983 } else { 984 if (TRANSPORTLOG.isDebugEnabled()) { 985 TRANSPORTLOG.debug("Unexpected exception on asyncDispatch, command of type: " + command.getDataStructureType(), e); 986 } 987 } 988 } finally { 989 if (messageDispatch != null) { 990 TransmitCallback sub = messageDispatch.getTransmitCallback(); 991 broker.postProcessDispatch(messageDispatch); 992 if (sub != null) { 993 sub.onSuccess(); 994 } 995 } 996 } 997 } 998 999 @Override 1000 public boolean iterate() { 1001 try { 1002 if (status.get() == PENDING_STOP || stopping.get()) { 1003 if (dispatchStopped.compareAndSet(false, true)) { 1004 if (transportException.get() == null) { 1005 try { 1006 dispatch(new ShutdownInfo()); 1007 } catch (Throwable ignore) { 1008 } 1009 } 1010 dispatchStoppedLatch.countDown(); 1011 } 1012 return false; 1013 } 1014 if (!dispatchStopped.get()) { 1015 Command command = null; 1016 synchronized (dispatchQueue) { 1017 if (dispatchQueue.isEmpty()) { 1018 return false; 1019 } 1020 command = dispatchQueue.remove(0); 1021 } 1022 processDispatch(command); 1023 return true; 1024 } 1025 return false; 1026 } catch (IOException e) { 1027 if (dispatchStopped.compareAndSet(false, true)) { 1028 dispatchStoppedLatch.countDown(); 1029 } 1030 serviceExceptionAsync(e); 1031 return false; 1032 } 1033 } 1034 1035 /** 1036 * Returns the statistics for this connection 1037 */ 1038 @Override 1039 public ConnectionStatistics getStatistics() { 1040 return statistics; 1041 } 1042 1043 public MessageAuthorizationPolicy getMessageAuthorizationPolicy() { 1044 return messageAuthorizationPolicy; 1045 } 1046 1047 public void setMessageAuthorizationPolicy(MessageAuthorizationPolicy messageAuthorizationPolicy) { 1048 this.messageAuthorizationPolicy = messageAuthorizationPolicy; 1049 } 1050 1051 @Override 1052 public boolean isManageable() { 1053 return manageable; 1054 } 1055 1056 @Override 1057 public void start() throws Exception { 1058 if (status.compareAndSet(NEW, STARTING)) { 1059 try { 1060 synchronized (this) { 1061 if (taskRunnerFactory != null) { 1062 taskRunner = taskRunnerFactory.createTaskRunner(this, "ActiveMQ Connection Dispatcher: " 1063 + getRemoteAddress()); 1064 } else { 1065 taskRunner = null; 1066 } 1067 transport.start(); 1068 active = true; 1069 BrokerInfo info = connector.getBrokerInfo().copy(); 1070 if (connector.isUpdateClusterClients()) { 1071 info.setPeerBrokerInfos(this.broker.getPeerBrokerInfos()); 1072 } else { 1073 info.setPeerBrokerInfos(null); 1074 } 1075 dispatchAsync(info); 1076 1077 connector.onStarted(this); 1078 } 1079 } catch (Exception e) { 1080 // Force clean up on an error starting up. 1081 status.set(PENDING_STOP); 1082 throw e; 1083 } finally { 1084 // stop() can be called from within the above block, 1085 // but we want to be sure start() completes before 1086 // stop() runs, so queue the stop until right now: 1087 if (!status.compareAndSet(STARTING, STARTED)) { 1088 LOG.debug("Calling the delayed stop() after start() {}", this); 1089 stop(); 1090 } 1091 } 1092 } 1093 } 1094 1095 @Override 1096 public void stop() throws Exception { 1097 // do not stop task the task runner factories (taskRunnerFactory, stopTaskRunnerFactory) 1098 // as their lifecycle is handled elsewhere 1099 1100 stopAsync(); 1101 while (!stopped.await(5, TimeUnit.SECONDS)) { 1102 LOG.info("The connection to '{}' is taking a long time to shutdown.", transport.getRemoteAddress()); 1103 } 1104 } 1105 1106 public void delayedStop(final int waitTime, final String reason, Throwable cause) { 1107 if (waitTime > 0) { 1108 status.compareAndSet(STARTING, PENDING_STOP); 1109 transportException.set(cause); 1110 try { 1111 stopTaskRunnerFactory.execute(new Runnable() { 1112 @Override 1113 public void run() { 1114 try { 1115 Thread.sleep(waitTime); 1116 stopAsync(); 1117 LOG.info("Stopping {} because {}", transport.getRemoteAddress(), reason); 1118 } catch (InterruptedException e) { 1119 } 1120 } 1121 }); 1122 } catch (Throwable t) { 1123 LOG.warn("Cannot create stopAsync. This exception will be ignored.", t); 1124 } 1125 } 1126 } 1127 1128 public void stopAsync(Throwable cause) { 1129 transportException.set(cause); 1130 stopAsync(); 1131 } 1132 1133 public void stopAsync() { 1134 // If we're in the middle of starting then go no further... for now. 1135 if (status.compareAndSet(STARTING, PENDING_STOP)) { 1136 LOG.debug("stopAsync() called in the middle of start(). Delaying till start completes.."); 1137 return; 1138 } 1139 if (stopping.compareAndSet(false, true)) { 1140 // Let all the connection contexts know we are shutting down 1141 // so that in progress operations can notice and unblock. 1142 List<TransportConnectionState> connectionStates = listConnectionStates(); 1143 for (TransportConnectionState cs : connectionStates) { 1144 ConnectionContext connectionContext = cs.getContext(); 1145 if (connectionContext != null) { 1146 connectionContext.getStopping().set(true); 1147 } 1148 } 1149 try { 1150 stopTaskRunnerFactory.execute(new Runnable() { 1151 @Override 1152 public void run() { 1153 serviceLock.writeLock().lock(); 1154 try { 1155 doStop(); 1156 } catch (Throwable e) { 1157 LOG.debug("Error occurred while shutting down a connection {}", this, e); 1158 } finally { 1159 stopped.countDown(); 1160 serviceLock.writeLock().unlock(); 1161 } 1162 } 1163 }); 1164 } catch (Throwable t) { 1165 LOG.warn("Cannot create async transport stopper thread. This exception is ignored. Not waiting for stop to complete", t); 1166 stopped.countDown(); 1167 } 1168 } 1169 } 1170 1171 @Override 1172 public String toString() { 1173 return "Transport Connection to: " + transport.getRemoteAddress(); 1174 } 1175 1176 protected void doStop() throws Exception { 1177 LOG.debug("Stopping connection: {}", transport.getRemoteAddress()); 1178 connector.onStopped(this); 1179 try { 1180 synchronized (this) { 1181 if (duplexBridge != null) { 1182 duplexBridge.stop(); 1183 } 1184 } 1185 } catch (Exception ignore) { 1186 LOG.trace("Exception caught stopping. This exception is ignored.", ignore); 1187 } 1188 try { 1189 transport.stop(); 1190 LOG.debug("Stopped transport: {}", transport.getRemoteAddress()); 1191 } catch (Exception e) { 1192 LOG.debug("Could not stop transport to {}. This exception is ignored.", transport.getRemoteAddress(), e); 1193 } 1194 if (taskRunner != null) { 1195 taskRunner.shutdown(1); 1196 taskRunner = null; 1197 } 1198 active = false; 1199 // Run the MessageDispatch callbacks so that message references get 1200 // cleaned up. 1201 synchronized (dispatchQueue) { 1202 for (Iterator<Command> iter = dispatchQueue.iterator(); iter.hasNext(); ) { 1203 Command command = iter.next(); 1204 if (command.isMessageDispatch()) { 1205 MessageDispatch md = (MessageDispatch) command; 1206 TransmitCallback sub = md.getTransmitCallback(); 1207 broker.postProcessDispatch(md); 1208 if (sub != null) { 1209 sub.onFailure(); 1210 } 1211 } 1212 } 1213 dispatchQueue.clear(); 1214 } 1215 // 1216 // Remove all logical connection associated with this connection 1217 // from the broker. 1218 if (!broker.isStopped()) { 1219 List<TransportConnectionState> connectionStates = listConnectionStates(); 1220 connectionStates = listConnectionStates(); 1221 for (TransportConnectionState cs : connectionStates) { 1222 cs.getContext().getStopping().set(true); 1223 try { 1224 LOG.debug("Cleaning up connection resources: {}", getRemoteAddress()); 1225 processRemoveConnection(cs.getInfo().getConnectionId(), RemoveInfo.LAST_DELIVERED_UNKNOWN); 1226 } catch (Throwable ignore) { 1227 LOG.debug("Exception caught removing connection {}. This exception is ignored.", cs.getInfo().getConnectionId(), ignore); 1228 } 1229 } 1230 } 1231 LOG.debug("Connection Stopped: {}", getRemoteAddress()); 1232 } 1233 1234 /** 1235 * @return Returns the blockedCandidate. 1236 */ 1237 public boolean isBlockedCandidate() { 1238 return blockedCandidate; 1239 } 1240 1241 /** 1242 * @param blockedCandidate The blockedCandidate to set. 1243 */ 1244 public void setBlockedCandidate(boolean blockedCandidate) { 1245 this.blockedCandidate = blockedCandidate; 1246 } 1247 1248 /** 1249 * @return Returns the markedCandidate. 1250 */ 1251 public boolean isMarkedCandidate() { 1252 return markedCandidate; 1253 } 1254 1255 /** 1256 * @param markedCandidate The markedCandidate to set. 1257 */ 1258 public void setMarkedCandidate(boolean markedCandidate) { 1259 this.markedCandidate = markedCandidate; 1260 if (!markedCandidate) { 1261 timeStamp = 0; 1262 blockedCandidate = false; 1263 } 1264 } 1265 1266 /** 1267 * @param slow The slow to set. 1268 */ 1269 public void setSlow(boolean slow) { 1270 this.slow = slow; 1271 } 1272 1273 /** 1274 * @return true if the Connection is slow 1275 */ 1276 @Override 1277 public boolean isSlow() { 1278 return slow; 1279 } 1280 1281 /** 1282 * @return true if the Connection is potentially blocked 1283 */ 1284 public boolean isMarkedBlockedCandidate() { 1285 return markedCandidate; 1286 } 1287 1288 /** 1289 * Mark the Connection, so we can deem if it's collectable on the next sweep 1290 */ 1291 public void doMark() { 1292 if (timeStamp == 0) { 1293 timeStamp = System.currentTimeMillis(); 1294 } 1295 } 1296 1297 /** 1298 * @return if after being marked, the Connection is still writing 1299 */ 1300 @Override 1301 public boolean isBlocked() { 1302 return blocked; 1303 } 1304 1305 /** 1306 * @return true if the Connection is connected 1307 */ 1308 @Override 1309 public boolean isConnected() { 1310 return connected; 1311 } 1312 1313 /** 1314 * @param blocked The blocked to set. 1315 */ 1316 public void setBlocked(boolean blocked) { 1317 this.blocked = blocked; 1318 } 1319 1320 /** 1321 * @param connected The connected to set. 1322 */ 1323 public void setConnected(boolean connected) { 1324 this.connected = connected; 1325 } 1326 1327 /** 1328 * @return true if the Connection is active 1329 */ 1330 @Override 1331 public boolean isActive() { 1332 return active; 1333 } 1334 1335 /** 1336 * @param active The active to set. 1337 */ 1338 public void setActive(boolean active) { 1339 this.active = active; 1340 } 1341 1342 /** 1343 * @return true if the Connection is starting 1344 */ 1345 public boolean isStarting() { 1346 return status.get() == STARTING; 1347 } 1348 1349 @Override 1350 public synchronized boolean isNetworkConnection() { 1351 return networkConnection; 1352 } 1353 1354 @Override 1355 public boolean isFaultTolerantConnection() { 1356 return this.faultTolerantConnection; 1357 } 1358 1359 /** 1360 * @return true if the Connection needs to stop 1361 */ 1362 public boolean isPendingStop() { 1363 return status.get() == PENDING_STOP; 1364 } 1365 1366 private NetworkBridgeConfiguration getNetworkConfiguration(final BrokerInfo info) throws IOException { 1367 Properties properties = MarshallingSupport.stringToProperties(info.getNetworkProperties()); 1368 Map<String, String> props = createMap(properties); 1369 NetworkBridgeConfiguration config = new NetworkBridgeConfiguration(); 1370 IntrospectionSupport.setProperties(config, props, ""); 1371 return config; 1372 } 1373 1374 @Override 1375 public Response processBrokerInfo(BrokerInfo info) { 1376 if (info.isSlaveBroker()) { 1377 LOG.error(" Slave Brokers are no longer supported - slave trying to attach is: {}", info.getBrokerName()); 1378 } else if (info.isNetworkConnection() && !info.isDuplexConnection()) { 1379 try { 1380 NetworkBridgeConfiguration config = getNetworkConfiguration(info); 1381 if (config.isSyncDurableSubs() && protocolVersion.get() >= CommandTypes.PROTOCOL_VERSION_DURABLE_SYNC) { 1382 LOG.debug("SyncDurableSubs is enabled, Sending BrokerSubscriptionInfo"); 1383 dispatchSync(NetworkBridgeUtils.getBrokerSubscriptionInfo(this.broker.getBrokerService(), config)); 1384 } 1385 } catch (Exception e) { 1386 LOG.error("Failed to respond to network bridge creation from broker {}", info.getBrokerId(), e); 1387 return null; 1388 } 1389 } else if (info.isNetworkConnection() && info.isDuplexConnection()) { 1390 // so this TransportConnection is the rear end of a network bridge 1391 // We have been requested to create a two way pipe ... 1392 try { 1393 NetworkBridgeConfiguration config = getNetworkConfiguration(info); 1394 config.setBrokerName(broker.getBrokerName()); 1395 1396 if (config.isSyncDurableSubs() && protocolVersion.get() >= CommandTypes.PROTOCOL_VERSION_DURABLE_SYNC) { 1397 LOG.debug("SyncDurableSubs is enabled, Sending BrokerSubscriptionInfo"); 1398 dispatchSync(NetworkBridgeUtils.getBrokerSubscriptionInfo(this.broker.getBrokerService(), config)); 1399 } 1400 1401 // check for existing duplex connection hanging about 1402 1403 // We first look if existing network connection already exists for the same broker Id and network connector name 1404 // It's possible in case of brief network fault to have this transport connector side of the connection always active 1405 // and the duplex network connector side wanting to open a new one 1406 // In this case, the old connection must be broken 1407 String duplexNetworkConnectorId = config.getName() + "@" + info.getBrokerId(); 1408 CopyOnWriteArrayList<TransportConnection> connections = this.connector.getConnections(); 1409 synchronized (connections) { 1410 for (Iterator<TransportConnection> iter = connections.iterator(); iter.hasNext(); ) { 1411 TransportConnection c = iter.next(); 1412 if ((c != this) && (duplexNetworkConnectorId.equals(c.getDuplexNetworkConnectorId()))) { 1413 LOG.warn("Stopping an existing active duplex connection [{}] for network connector ({}).", c, duplexNetworkConnectorId); 1414 c.stopAsync(); 1415 // better to wait for a bit rather than get connection id already in use and failure to start new bridge 1416 c.getStopped().await(1, TimeUnit.SECONDS); 1417 } 1418 } 1419 setDuplexNetworkConnectorId(duplexNetworkConnectorId); 1420 } 1421 Transport localTransport = NetworkBridgeFactory.createLocalTransport(config, broker.getVmConnectorURI()); 1422 Transport remoteBridgeTransport = transport; 1423 if (! (remoteBridgeTransport instanceof ResponseCorrelator)) { 1424 // the vm transport case is already wrapped 1425 remoteBridgeTransport = new ResponseCorrelator(remoteBridgeTransport); 1426 } 1427 String duplexName = localTransport.toString(); 1428 if (duplexName.contains("#")) { 1429 duplexName = duplexName.substring(duplexName.lastIndexOf("#")); 1430 } 1431 MBeanNetworkListener listener = new MBeanNetworkListener(brokerService, config, brokerService.createDuplexNetworkConnectorObjectName(duplexName)); 1432 listener.setCreatedByDuplex(true); 1433 duplexBridge = config.getBridgeFactory().createNetworkBridge(config, localTransport, remoteBridgeTransport, listener); 1434 duplexBridge.setBrokerService(brokerService); 1435 //Need to set durableDestinations to properly restart subs when dynamicOnly=false 1436 duplexBridge.setDurableDestinations(NetworkConnector.getDurableTopicDestinations( 1437 broker.getDurableDestinations())); 1438 1439 // now turn duplex off this side 1440 info.setDuplexConnection(false); 1441 duplexBridge.setCreatedByDuplex(true); 1442 duplexBridge.duplexStart(this, brokerInfo, info); 1443 LOG.info("Started responder end of duplex bridge {}", duplexNetworkConnectorId); 1444 return null; 1445 } catch (TransportDisposedIOException e) { 1446 LOG.warn("Duplex bridge {} was stopped before it was correctly started.", duplexNetworkConnectorId); 1447 return null; 1448 } catch (Exception e) { 1449 LOG.error("Failed to create responder end of duplex network bridge {}", duplexNetworkConnectorId, e); 1450 return null; 1451 } 1452 } 1453 // We only expect to get one broker info command per connection 1454 if (this.brokerInfo != null) { 1455 LOG.warn("Unexpected extra broker info command received: {}", info); 1456 } 1457 this.brokerInfo = info; 1458 networkConnection = true; 1459 List<TransportConnectionState> connectionStates = listConnectionStates(); 1460 for (TransportConnectionState cs : connectionStates) { 1461 cs.getContext().setNetworkConnection(true); 1462 } 1463 return null; 1464 } 1465 1466 @SuppressWarnings({"unchecked", "rawtypes"}) 1467 private HashMap<String, String> createMap(Properties properties) { 1468 return new HashMap(properties); 1469 } 1470 1471 protected void dispatch(Command command) throws IOException { 1472 try { 1473 setMarkedCandidate(true); 1474 transport.oneway(command); 1475 } finally { 1476 setMarkedCandidate(false); 1477 } 1478 } 1479 1480 @Override 1481 public String getRemoteAddress() { 1482 return transport.getRemoteAddress(); 1483 } 1484 1485 public Transport getTransport() { 1486 return transport; 1487 } 1488 1489 @Override 1490 public String getConnectionId() { 1491 List<TransportConnectionState> connectionStates = listConnectionStates(); 1492 for (TransportConnectionState cs : connectionStates) { 1493 if (cs.getInfo().getClientId() != null) { 1494 return cs.getInfo().getClientId(); 1495 } 1496 return cs.getInfo().getConnectionId().toString(); 1497 } 1498 return null; 1499 } 1500 1501 @Override 1502 public void updateClient(ConnectionControl control) { 1503 if (isActive() && isBlocked() == false && isFaultTolerantConnection() && this.wireFormatInfo != null 1504 && this.wireFormatInfo.getVersion() >= 6) { 1505 dispatchAsync(control); 1506 } 1507 } 1508 1509 public ProducerBrokerExchange getProducerBrokerExchangeIfExists(ProducerInfo producerInfo){ 1510 ProducerBrokerExchange result = null; 1511 if (producerInfo != null && producerInfo.getProducerId() != null){ 1512 synchronized (producerExchanges){ 1513 result = producerExchanges.get(producerInfo.getProducerId()); 1514 } 1515 } 1516 return result; 1517 } 1518 1519 private ProducerBrokerExchange getProducerBrokerExchange(ProducerId id) throws IOException { 1520 ProducerBrokerExchange result = producerExchanges.get(id); 1521 if (result == null) { 1522 synchronized (producerExchanges) { 1523 result = new ProducerBrokerExchange(); 1524 TransportConnectionState state = lookupConnectionState(id); 1525 context = state.getContext(); 1526 result.setConnectionContext(context); 1527 if (context.isReconnect() || (context.isNetworkConnection() && connector.isAuditNetworkProducers())) { 1528 result.setLastStoredSequenceId(brokerService.getPersistenceAdapter().getLastProducerSequenceId(id)); 1529 } 1530 SessionState ss = state.getSessionState(id.getParentId()); 1531 if (ss != null) { 1532 result.setProducerState(ss.getProducerState(id)); 1533 ProducerState producerState = ss.getProducerState(id); 1534 if (producerState != null && producerState.getInfo() != null) { 1535 ProducerInfo info = producerState.getInfo(); 1536 result.setMutable(info.getDestination() == null || info.getDestination().isComposite()); 1537 } 1538 } 1539 producerExchanges.put(id, result); 1540 } 1541 } else { 1542 context = result.getConnectionContext(); 1543 } 1544 return result; 1545 } 1546 1547 private void removeProducerBrokerExchange(ProducerId id) { 1548 synchronized (producerExchanges) { 1549 producerExchanges.remove(id); 1550 } 1551 } 1552 1553 private ConsumerBrokerExchange getConsumerBrokerExchange(ConsumerId id) { 1554 ConsumerBrokerExchange result = consumerExchanges.get(id); 1555 return result; 1556 } 1557 1558 private ConsumerBrokerExchange addConsumerBrokerExchange(TransportConnectionState connectionState, ConsumerId id) { 1559 ConsumerBrokerExchange result = consumerExchanges.get(id); 1560 if (result == null) { 1561 synchronized (consumerExchanges) { 1562 result = new ConsumerBrokerExchange(); 1563 context = connectionState.getContext(); 1564 result.setConnectionContext(context); 1565 SessionState ss = connectionState.getSessionState(id.getParentId()); 1566 if (ss != null) { 1567 ConsumerState cs = ss.getConsumerState(id); 1568 if (cs != null) { 1569 ConsumerInfo info = cs.getInfo(); 1570 if (info != null) { 1571 if (info.getDestination() != null && info.getDestination().isPattern()) { 1572 result.setWildcard(true); 1573 } 1574 } 1575 } 1576 } 1577 consumerExchanges.put(id, result); 1578 } 1579 } 1580 return result; 1581 } 1582 1583 private void removeConsumerBrokerExchange(ConsumerId id) { 1584 synchronized (consumerExchanges) { 1585 consumerExchanges.remove(id); 1586 } 1587 } 1588 1589 public int getProtocolVersion() { 1590 return protocolVersion.get(); 1591 } 1592 1593 @Override 1594 public Response processControlCommand(ControlCommand command) throws Exception { 1595 return null; 1596 } 1597 1598 @Override 1599 public Response processMessageDispatch(MessageDispatch dispatch) throws Exception { 1600 return null; 1601 } 1602 1603 @Override 1604 public Response processConnectionControl(ConnectionControl control) throws Exception { 1605 if (control != null) { 1606 faultTolerantConnection = control.isFaultTolerant(); 1607 } 1608 return null; 1609 } 1610 1611 @Override 1612 public Response processConnectionError(ConnectionError error) throws Exception { 1613 return null; 1614 } 1615 1616 @Override 1617 public Response processConsumerControl(ConsumerControl control) throws Exception { 1618 ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(control.getConsumerId()); 1619 broker.processConsumerControl(consumerExchange, control); 1620 return null; 1621 } 1622 1623 protected synchronized TransportConnectionState registerConnectionState(ConnectionId connectionId, 1624 TransportConnectionState state) { 1625 TransportConnectionState cs = null; 1626 if (!connectionStateRegister.isEmpty() && !connectionStateRegister.doesHandleMultipleConnectionStates()) { 1627 // swap implementations 1628 TransportConnectionStateRegister newRegister = new MapTransportConnectionStateRegister(); 1629 newRegister.intialize(connectionStateRegister); 1630 connectionStateRegister = newRegister; 1631 } 1632 cs = connectionStateRegister.registerConnectionState(connectionId, state); 1633 return cs; 1634 } 1635 1636 protected synchronized TransportConnectionState unregisterConnectionState(ConnectionId connectionId) { 1637 return connectionStateRegister.unregisterConnectionState(connectionId); 1638 } 1639 1640 protected synchronized List<TransportConnectionState> listConnectionStates() { 1641 return connectionStateRegister.listConnectionStates(); 1642 } 1643 1644 protected synchronized TransportConnectionState lookupConnectionState(String connectionId) { 1645 return connectionStateRegister.lookupConnectionState(connectionId); 1646 } 1647 1648 protected synchronized TransportConnectionState lookupConnectionState(ConsumerId id) { 1649 return connectionStateRegister.lookupConnectionState(id); 1650 } 1651 1652 protected synchronized TransportConnectionState lookupConnectionState(ProducerId id) { 1653 return connectionStateRegister.lookupConnectionState(id); 1654 } 1655 1656 protected synchronized TransportConnectionState lookupConnectionState(SessionId id) { 1657 return connectionStateRegister.lookupConnectionState(id); 1658 } 1659 1660 // public only for testing 1661 public synchronized TransportConnectionState lookupConnectionState(ConnectionId connectionId) { 1662 return connectionStateRegister.lookupConnectionState(connectionId); 1663 } 1664 1665 protected synchronized void setDuplexNetworkConnectorId(String duplexNetworkConnectorId) { 1666 this.duplexNetworkConnectorId = duplexNetworkConnectorId; 1667 } 1668 1669 protected synchronized String getDuplexNetworkConnectorId() { 1670 return this.duplexNetworkConnectorId; 1671 } 1672 1673 public boolean isStopping() { 1674 return stopping.get(); 1675 } 1676 1677 protected CountDownLatch getStopped() { 1678 return stopped; 1679 } 1680 1681 private int getProducerCount(ConnectionId connectionId) { 1682 int result = 0; 1683 TransportConnectionState cs = lookupConnectionState(connectionId); 1684 if (cs != null) { 1685 for (SessionId sessionId : cs.getSessionIds()) { 1686 SessionState sessionState = cs.getSessionState(sessionId); 1687 if (sessionState != null) { 1688 result += sessionState.getProducerIds().size(); 1689 } 1690 } 1691 } 1692 return result; 1693 } 1694 1695 private int getConsumerCount(ConnectionId connectionId) { 1696 int result = 0; 1697 TransportConnectionState cs = lookupConnectionState(connectionId); 1698 if (cs != null) { 1699 for (SessionId sessionId : cs.getSessionIds()) { 1700 SessionState sessionState = cs.getSessionState(sessionId); 1701 if (sessionState != null) { 1702 result += sessionState.getConsumerIds().size(); 1703 } 1704 } 1705 } 1706 return result; 1707 } 1708 1709 public WireFormatInfo getRemoteWireFormatInfo() { 1710 return wireFormatInfo; 1711 } 1712 1713 /* (non-Javadoc) 1714 * @see org.apache.activemq.state.CommandVisitor#processBrokerSubscriptionInfo(org.apache.activemq.command.BrokerSubscriptionInfo) 1715 */ 1716 @Override 1717 public Response processBrokerSubscriptionInfo(BrokerSubscriptionInfo info) throws Exception { 1718 return null; 1719 } 1720}