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.jmx;
018
019import java.io.File;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023
024import javax.management.ObjectName;
025import javax.management.openmbean.CompositeDataSupport;
026import javax.management.openmbean.CompositeType;
027import javax.management.openmbean.TabularData;
028import javax.management.openmbean.TabularDataSupport;
029import javax.management.openmbean.TabularType;
030
031import org.apache.activemq.broker.BrokerService;
032import org.apache.activemq.broker.scheduler.JobSchedulerStore;
033import org.apache.activemq.store.PersistenceAdapter;
034import org.apache.activemq.usage.SystemUsage;
035
036public class HealthView implements HealthViewMBean {
037
038    private ManagedRegionBroker broker;
039    private volatile String currentState = "Good";
040
041    public HealthView(ManagedRegionBroker broker) {
042        this.broker = broker;
043    }
044
045    @Override
046    public TabularData health() throws Exception {
047        OpenTypeSupport.OpenTypeFactory factory = OpenTypeSupport.getFactory(HealthStatus.class);
048        CompositeType ct = factory.getCompositeType();
049        TabularType tt = new TabularType("HealthStatus", "HealthStatus", ct, new String[] { "healthId", "level", "message", "resource" });
050        TabularDataSupport rc = new TabularDataSupport(tt);
051
052        List<HealthStatus> list = healthList();
053        for (HealthStatus healthStatus : list) {
054            rc.put(new CompositeDataSupport(ct, factory.getFields(healthStatus)));
055        }
056        return rc;
057    }
058
059    @Override
060    public List<HealthStatus> healthList() throws Exception {
061        List<HealthStatus> answer = new ArrayList<HealthStatus>();
062        Map<ObjectName, DestinationView> queueViews = broker.getQueueViews();
063        for (Map.Entry<ObjectName, DestinationView> entry : queueViews.entrySet()) {
064            DestinationView queue = entry.getValue();
065            if (queue.getConsumerCount() == 0 && queue.getProducerCount() > 0) {
066                ObjectName key = entry.getKey();
067                String message = "Queue " + queue.getName() + " has no consumers";
068                answer.add(new HealthStatus("org.apache.activemq.noConsumer", "WARNING", message, key.toString()));
069            }
070        }
071
072        /**
073         * Check persistence store directory limits
074         */
075        BrokerService brokerService = broker.getBrokerService();
076        if (brokerService != null && brokerService.getPersistenceAdapter() != null) {
077            PersistenceAdapter adapter = brokerService.getPersistenceAdapter();
078            File dir = adapter.getDirectory();
079            if (brokerService.isPersistent()) {
080                SystemUsage usage = brokerService.getSystemUsage();
081                if (dir != null && usage != null) {
082                    String dirPath = dir.getAbsolutePath();
083                    if (!dir.isAbsolute()) {
084                        dir = new File(dirPath);
085                    }
086
087                    while (dir != null && !dir.isDirectory()) {
088                        dir = dir.getParentFile();
089                    }
090
091                    long storeSize = adapter.size();
092                    long storeLimit = usage.getStoreUsage().getLimit();
093                    long dirFreeSpace = dir.getUsableSpace();
094
095                    if (storeSize != 0 && storeLimit != 0) {
096                        int val = (int) ((storeSize * 100) / storeLimit);
097                        if (val > 90) {
098                            answer.add(new HealthStatus("org.apache.activemq.StoreLimit", "WARNING", "Message Store size is within " + val + "% of its limit",
099                                adapter.toString()));
100                        }
101                    }
102
103                    if ((storeLimit - storeSize) > dirFreeSpace) {
104                        String message = "Store limit is " + storeLimit / (1024 * 1024) + " mb, whilst the data directory: " + dir.getAbsolutePath()
105                            + " only has " + dirFreeSpace / (1024 * 1024) + " mb of usable space";
106                        answer.add(new HealthStatus("org.apache.activemq.FreeDiskSpaceLeft", "WARNING", message, adapter.toString()));
107                    }
108                }
109
110                File tmpDir = brokerService.getTmpDataDirectory();
111                if (tmpDir != null) {
112
113                    String tmpDirPath = tmpDir.getAbsolutePath();
114                    if (!tmpDir.isAbsolute()) {
115                        tmpDir = new File(tmpDirPath);
116                    }
117
118                    long storeSize = usage.getTempUsage().getUsage();
119                    long storeLimit = usage.getTempUsage().getLimit();
120                    while (tmpDir != null && !tmpDir.isDirectory()) {
121                        tmpDir = tmpDir.getParentFile();
122                    }
123
124                    if (storeLimit != 0) {
125                        int val = (int) ((storeSize * 100) / storeLimit);
126                        if (val > 90) {
127                            answer.add(new HealthStatus("org.apache.activemq.TempStoreLimit", "WARNING", "TempMessage Store size is within " + val
128                                + "% of its limit", adapter.toString()));
129                        }
130                    }
131                }
132            }
133        }
134
135        if (brokerService != null && brokerService.getJobSchedulerStore() != null) {
136            JobSchedulerStore scheduler = brokerService.getJobSchedulerStore();
137            File dir = scheduler.getDirectory();
138            if (brokerService.isPersistent()) {
139                SystemUsage usage = brokerService.getSystemUsage();
140                if (dir != null && usage != null) {
141                    String dirPath = dir.getAbsolutePath();
142                    if (!dir.isAbsolute()) {
143                        dir = new File(dirPath);
144                    }
145
146                    while (dir != null && !dir.isDirectory()) {
147                        dir = dir.getParentFile();
148                    }
149                    long storeSize = scheduler.size();
150                    long storeLimit = usage.getJobSchedulerUsage().getLimit();
151                    long dirFreeSpace = dir.getUsableSpace();
152
153                    if (storeSize != 0 && storeLimit != 0) {
154                        int val = (int) ((storeSize * 100) / storeLimit);
155                        if (val > 90) {
156                            answer.add(new HealthStatus("org.apache.activemq.JobSchedulerLimit", "WARNING", "JobSchedulerMessage Store size is within " + val
157                                + "% of its limit", scheduler.toString()));
158                        }
159                    }
160
161                    if ((storeLimit - storeSize) > dirFreeSpace) {
162                        String message = "JobSchedulerStore limit is " + storeLimit / (1024 * 1024) + " mb, whilst the data directory: "
163                            + dir.getAbsolutePath() + " only has " + dirFreeSpace / (1024 * 1024) + " mb of usable space";
164                        answer.add(new HealthStatus("org.apache.activemq.FreeDiskSpaceLeft", "WARNING", message, scheduler.toString()));
165                    }
166                }
167            }
168        }
169
170        StringBuilder currentState = new StringBuilder();
171        if (answer != null && !answer.isEmpty()) {
172            currentState.append("Getting Worried {");
173            for (HealthStatus hs : answer) {
174                currentState.append(hs).append(" , ");
175            }
176            currentState.append(" }");
177        } else {
178            currentState.append("Good");
179        }
180
181        this.currentState = currentState.toString();
182
183        return answer;
184    }
185
186    @Override
187    public String healthStatus() throws Exception {
188        // Must invoke healthList in order to update state.
189        healthList();
190
191        return getCurrentStatus();
192    }
193
194    /**
195     * @return String representation of the current Broker state
196     */
197    @Override
198    public String getCurrentStatus() {
199        return this.currentState;
200    }
201}