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.util;
018
019import java.io.IOException;
020import java.sql.SQLException;
021import java.util.Map;
022import java.util.concurrent.TimeUnit;
023import java.util.concurrent.atomic.AtomicBoolean;
024
025import org.apache.activemq.broker.BrokerService;
026import org.apache.activemq.broker.SuppressReplyException;
027import org.apache.activemq.broker.region.Destination;
028import org.apache.activemq.broker.region.Queue;
029import org.apache.activemq.broker.region.RegionBroker;
030import org.apache.activemq.command.ActiveMQDestination;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * @org.apache.xbean.XBean
036 */
037 public class DefaultIOExceptionHandler implements IOExceptionHandler {
038
039    private static final Logger LOG = LoggerFactory
040            .getLogger(DefaultIOExceptionHandler.class);
041    protected BrokerService broker;
042    private boolean ignoreAllErrors = false;
043    private boolean ignoreNoSpaceErrors = true;
044    private boolean ignoreSQLExceptions = true;
045    private boolean stopStartConnectors = false;
046    private String noSpaceMessage = "space";
047    private String sqlExceptionMessage = ""; // match all
048    private long resumeCheckSleepPeriod = 5*1000;
049    private AtomicBoolean handlingException = new AtomicBoolean(false);
050
051    public void handle(IOException exception) {
052        if (ignoreAllErrors) {
053            LOG.info("Ignoring IO exception, " + exception, exception);
054            return;
055        }
056
057        if (ignoreNoSpaceErrors) {
058            Throwable cause = exception;
059            while (cause != null && cause instanceof IOException) {
060                String message = cause.getMessage();
061                if (message != null && message.contains(noSpaceMessage)) {
062                    LOG.info("Ignoring no space left exception, " + exception, exception);
063                    return;
064                }
065                cause = cause.getCause();
066            }
067        }
068
069        if (ignoreSQLExceptions) {
070            Throwable cause = exception;
071            while (cause != null) {
072                String message = cause.getMessage();
073                if (cause instanceof SQLException && message.contains(sqlExceptionMessage)) {
074                    LOG.info("Ignoring SQLException, " + exception, cause);
075                    return;
076                }
077                cause = cause.getCause();
078            }
079        }
080
081        if (stopStartConnectors) {
082            if (handlingException.compareAndSet(false, true)) {
083                LOG.info("Initiating stop/restart of transports on " + broker + " due to IO exception, " + exception, exception);
084
085                new Thread("IOExceptionHandler: stop transports") {
086                    public void run() {
087                        try {
088                            ServiceStopper stopper = new ServiceStopper();
089                            broker.stopAllConnectors(stopper);
090                            LOG.info("Successfully stopped transports on " + broker);
091                        } catch (Exception e) {
092                            LOG.warn("Failure occurred while stopping broker connectors", e);
093                        } finally {
094                            // resume again
095                            new Thread("IOExceptionHandler: restart transports") {
096                                public void run() {
097                                    try {
098                                        while (hasLockOwnership() && isPersistenceAdapterDown()) {
099                                            LOG.info("waiting for broker persistence adapter checkpoint to succeed before restarting transports");
100                                            TimeUnit.MILLISECONDS.sleep(resumeCheckSleepPeriod);
101                                        }
102                                        if (hasLockOwnership()) {
103                                            Map<ActiveMQDestination, Destination> destinations = ((RegionBroker)broker.getRegionBroker()).getDestinationMap();
104                                            for (Destination destination : destinations.values()) {
105
106                                                if (destination instanceof Queue) {
107                                                    Queue queue = (Queue)destination;
108                                                    if (queue.isResetNeeded()) {
109                                                        queue.clearPendingMessages();
110                                                    }
111                                                }
112                                            }
113                                            broker.startAllConnectors();
114                                            LOG.info("Successfully restarted transports on " + broker);
115                                        }
116                                    } catch (Exception e) {
117                                        LOG.warn("Stopping " + broker + " due to failure restarting transports", e);
118                                        stopBroker(e);
119                                    } finally {
120                                        handlingException.compareAndSet(true, false);
121                                    }
122                                }
123
124                                private boolean isPersistenceAdapterDown() {
125                                    boolean checkpointSuccess = false;
126                                    try {
127                                        broker.getPersistenceAdapter().checkpoint(true);
128                                        checkpointSuccess = true;
129                                    } catch (Throwable ignored) {
130                                    }
131                                    return !checkpointSuccess;
132                                }
133                            }.start();
134
135
136                        }
137                    }
138                }.start();
139            }
140
141            throw new SuppressReplyException("Stop/RestartTransportsInitiated", exception);
142        }
143
144        if (handlingException.compareAndSet(false, true)) {
145            stopBroker(exception);
146        }
147
148        // we don't want to propagate the exception back to the client
149        // They will see a delay till they see a disconnect via socket.close
150        // at which point failover: can kick in.
151        throw new SuppressReplyException("ShutdownBrokerInitiated", exception);
152    }
153
154    private void stopBroker(Exception exception) {
155        LOG.info("Stopping " + broker + " due to exception, " + exception, exception);
156        new Thread("IOExceptionHandler: stopping " + broker) {
157            public void run() {
158                try {
159                    if( broker.isRestartAllowed() ) {
160                        broker.requestRestart();
161                    }
162                    broker.stop();
163                } catch (Exception e) {
164                    LOG.warn("Failure occurred while stopping broker", e);
165                }
166            }
167        }.start();
168    }
169
170    protected boolean hasLockOwnership() throws IOException {
171        return true;
172    }
173
174    public void setBrokerService(BrokerService broker) {
175        this.broker = broker;
176    }
177
178    public boolean isIgnoreAllErrors() {
179        return ignoreAllErrors;
180    }
181
182    public void setIgnoreAllErrors(boolean ignoreAllErrors) {
183        this.ignoreAllErrors = ignoreAllErrors;
184    }
185
186    public boolean isIgnoreNoSpaceErrors() {
187        return ignoreNoSpaceErrors;
188    }
189
190    public void setIgnoreNoSpaceErrors(boolean ignoreNoSpaceErrors) {
191        this.ignoreNoSpaceErrors = ignoreNoSpaceErrors;
192    }
193
194    public String getNoSpaceMessage() {
195        return noSpaceMessage;
196    }
197
198    public void setNoSpaceMessage(String noSpaceMessage) {
199        this.noSpaceMessage = noSpaceMessage;
200    }
201
202    public boolean isIgnoreSQLExceptions() {
203        return ignoreSQLExceptions;
204    }
205
206    public void setIgnoreSQLExceptions(boolean ignoreSQLExceptions) {
207        this.ignoreSQLExceptions = ignoreSQLExceptions;
208    }
209
210    public String getSqlExceptionMessage() {
211        return sqlExceptionMessage;
212    }
213
214    public void setSqlExceptionMessage(String sqlExceptionMessage) {
215        this.sqlExceptionMessage = sqlExceptionMessage;
216    }
217
218    public boolean isStopStartConnectors() {
219        return stopStartConnectors;
220    }
221
222    public void setStopStartConnectors(boolean stopStartConnectors) {
223        this.stopStartConnectors = stopStartConnectors;
224    }
225
226    public long getResumeCheckSleepPeriod() {
227        return resumeCheckSleepPeriod;
228    }
229
230    public void setResumeCheckSleepPeriod(long resumeCheckSleepPeriod) {
231        this.resumeCheckSleepPeriod = resumeCheckSleepPeriod;
232    }
233}