/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.server.files;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.core.server.ActiveMQScheduledComponent;
import org.jboss.logging.Logger;
/**
* This will keep a list of fileStores. It will make a comparison on all file stores registered. if any is over the limit,
* all Callbacks will be called with over.
*
* For instance: if Large Messages folder is registered on a different folder and it's over capacity,
* the whole system will be waiting it to be released.
*/
public class FileStoreMonitor extends ActiveMQScheduledComponent {
private static final Logger logger = Logger.getLogger(FileStoreMonitor.class);
private final Set<Callback> callbackList = new HashSet<>();
private final Set<FileStore> stores = new HashSet<>();
private double maxUsage;
public FileStoreMonitor(ScheduledExecutorService scheduledExecutorService,
Executor executor,
long checkPeriod,
TimeUnit timeUnit,
double maxUsage) {
super(scheduledExecutorService, executor, checkPeriod, timeUnit, false);
this.maxUsage = maxUsage;
}
public synchronized FileStoreMonitor addCallback(Callback callback) {
callbackList.add(callback);
return this;
}
public synchronized FileStoreMonitor addStore(File file) throws IOException {
// JDBC storage may return this as null, and we may need to ignore it
if (file != null && file.exists()) {
addStore(Files.getFileStore(file.toPath()));
}
return this;
}
public synchronized FileStoreMonitor addStore(FileStore store) {
stores.add(store);
return this;
}
@Override
public void run() {
tick();
}
public synchronized void tick() {
boolean over = false;
FileStore lastStore = null;
double usage = 0;
for (FileStore store : stores) {
try {
lastStore = store;
usage = calculateUsage(store);
over = usage > maxUsage;
if (over) {
break;
}
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
for (Callback callback : callbackList) {
callback.tick(lastStore, usage);
if (over) {
callback.over(lastStore, usage);
} else {
callback.under(lastStore, usage);
}
}
}
public double getMaxUsage() {
return maxUsage;
}
public FileStoreMonitor setMaxUsage(double maxUsage) {
this.maxUsage = maxUsage;
return this;
}
protected double calculateUsage(FileStore store) throws IOException {
return 1.0 - (double) store.getUsableSpace() / (double) store.getTotalSpace();
}
public interface Callback {
void tick(FileStore store, double usage);
void over(FileStore store, double usage);
void under(FileStore store, double usage);
}
}