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