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