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.IOException; 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.util.ArrayList; 023import java.util.LinkedList; 024import java.util.List; 025import java.util.StringTokenizer; 026import java.util.concurrent.CopyOnWriteArrayList; 027import java.util.regex.Pattern; 028 029import javax.management.ObjectName; 030 031import org.apache.activemq.broker.jmx.ManagedTransportConnector; 032import org.apache.activemq.broker.jmx.ManagementContext; 033import org.apache.activemq.broker.region.ConnectorStatistics; 034import org.apache.activemq.command.BrokerInfo; 035import org.apache.activemq.command.ConnectionControl; 036import org.apache.activemq.security.MessageAuthorizationPolicy; 037import org.apache.activemq.thread.TaskRunnerFactory; 038import org.apache.activemq.transport.Transport; 039import org.apache.activemq.transport.TransportAcceptListener; 040import org.apache.activemq.transport.TransportFactorySupport; 041import org.apache.activemq.transport.TransportServer; 042import org.apache.activemq.transport.discovery.DiscoveryAgent; 043import org.apache.activemq.transport.discovery.DiscoveryAgentFactory; 044import org.apache.activemq.util.ServiceStopper; 045import org.apache.activemq.util.ServiceSupport; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049/** 050 * @org.apache.xbean.XBean 051 */ 052public class TransportConnector implements Connector, BrokerServiceAware { 053 054 final Logger LOG = LoggerFactory.getLogger(TransportConnector.class); 055 056 protected final CopyOnWriteArrayList<TransportConnection> connections = new CopyOnWriteArrayList<TransportConnection>(); 057 protected TransportStatusDetector statusDector; 058 private BrokerService brokerService; 059 private TransportServer server; 060 private URI uri; 061 private BrokerInfo brokerInfo = new BrokerInfo(); 062 private TaskRunnerFactory taskRunnerFactory; 063 private MessageAuthorizationPolicy messageAuthorizationPolicy; 064 private DiscoveryAgent discoveryAgent; 065 private final ConnectorStatistics statistics = new ConnectorStatistics(); 066 private URI discoveryUri; 067 private String name; 068 private boolean disableAsyncDispatch; 069 private boolean enableStatusMonitor = false; 070 private Broker broker; 071 private boolean updateClusterClients = false; 072 private boolean rebalanceClusterClients; 073 private boolean updateClusterClientsOnRemove = false; 074 private String updateClusterFilter; 075 private boolean auditNetworkProducers = false; 076 private int maximumProducersAllowedPerConnection = Integer.MAX_VALUE; 077 private int maximumConsumersAllowedPerConnection = Integer.MAX_VALUE; 078 private PublishedAddressPolicy publishedAddressPolicy = new PublishedAddressPolicy(); 079 private boolean allowLinkStealing = false; 080 private boolean warnOnRemoteClose = false; 081 private boolean displayStackTrace = false; 082 083 LinkedList<String> peerBrokers = new LinkedList<String>(); 084 085 public TransportConnector() { 086 } 087 088 public TransportConnector(TransportServer server) { 089 this(); 090 setServer(server); 091 if (server != null && server.getConnectURI() != null) { 092 URI uri = server.getConnectURI(); 093 if (uri != null && uri.getScheme().equals("vm")) { 094 setEnableStatusMonitor(false); 095 } 096 } 097 } 098 099 /** 100 * @return Returns the connections. 101 */ 102 public CopyOnWriteArrayList<TransportConnection> getConnections() { 103 return connections; 104 } 105 106 /** 107 * Factory method to create a JMX managed version of this transport 108 * connector 109 */ 110 public ManagedTransportConnector asManagedConnector(ManagementContext context, ObjectName connectorName) throws IOException, URISyntaxException { 111 ManagedTransportConnector rc = new ManagedTransportConnector(context, connectorName, getServer()); 112 rc.setBrokerInfo(getBrokerInfo()); 113 rc.setDisableAsyncDispatch(isDisableAsyncDispatch()); 114 rc.setDiscoveryAgent(getDiscoveryAgent()); 115 rc.setDiscoveryUri(getDiscoveryUri()); 116 rc.setEnableStatusMonitor(isEnableStatusMonitor()); 117 rc.setMessageAuthorizationPolicy(getMessageAuthorizationPolicy()); 118 rc.setName(getName()); 119 rc.setTaskRunnerFactory(getTaskRunnerFactory()); 120 rc.setUri(getUri()); 121 rc.setBrokerService(brokerService); 122 rc.setUpdateClusterClients(isUpdateClusterClients()); 123 rc.setRebalanceClusterClients(isRebalanceClusterClients()); 124 rc.setUpdateClusterFilter(getUpdateClusterFilter()); 125 rc.setUpdateClusterClientsOnRemove(isUpdateClusterClientsOnRemove()); 126 rc.setAuditNetworkProducers(isAuditNetworkProducers()); 127 rc.setMaximumConsumersAllowedPerConnection(getMaximumConsumersAllowedPerConnection()); 128 rc.setMaximumProducersAllowedPerConnection(getMaximumProducersAllowedPerConnection()); 129 rc.setPublishedAddressPolicy(getPublishedAddressPolicy()); 130 rc.setAllowLinkStealing(allowLinkStealing); 131 rc.setWarnOnRemoteClose(isWarnOnRemoteClose()); 132 return rc; 133 } 134 135 @Override 136 public BrokerInfo getBrokerInfo() { 137 return brokerInfo; 138 } 139 140 public void setBrokerInfo(BrokerInfo brokerInfo) { 141 this.brokerInfo = brokerInfo; 142 } 143 144 public TransportServer getServer() throws IOException, URISyntaxException { 145 if (server == null) { 146 setServer(createTransportServer()); 147 } 148 return server; 149 } 150 151 public void setServer(TransportServer server) { 152 this.server = server; 153 } 154 155 public URI getUri() { 156 if (uri == null) { 157 try { 158 uri = getConnectUri(); 159 } catch (Throwable e) { 160 } 161 } 162 return uri; 163 } 164 165 /** 166 * Sets the server transport URI to use if there is not a 167 * {@link TransportServer} configured via the 168 * {@link #setServer(TransportServer)} method. This value is used to lazy 169 * create a {@link TransportServer} instance 170 * 171 * @param uri 172 */ 173 public void setUri(URI uri) { 174 this.uri = uri; 175 } 176 177 public TaskRunnerFactory getTaskRunnerFactory() { 178 return taskRunnerFactory; 179 } 180 181 public void setTaskRunnerFactory(TaskRunnerFactory taskRunnerFactory) { 182 this.taskRunnerFactory = taskRunnerFactory; 183 } 184 185 /** 186 * @return the statistics for this connector 187 */ 188 @Override 189 public ConnectorStatistics getStatistics() { 190 return statistics; 191 } 192 193 public MessageAuthorizationPolicy getMessageAuthorizationPolicy() { 194 return messageAuthorizationPolicy; 195 } 196 197 /** 198 * Sets the policy used to decide if the current connection is authorized to 199 * consume a given message 200 */ 201 public void setMessageAuthorizationPolicy(MessageAuthorizationPolicy messageAuthorizationPolicy) { 202 this.messageAuthorizationPolicy = messageAuthorizationPolicy; 203 } 204 205 @Override 206 public void start() throws Exception { 207 broker = brokerService.getBroker(); 208 brokerInfo.setBrokerName(broker.getBrokerName()); 209 brokerInfo.setBrokerId(broker.getBrokerId()); 210 brokerInfo.setPeerBrokerInfos(broker.getPeerBrokerInfos()); 211 brokerInfo.setFaultTolerantConfiguration(broker.isFaultTolerantConfiguration()); 212 brokerInfo.setBrokerURL(broker.getBrokerService().getDefaultSocketURIString()); 213 getServer().setAcceptListener(new TransportAcceptListener() { 214 @Override 215 public void onAccept(final Transport transport) { 216 final String remoteHost = transport.getRemoteAddress(); 217 try { 218 brokerService.getTaskRunnerFactory().execute(new Runnable() { 219 @Override 220 public void run() { 221 try { 222 if (!brokerService.isStopping()) { 223 Connection connection = createConnection(transport); 224 connection.start(); 225 } else { 226 throw new BrokerStoppedException("Broker " + brokerService + " is being stopped"); 227 } 228 } catch (Exception e) { 229 ServiceSupport.dispose(transport); 230 onAcceptError(e, remoteHost); 231 } 232 } 233 }); 234 } catch (Exception e) { 235 ServiceSupport.dispose(transport); 236 onAcceptError(e, remoteHost); 237 } 238 } 239 240 @Override 241 public void onAcceptError(Exception error) { 242 onAcceptError(error, null); 243 } 244 245 private void onAcceptError(Exception error, String remoteHost) { 246 if (brokerService != null && brokerService.isStopping()) { 247 LOG.info("Could not accept connection during shutdown {} : {} ({})", (remoteHost == null ? "" : "from " + remoteHost), error.getLocalizedMessage(), getRootCause(error).getMessage()); 248 } else { 249 LOG.warn("Could not accept connection {}: {} ({})", (remoteHost == null ? "" : "from " + remoteHost), error.getMessage(), getRootCause(error).getMessage()); 250 LOG.debug("Reason: " + error.getMessage(), error); 251 } 252 } 253 }); 254 getServer().setBrokerInfo(brokerInfo); 255 getServer().start(); 256 257 DiscoveryAgent da = getDiscoveryAgent(); 258 if (da != null) { 259 da.registerService(getPublishableConnectString()); 260 da.start(); 261 } 262 if (enableStatusMonitor) { 263 this.statusDector = new TransportStatusDetector(this); 264 this.statusDector.start(); 265 } 266 267 LOG.info("Connector {} started", getName()); 268 } 269 270 public static Throwable getRootCause(final Throwable throwable) { 271 final List<Throwable> list = getThrowableList(throwable); 272 return list.isEmpty() ? null : list.get(list.size() - 1); 273 } 274 275 static List<Throwable> getThrowableList(Throwable throwable) { 276 final List<Throwable> list = new ArrayList<>(); 277 while (throwable != null && !list.contains(throwable)) { 278 list.add(throwable); 279 throwable = throwable.getCause(); 280 } 281 return list; 282 } 283 284 public String getPublishableConnectString() throws Exception { 285 String publishableConnectString = publishedAddressPolicy.getPublishableConnectString(this); 286 LOG.debug("Publishing: {} for broker transport URI: {}", publishableConnectString, getConnectUri()); 287 return publishableConnectString; 288 } 289 290 public URI getPublishableConnectURI() throws Exception { 291 return publishedAddressPolicy.getPublishableConnectURI(this); 292 } 293 294 @Override 295 public void stop() throws Exception { 296 ServiceStopper ss = new ServiceStopper(); 297 if (discoveryAgent != null) { 298 ss.stop(discoveryAgent); 299 } 300 if (server != null) { 301 ss.stop(server); 302 } 303 if (this.statusDector != null) { 304 this.statusDector.stop(); 305 } 306 307 for (TransportConnection connection : connections) { 308 ss.stop(connection); 309 } 310 server = null; 311 ss.throwFirstException(); 312 LOG.info("Connector {} stopped", getName()); 313 } 314 315 // Implementation methods 316 // ------------------------------------------------------------------------- 317 protected Connection createConnection(Transport transport) throws IOException { 318 // prefer to use task runner from broker service as stop task runner, as we can then 319 // tie it to the lifecycle of the broker service 320 TransportConnection answer = new TransportConnection(this, transport, broker, disableAsyncDispatch ? null 321 : taskRunnerFactory, brokerService.getTaskRunnerFactory()); 322 boolean statEnabled = this.getStatistics().isEnabled(); 323 answer.getStatistics().setEnabled(statEnabled); 324 answer.setMessageAuthorizationPolicy(messageAuthorizationPolicy); 325 return answer; 326 } 327 328 protected TransportServer createTransportServer() throws IOException, URISyntaxException { 329 if (uri == null) { 330 throw new IllegalArgumentException("You must specify either a server or uri property"); 331 } 332 if (brokerService == null) { 333 throw new IllegalArgumentException( 334 "You must specify the brokerService property. Maybe this connector should be added to a broker?"); 335 } 336 return TransportFactorySupport.bind(brokerService, uri); 337 } 338 339 public DiscoveryAgent getDiscoveryAgent() throws IOException { 340 if (discoveryAgent == null) { 341 discoveryAgent = createDiscoveryAgent(); 342 } 343 return discoveryAgent; 344 } 345 346 protected DiscoveryAgent createDiscoveryAgent() throws IOException { 347 if (discoveryUri != null) { 348 DiscoveryAgent agent = DiscoveryAgentFactory.createDiscoveryAgent(discoveryUri); 349 350 if (agent != null && agent instanceof BrokerServiceAware) { 351 ((BrokerServiceAware) agent).setBrokerService(brokerService); 352 } 353 354 return agent; 355 } 356 return null; 357 } 358 359 public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) { 360 this.discoveryAgent = discoveryAgent; 361 } 362 363 public URI getDiscoveryUri() { 364 return discoveryUri; 365 } 366 367 public void setDiscoveryUri(URI discoveryUri) { 368 this.discoveryUri = discoveryUri; 369 } 370 371 public URI getConnectUri() throws IOException, URISyntaxException { 372 if (server != null) { 373 return server.getConnectURI(); 374 } else { 375 return uri; 376 } 377 } 378 379 public void onStarted(TransportConnection connection) { 380 connections.add(connection); 381 } 382 383 public void onStopped(TransportConnection connection) { 384 connections.remove(connection); 385 } 386 387 public String getName() { 388 if (name == null) { 389 uri = getUri(); 390 if (uri != null) { 391 name = uri.toString(); 392 } 393 } 394 return name; 395 } 396 397 public void setName(String name) { 398 this.name = name; 399 } 400 401 @Override 402 public String toString() { 403 String rc = getName(); 404 if (rc == null) { 405 rc = super.toString(); 406 } 407 return rc; 408 } 409 410 protected ConnectionControl getConnectionControl() { 411 boolean rebalance = isRebalanceClusterClients(); 412 String connectedBrokers = ""; 413 String separator = ""; 414 415 if (isUpdateClusterClients()) { 416 synchronized (peerBrokers) { 417 for (String uri : getPeerBrokers()) { 418 connectedBrokers += separator + uri; 419 separator = ","; 420 } 421 422 if (rebalance) { 423 String shuffle = peerBrokers.removeFirst(); 424 peerBrokers.addLast(shuffle); 425 } 426 } 427 } 428 ConnectionControl control = new ConnectionControl(); 429 control.setConnectedBrokers(connectedBrokers); 430 control.setRebalanceConnection(rebalance); 431 return control; 432 } 433 434 public void addPeerBroker(BrokerInfo info) { 435 if (isMatchesClusterFilter(info.getBrokerName())) { 436 synchronized (peerBrokers) { 437 getPeerBrokers().addLast(info.getBrokerURL()); 438 } 439 } 440 } 441 442 public void removePeerBroker(BrokerInfo info) { 443 synchronized (peerBrokers) { 444 getPeerBrokers().remove(info.getBrokerURL()); 445 } 446 } 447 448 public LinkedList<String> getPeerBrokers() { 449 synchronized (peerBrokers) { 450 if (peerBrokers.isEmpty()) { 451 peerBrokers.add(brokerService.getDefaultSocketURIString()); 452 } 453 return peerBrokers; 454 } 455 } 456 457 @Override 458 public void updateClientClusterInfo() { 459 if (isRebalanceClusterClients() || isUpdateClusterClients()) { 460 ConnectionControl control = getConnectionControl(); 461 for (Connection c : this.connections) { 462 c.updateClient(control); 463 if (isRebalanceClusterClients()) { 464 control = getConnectionControl(); 465 } 466 } 467 } 468 } 469 470 private boolean isMatchesClusterFilter(String brokerName) { 471 boolean result = true; 472 String filter = getUpdateClusterFilter(); 473 if (filter != null) { 474 filter = filter.trim(); 475 if (filter.length() > 0) { 476 result = false; 477 StringTokenizer tokenizer = new StringTokenizer(filter, ","); 478 while (!result && tokenizer.hasMoreTokens()) { 479 String token = tokenizer.nextToken(); 480 result = isMatchesClusterFilter(brokerName, token); 481 } 482 } 483 } 484 485 return result; 486 } 487 488 private boolean isMatchesClusterFilter(String brokerName, String match) { 489 boolean result = false; 490 if (brokerName != null && match != null && brokerName.length() > 0 && match.length() > 0) { 491 result = Pattern.matches(match, brokerName); 492 } 493 return result; 494 } 495 496 public boolean isDisableAsyncDispatch() { 497 return disableAsyncDispatch; 498 } 499 500 public void setDisableAsyncDispatch(boolean disableAsyncDispatch) { 501 this.disableAsyncDispatch = disableAsyncDispatch; 502 } 503 504 /** 505 * @return the enableStatusMonitor 506 */ 507 public boolean isEnableStatusMonitor() { 508 return enableStatusMonitor; 509 } 510 511 /** 512 * @param enableStatusMonitor 513 * the enableStatusMonitor to set 514 */ 515 public void setEnableStatusMonitor(boolean enableStatusMonitor) { 516 this.enableStatusMonitor = enableStatusMonitor; 517 } 518 519 /** 520 * This is called by the BrokerService right before it starts the transport. 521 */ 522 @Override 523 public void setBrokerService(BrokerService brokerService) { 524 this.brokerService = brokerService; 525 } 526 527 public Broker getBroker() { 528 return broker; 529 } 530 531 public BrokerService getBrokerService() { 532 return brokerService; 533 } 534 535 /** 536 * @return the updateClusterClients 537 */ 538 @Override 539 public boolean isUpdateClusterClients() { 540 return this.updateClusterClients; 541 } 542 543 /** 544 * @param updateClusterClients 545 * the updateClusterClients to set 546 */ 547 public void setUpdateClusterClients(boolean updateClusterClients) { 548 this.updateClusterClients = updateClusterClients; 549 } 550 551 /** 552 * @return the rebalanceClusterClients 553 */ 554 @Override 555 public boolean isRebalanceClusterClients() { 556 return this.rebalanceClusterClients; 557 } 558 559 /** 560 * @param rebalanceClusterClients 561 * the rebalanceClusterClients to set 562 */ 563 public void setRebalanceClusterClients(boolean rebalanceClusterClients) { 564 this.rebalanceClusterClients = rebalanceClusterClients; 565 } 566 567 /** 568 * @return the updateClusterClientsOnRemove 569 */ 570 @Override 571 public boolean isUpdateClusterClientsOnRemove() { 572 return this.updateClusterClientsOnRemove; 573 } 574 575 /** 576 * @param updateClusterClientsOnRemove the updateClusterClientsOnRemove to set 577 */ 578 public void setUpdateClusterClientsOnRemove(boolean updateClusterClientsOnRemove) { 579 this.updateClusterClientsOnRemove = updateClusterClientsOnRemove; 580 } 581 582 /** 583 * @return the updateClusterFilter 584 */ 585 @Override 586 public String getUpdateClusterFilter() { 587 return this.updateClusterFilter; 588 } 589 590 /** 591 * @param updateClusterFilter 592 * the updateClusterFilter to set 593 */ 594 public void setUpdateClusterFilter(String updateClusterFilter) { 595 this.updateClusterFilter = updateClusterFilter; 596 } 597 598 @Override 599 public int connectionCount() { 600 return connections.size(); 601 } 602 603 @Override 604 public boolean isAllowLinkStealing() { 605 return server.isAllowLinkStealing(); 606 } 607 608 public void setAllowLinkStealing(boolean allowLinkStealing) { 609 this.allowLinkStealing = allowLinkStealing; 610 } 611 612 public boolean isAuditNetworkProducers() { 613 return auditNetworkProducers; 614 } 615 616 /** 617 * Enable a producer audit on network connections, Traps the case of a missing send reply and resend. 618 * Note: does not work with conduit=false, networked composite destinations or networked virtual topics 619 * @param auditNetworkProducers 620 */ 621 public void setAuditNetworkProducers(boolean auditNetworkProducers) { 622 this.auditNetworkProducers = auditNetworkProducers; 623 } 624 625 public int getMaximumProducersAllowedPerConnection() { 626 return maximumProducersAllowedPerConnection; 627 } 628 629 public void setMaximumProducersAllowedPerConnection(int maximumProducersAllowedPerConnection) { 630 this.maximumProducersAllowedPerConnection = maximumProducersAllowedPerConnection; 631 } 632 633 public int getMaximumConsumersAllowedPerConnection() { 634 return maximumConsumersAllowedPerConnection; 635 } 636 637 public void setMaximumConsumersAllowedPerConnection(int maximumConsumersAllowedPerConnection) { 638 this.maximumConsumersAllowedPerConnection = maximumConsumersAllowedPerConnection; 639 } 640 641 /** 642 * Gets the currently configured policy for creating the published connection address of this 643 * TransportConnector. 644 * 645 * @return the publishedAddressPolicy 646 */ 647 public PublishedAddressPolicy getPublishedAddressPolicy() { 648 return publishedAddressPolicy; 649 } 650 651 /** 652 * Sets the configured policy for creating the published connection address of this 653 * TransportConnector. 654 * 655 * @return the publishedAddressPolicy 656 */ 657 public void setPublishedAddressPolicy(PublishedAddressPolicy publishedAddressPolicy) { 658 this.publishedAddressPolicy = publishedAddressPolicy; 659 } 660 661 public boolean isWarnOnRemoteClose() { 662 return warnOnRemoteClose; 663 } 664 665 public void setWarnOnRemoteClose(boolean warnOnRemoteClose) { 666 this.warnOnRemoteClose = warnOnRemoteClose; 667 } 668 669 public boolean isDisplayStackTrace() { 670 return displayStackTrace; 671 } 672 673 public void setDisplayStackTrace(boolean displayStackTrace) { 674 this.displayStackTrace = displayStackTrace; 675 } 676}