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 org.apache.activemq.store.PersistenceAdapter;
020import org.apache.activemq.util.ServiceStopper;
021import org.apache.activemq.util.ServiceSupport;
022import org.apache.activemq.util.ThreadPoolUtils;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import java.io.IOException;
027import java.util.concurrent.ScheduledFuture;
028import java.util.concurrent.ScheduledThreadPoolExecutor;
029import java.util.concurrent.ThreadFactory;
030import java.util.concurrent.TimeUnit;
031
032/**
033 * Helper class for working with services that requires locking
034 */
035public abstract class LockableServiceSupport extends ServiceSupport implements Lockable, BrokerServiceAware {
036
037    private static final Logger LOG = LoggerFactory.getLogger(LockableServiceSupport.class);
038    boolean useLock = true;
039    boolean stopOnError = false;
040    Locker locker;
041    long lockKeepAlivePeriod = 0;
042    private ScheduledFuture<?> keepAliveTicket;
043    protected ScheduledThreadPoolExecutor clockDaemon;
044    protected BrokerService brokerService;
045
046    /**
047     * Initialize resources before locking
048     *
049     * @throws Exception
050     */
051    abstract public void init() throws Exception;
052
053    @Override
054    public void setUseLock(boolean useLock) {
055        this.useLock = useLock;
056    }
057
058    public boolean isUseLock() {
059        return this.useLock;
060    }
061
062    @Override
063    public void setStopOnError(boolean stopOnError) {
064        this.stopOnError = stopOnError;
065    }
066
067    public boolean isStopOnError() {
068        return this.stopOnError;
069    }
070
071    @Override
072    public void setLocker(Locker locker) throws IOException {
073        this.locker = locker;
074        locker.setLockable(this);
075        if (this instanceof PersistenceAdapter) {
076            this.locker.configure((PersistenceAdapter)this);
077        }
078    }
079
080    public Locker getLocker() throws IOException {
081        if (this.locker == null) {
082            setLocker(createDefaultLocker());
083        }
084        return this.locker;
085    }
086
087    @Override
088    public void setLockKeepAlivePeriod(long lockKeepAlivePeriod) {
089        this.lockKeepAlivePeriod = lockKeepAlivePeriod;
090    }
091
092    @Override
093    public long getLockKeepAlivePeriod() {
094        return lockKeepAlivePeriod;
095    }
096
097    @Override
098    public void preStart() throws Exception {
099        init();
100        if (useLock) {
101            if (getLocker() == null) {
102                LOG.warn("No locker configured");
103            } else {
104                getLocker().start();
105                if (lockKeepAlivePeriod > 0) {
106                    keepAliveTicket = getScheduledThreadPoolExecutor().scheduleAtFixedRate(new Runnable() {
107                        public void run() {
108                            keepLockAlive();
109                        }
110                    }, lockKeepAlivePeriod, lockKeepAlivePeriod, TimeUnit.MILLISECONDS);
111                }
112            }
113        }
114    }
115
116    @Override
117    public void postStop(ServiceStopper stopper) throws Exception {
118        if (useLock) {
119            if (keepAliveTicket != null) {
120                keepAliveTicket.cancel(false);
121                keepAliveTicket = null;
122            }
123            if (locker != null) {
124                getLocker().stop();
125                locker = null;
126            }
127        }
128        ThreadPoolUtils.shutdown(clockDaemon);
129        clockDaemon = null;
130    }
131
132    protected void keepLockAlive() {
133        boolean stop = false;
134        try {
135            Locker locker = getLocker();
136            if (locker != null) {
137                if (!locker.keepAlive()) {
138                    stop = true;
139                }
140            }
141        } catch (SuppressReplyException e) {
142            if (stopOnError) {
143                stop = true;
144            }
145            LOG.warn("locker keepAlive resulted in", e);
146        } catch (IOException e) {
147            if (stopOnError) {
148                stop = true;
149            }
150            LOG.warn("locker keepAlive resulted in", e);
151        }
152        if (stop) {
153            stopBroker();
154        }
155    }
156
157    protected void stopBroker() {
158        // we can no longer keep the lock so lets fail
159        LOG.error("{}, no longer able to keep the exclusive lock so giving up being a master", brokerService.getBrokerName());
160        try {
161            if( brokerService.isRestartAllowed() ) {
162                brokerService.requestRestart();
163            }
164            brokerService.stop();
165        } catch (Exception e) {
166            LOG.warn("Failure occurred while stopping broker");
167        }
168    }
169
170    public ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor() {
171        if (clockDaemon == null) {
172            clockDaemon = new ScheduledThreadPoolExecutor(5, new ThreadFactory() {
173                public Thread newThread(Runnable runnable) {
174                    Thread thread = new Thread(runnable, "ActiveMQ Lock KeepAlive Timer");
175                    thread.setDaemon(true);
176                    return thread;
177                }
178            });
179        }
180        return clockDaemon;
181    }
182
183    public void setScheduledThreadPoolExecutor(ScheduledThreadPoolExecutor clockDaemon) {
184        this.clockDaemon = clockDaemon;
185    }
186
187    @Override
188    public void setBrokerService(BrokerService brokerService) {
189        this.brokerService = brokerService;
190    }
191
192    public BrokerService getBrokerService() {
193        return this.brokerService;
194    }
195}