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}