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;
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.setAllowLinkStealing(isAllowLinkStealing());
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                try {
214                    brokerService.getTaskRunnerFactory().execute(new Runnable() {
215                        @Override
216                        public void run() {
217                            try {
218                                if (!brokerService.isStopping()) {
219                                    Connection connection = createConnection(transport);
220                                    connection.start();
221                                } else {
222                                    throw new BrokerStoppedException("Broker " + brokerService + " is being stopped");
223                                }
224                            } catch (Exception e) {
225                                String remoteHost = transport.getRemoteAddress();
226                                ServiceSupport.dispose(transport);
227                                onAcceptError(e, remoteHost);
228                            }
229                        }
230                    });
231                } catch (Exception e) {
232                    String remoteHost = transport.getRemoteAddress();
233                    ServiceSupport.dispose(transport);
234                    onAcceptError(e, remoteHost);
235                }
236            }
237
238            @Override
239            public void onAcceptError(Exception error) {
240                onAcceptError(error, null);
241            }
242
243            private void onAcceptError(Exception error, String remoteHost) {
244                if (brokerService != null && brokerService.isStopping()) {
245                    LOG.info("Could not accept connection during shutdown {} : {} ({})", (remoteHost == null ? "" : "from " + remoteHost), error.getLocalizedMessage(), getRootCause(error).getMessage());
246                } else {
247                    LOG.warn("Could not accept connection {}: {} ({})", (remoteHost == null ? "" : "from " + remoteHost), error.getMessage(), getRootCause(error).getMessage());
248                    LOG.debug("Reason: " + error.getMessage(), error);
249                }
250            }
251        });
252        getServer().setBrokerInfo(brokerInfo);
253        getServer().start();
254
255        DiscoveryAgent da = getDiscoveryAgent();
256        if (da != null) {
257            da.registerService(getPublishableConnectString());
258            da.start();
259        }
260        if (enableStatusMonitor) {
261            this.statusDector = new TransportStatusDetector(this);
262            this.statusDector.start();
263        }
264
265        LOG.info("Connector {} started", getName());
266    }
267
268    static Throwable getRootCause(final Throwable throwable) {
269        final List<Throwable> list = getThrowableList(throwable);
270        return list.isEmpty() ? null : list.get(list.size() - 1);
271    }
272
273    static List<Throwable> getThrowableList(Throwable throwable) {
274        final List<Throwable> list = new ArrayList<>();
275        while (throwable != null && !list.contains(throwable)) {
276            list.add(throwable);
277            throwable = throwable.getCause();
278        }
279        return list;
280    }
281
282    public String getPublishableConnectString() throws Exception {
283        String publishableConnectString = publishedAddressPolicy.getPublishableConnectString(this);
284        LOG.debug("Publishing: {} for broker transport URI: {}", publishableConnectString, getConnectUri());
285        return publishableConnectString;
286    }
287
288    public URI getPublishableConnectURI() throws Exception {
289        return publishedAddressPolicy.getPublishableConnectURI(this);
290    }
291
292    @Override
293    public void stop() throws Exception {
294        ServiceStopper ss = new ServiceStopper();
295        if (discoveryAgent != null) {
296            ss.stop(discoveryAgent);
297        }
298        if (server != null) {
299            ss.stop(server);
300        }
301        if (this.statusDector != null) {
302            this.statusDector.stop();
303        }
304
305        for (TransportConnection connection : connections) {
306            ss.stop(connection);
307        }
308        server = null;
309        ss.throwFirstException();
310        LOG.info("Connector {} stopped", getName());
311    }
312
313    // Implementation methods
314    // -------------------------------------------------------------------------
315    protected Connection createConnection(Transport transport) throws IOException {
316        // prefer to use task runner from broker service as stop task runner, as we can then
317        // tie it to the lifecycle of the broker service
318        TransportConnection answer = new TransportConnection(this, transport, broker, disableAsyncDispatch ? null
319                : taskRunnerFactory, brokerService.getTaskRunnerFactory());
320        boolean statEnabled = this.getStatistics().isEnabled();
321        answer.getStatistics().setEnabled(statEnabled);
322        answer.setMessageAuthorizationPolicy(messageAuthorizationPolicy);
323        return answer;
324    }
325
326    protected TransportServer createTransportServer() throws IOException, URISyntaxException {
327        if (uri == null) {
328            throw new IllegalArgumentException("You must specify either a server or uri property");
329        }
330        if (brokerService == null) {
331            throw new IllegalArgumentException(
332                    "You must specify the brokerService property. Maybe this connector should be added to a broker?");
333        }
334        return TransportFactorySupport.bind(brokerService, uri);
335    }
336
337    public DiscoveryAgent getDiscoveryAgent() throws IOException {
338        if (discoveryAgent == null) {
339            discoveryAgent = createDiscoveryAgent();
340        }
341        return discoveryAgent;
342    }
343
344    protected DiscoveryAgent createDiscoveryAgent() throws IOException {
345        if (discoveryUri != null) {
346            DiscoveryAgent agent = DiscoveryAgentFactory.createDiscoveryAgent(discoveryUri);
347
348            if (agent != null && agent instanceof BrokerServiceAware) {
349                ((BrokerServiceAware) agent).setBrokerService(brokerService);
350            }
351
352            return agent;
353        }
354        return null;
355    }
356
357    public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) {
358        this.discoveryAgent = discoveryAgent;
359    }
360
361    public URI getDiscoveryUri() {
362        return discoveryUri;
363    }
364
365    public void setDiscoveryUri(URI discoveryUri) {
366        this.discoveryUri = discoveryUri;
367    }
368
369    public URI getConnectUri() throws IOException, URISyntaxException {
370        if (server != null) {
371            return server.getConnectURI();
372        } else {
373            return uri;
374        }
375    }
376
377    public void onStarted(TransportConnection connection) {
378        connections.add(connection);
379    }
380
381    public void onStopped(TransportConnection connection) {
382        connections.remove(connection);
383    }
384
385    public String getName() {
386        if (name == null) {
387            uri = getUri();
388            if (uri != null) {
389                name = uri.toString();
390            }
391        }
392        return name;
393    }
394
395    public void setName(String name) {
396        this.name = name;
397    }
398
399    @Override
400    public String toString() {
401        String rc = getName();
402        if (rc == null) {
403            rc = super.toString();
404        }
405        return rc;
406    }
407
408    protected ConnectionControl getConnectionControl() {
409        boolean rebalance = isRebalanceClusterClients();
410        String connectedBrokers = "";
411        String separator = "";
412
413        if (isUpdateClusterClients()) {
414            synchronized (peerBrokers) {
415                for (String uri : getPeerBrokers()) {
416                    connectedBrokers += separator + uri;
417                    separator = ",";
418                }
419
420                if (rebalance) {
421                    String shuffle = peerBrokers.removeFirst();
422                    peerBrokers.addLast(shuffle);
423                }
424            }
425        }
426        ConnectionControl control = new ConnectionControl();
427        control.setConnectedBrokers(connectedBrokers);
428        control.setRebalanceConnection(rebalance);
429        return control;
430    }
431
432    public void addPeerBroker(BrokerInfo info) {
433        if (isMatchesClusterFilter(info.getBrokerName())) {
434            synchronized (peerBrokers) {
435                getPeerBrokers().addLast(info.getBrokerURL());
436            }
437        }
438    }
439
440    public void removePeerBroker(BrokerInfo info) {
441        synchronized (peerBrokers) {
442            getPeerBrokers().remove(info.getBrokerURL());
443        }
444    }
445
446    public LinkedList<String> getPeerBrokers() {
447        synchronized (peerBrokers) {
448            if (peerBrokers.isEmpty()) {
449                peerBrokers.add(brokerService.getDefaultSocketURIString());
450            }
451            return peerBrokers;
452        }
453    }
454
455    @Override
456    public void updateClientClusterInfo() {
457        if (isRebalanceClusterClients() || isUpdateClusterClients()) {
458            ConnectionControl control = getConnectionControl();
459            for (Connection c : this.connections) {
460                c.updateClient(control);
461                if (isRebalanceClusterClients()) {
462                    control = getConnectionControl();
463                }
464            }
465        }
466    }
467
468    private boolean isMatchesClusterFilter(String brokerName) {
469        boolean result = true;
470        String filter = getUpdateClusterFilter();
471        if (filter != null) {
472            filter = filter.trim();
473            if (filter.length() > 0) {
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 = true;
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 void setAllowLinkStealing (boolean allowLinkStealing) {
606        this.allowLinkStealing=allowLinkStealing;
607    }
608
609    public boolean isAuditNetworkProducers() {
610        return auditNetworkProducers;
611    }
612
613    /**
614     * Enable a producer audit on network connections, Traps the case of a missing send reply and resend.
615     * Note: does not work with conduit=false, networked composite destinations or networked virtual topics
616     * @param auditNetworkProducers
617     */
618    public void setAuditNetworkProducers(boolean auditNetworkProducers) {
619        this.auditNetworkProducers = auditNetworkProducers;
620    }
621
622    public int getMaximumProducersAllowedPerConnection() {
623        return maximumProducersAllowedPerConnection;
624    }
625
626    public void setMaximumProducersAllowedPerConnection(int maximumProducersAllowedPerConnection) {
627        this.maximumProducersAllowedPerConnection = maximumProducersAllowedPerConnection;
628    }
629
630    public int getMaximumConsumersAllowedPerConnection() {
631        return maximumConsumersAllowedPerConnection;
632    }
633
634    public void setMaximumConsumersAllowedPerConnection(int maximumConsumersAllowedPerConnection) {
635        this.maximumConsumersAllowedPerConnection = maximumConsumersAllowedPerConnection;
636    }
637
638    /**
639     * Gets the currently configured policy for creating the published connection address of this
640     * TransportConnector.
641     *
642     * @return the publishedAddressPolicy
643     */
644    public PublishedAddressPolicy getPublishedAddressPolicy() {
645        return publishedAddressPolicy;
646    }
647
648    /**
649     * Sets the configured policy for creating the published connection address of this
650     * TransportConnector.
651     *
652     * @return the publishedAddressPolicy
653     */
654    public void setPublishedAddressPolicy(PublishedAddressPolicy publishedAddressPolicy) {
655        this.publishedAddressPolicy = publishedAddressPolicy;
656    }
657}