com.stimulus.archiva.domain.Volumes.java Source code

Java tutorial

Introduction

Here is the source code for com.stimulus.archiva.domain.Volumes.java

Source

package com.stimulus.archiva.domain;

/* Copyright (C) 2005-2007 Jamie Angus Band 
 * MailArchiva Open Source Edition Copyright (c) 2005-2007 Jamie Angus Band
 * This program is free software; you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

import org.apache.commons.logging.*;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.stimulus.archiva.domain.Volume.Space;
import com.stimulus.archiva.domain.Volume.Status;
import com.stimulus.archiva.exception.*;
import com.stimulus.util.*;
import com.stimulus.archiva.monitor.*;

public class Volumes implements Serializable, Props, Cloneable {

    private static final long serialVersionUID = -1331431169000378587L;

    public enum Priority {
        PRIORITY_HIGHER, PRIORITY_LOWER
    };

    public enum AutoCreateEvent {
        WHENFULL("when full", 0, 0), EVERY3MIN("every 3 minutes", Calendar.MINUTE, 3), //Debug
        MONTHLY("monthly", Calendar.MONTH, 1), QUARTERLY("quarterly", Calendar.MONTH, 3), YEARLY("yearly",
                Calendar.YEAR, 1);

        private String label;
        /**
         * eg. Calendar.MONTH
         */
        private int intervalField;
        private int intervalAmount;

        private AutoCreateEvent(String label, int intervalField, int intervalAmount) {
            this.label = label;
            this.intervalField = intervalField;
            this.intervalAmount = intervalAmount;
        }

        public String getLabel() {
            return label;
        }

        public String getValue() {
            return name();
        }
    };

    protected static final Log logger = LogFactory.getLog(Volumes.class);
    protected int diskSpaceCheckWait = 24;
    protected int diskSpaceWarn = 50;
    protected int diskSpaceThreshold = 0;
    protected boolean diskSpaceChecking = true;
    protected boolean autoCreate = false;
    protected AutoCreateEvent autoCreateEvent = AutoCreateEvent.WHENFULL;
    protected LinkedList<Volume> volumes = new LinkedList<Volume>();

    public static final String INFO_FILE = "volumeinfo";

    protected static final String autoCreateKey = "volume.autocreate";
    protected static final String autoCreateEventKey = "volume.autocreate.event";
    protected static final String diskSpaceCheckWaitKey = "volume.diskspace.interval";
    protected static final String diskSpaceWarnKey = "volume.diskspace.warning";
    protected static final String diskSpaceThresholdKey = "volume.diskspace.limit"; // mb
    protected static final String diskSpaceCheckKey = "volume.diskspace.check"; // mb
    protected static final String defaultDiskSpaceCheckWait = "24"; // hours
    protected static final String defaultDiskSpaceWarn = "50"; // megabytes
    protected static final String defaultDiskSpaceThreshold = "0"; // megabytes
    protected static final String defaultDiskSpaceCheck = "yes";
    protected static final String defaultMessageStorePath = Character.toString(File.separatorChar) + "store";
    protected static final String defaultSearchIndexPath = Character.toString(File.separatorChar) + "index";
    protected static final int defaultVolumeMaxSize = 30000;
    protected static final String defaultAutoCreate = "no";
    protected static final String defaultAutoCreateEvent = "whenfull";

    public Volumes() {
    }

    public Volumes(boolean diskSpaceChecking, int diskSpaceCheckWait, int diskSpaceWarn, int diskSpaceThreshold) {
        this.diskSpaceCheckWait = diskSpaceCheckWait;
        this.diskSpaceWarn = diskSpaceWarn;
        this.diskSpaceThreshold = diskSpaceThreshold;
        this.diskSpaceChecking = diskSpaceChecking;
    }

    public int getDefaultVolumeMaxSize() {
        return defaultVolumeMaxSize;
    }

    public void setDiskSpaceChecking(boolean diskSpaceChecking) {
        this.diskSpaceChecking = diskSpaceChecking;
    }

    public void setDiskSpaceWarn(int diskSpaceWarn) {
        this.diskSpaceWarn = diskSpaceWarn;
    }

    public long getDiskSpaceWarnBytes() {
        return this.diskSpaceWarn * 1024 * 1024;
    }

    public void setDiskSpaceThreshold(int diskSpaceThresholdBytes) {
        this.diskSpaceThreshold = diskSpaceThresholdBytes;
    }

    public boolean getDiskSpaceChecking() {
        return diskSpaceChecking;
    }

    public int getDiskSpaceCheckWait() {
        return diskSpaceCheckWait;
    }

    public void setDiskSpaceCheckWait(int diskSpaceCheckWait) {
        this.diskSpaceCheckWait = diskSpaceCheckWait;
    }

    public int getDiskSpaceWarn() {
        return diskSpaceWarn;
    }

    public int getDiskSpaceThreshold() {
        return diskSpaceThreshold;
    }

    public long getDiskSpaceThresholdBytes() {
        return diskSpaceThreshold * 1024 * 1024;
    }

    public AutoCreateEvent getAutoCreateEvent() {
        return autoCreateEvent;
    }

    public void setAutoCreateEvent(AutoCreateEvent autoCreateEvent) {
        this.autoCreateEvent = autoCreateEvent;
    }

    public boolean getAutoCreate() {
        return autoCreate;
    }

    public void setAutoCreate(boolean autoCreate) {
        this.autoCreate = autoCreate;
    }

    public Volume addVolume(String path, String indexPath, long maxSize, boolean allowRemoteSearch)
            throws ConfigurationException {
        Volume mv = new Volume(path, indexPath, maxSize, allowRemoteSearch);
        addVolume(mv);
        return mv;
    }

    public synchronized Volume newVolume() throws ConfigurationException {

        String storePath;
        String indexPath;

        boolean windows = (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).indexOf("windows") != -1);
        if (windows) {
            storePath = File.separator + "store" + File.separator + "store0";
            indexPath = File.separator + "index" + File.separator + "store0";
        } else {
            storePath = File.separator + "var" + File.separator + "store" + File.separator + "store0";
            indexPath = File.separator + "var" + File.separator + "index" + File.separator + "index0";
        }

        long volumeSize = getDefaultVolumeMaxSize();

        int maxno = 0;
        Pattern pattern = Pattern.compile("(.*[a-zA-z])([0-9]+)");
        for (Volume volume : volumes) {
            String sp = volume.getPath();
            String ip = volume.getIndexPath();
            Matcher matcher = pattern.matcher(sp);
            Matcher matcher2 = pattern.matcher(ip);
            if (matcher.find() && matcher2.find()) {
                int digit = Integer.parseInt(matcher.group(2));
                if (digit >= maxno) {
                    digit++;
                    storePath = matcher.group(1) + (digit);
                    indexPath = matcher2.group(1) + (digit);
                    volumeSize = volume.getMaxSize();
                    maxno = digit;

                }
            }
        }

        return addVolume(storePath, indexPath, volumeSize, false);
    }

    public synchronized void createNewVolumeIfNone() throws ConfigurationException {
        if (getVolume(Volume.Status.UNUSED) == null && getVolume(Volume.Status.NEW) == null) {
            logger.debug("no unused volume. creating one.");
            Volume newVolume = newVolume();
            logger.debug("saving new volume:" + newVolume.path + "," + newVolume.getStatus());
            newVolume.save();
        }
    }

    public synchronized void addVolume(Volume volume) {
        volumes.add(volume);
        Collections.sort(volumes);
    }

    public synchronized void saveAllVolumeInfo(boolean overwrite) throws ConfigurationException {
        Config.getConfig().getVolumeIRService().block();
        logger.debug("saveAllVolumeInfo()");
        for (Volume v : volumes) {
            v.save();
        }
        Config.getConfig().getVolumeIRService().unblock();
    }

    public synchronized void loadAllVolumeInfo() throws ConfigurationException {
        Config.getConfig().getVolumeIRService().block();
        logger.debug("loadAllVolumeInfo()");
        for (Volume v : volumes) {
            v.load();
        }
        Config.getConfig().getVolumeIRService().unblock();
    }

    public synchronized void readActiveVolumeStatus(Email email) throws ConfigurationException {
        Volume v = getVolume(Volume.Status.ACTIVE);
        if (v == null)
            throw new ConfigurationException("failed to touch active volume. no volume is active.", logger);
    }

    public synchronized Volume removeVolume(int id) throws ConfigurationException {
        Volume v = volumes.get(id);
        if (v.getStatus() == Volume.Status.ACTIVE)
            throw new ConfigurationException("failed to delete active volume. it must be closed first", logger);
        volumes.remove(v);
        Collections.sort(volumes);
        return v;
    }

    public synchronized int indexOfVolume(Volume v) {
        return volumes.indexOf(v);
    }

    public synchronized List<Volume> getVolumes() {
        return (List<Volume>) volumes.clone(); // want to avoid synchronization issues
    }

    public synchronized Volume getVolume(int index) {
        return volumes.get(index);
    }

    public synchronized void setVolumePriority(int id, Priority priority) {
        LinkedList<Volume> list = volumes;
        Volume v = list.get(id);

        if (v.getStatus() != Volume.Status.UNUSED) // can only reorder unused
            return;

        if (priority == Priority.PRIORITY_HIGHER && id - 1 >= 0) { // cannot affect non unused vols
            Volume vs = list.get(id - 1);
            if (vs.getStatus() != Volume.Status.UNUSED)
                return;
        }

        list.remove(v);
        switch (priority) {
        case PRIORITY_HIGHER:
            if ((id - 1) <= 0)
                list.addFirst(v);
            else
                list.add(id - 1, v);
            break;
        case PRIORITY_LOWER:
            if ((id + 1) >= list.size())
                list.addLast(v);
            else
                list.add(id + 1, v);
            break;

        }
    }

    public synchronized void clearAllVolumes() {
        volumes.clear();
    }

    public synchronized Volume getNewVolume(String id) throws ConfigurationException {
        for (Volume volume : volumes) {
            if (volume.getStatus() == Volume.Status.UNUSED)
                throw new ConfigurationException("failed to lookup new volumes {id='" + id + "'}", logger,
                        ChainedException.Level.DEBUG);
            if (Compare.equalsIgnoreCase(volume.getID(), id))
                return volume;
        }
        // backup plan - try search using modified date
        for (Volume volume : volumes) {
            if (Compare.equalsIgnoreCase(DateUtil.convertDatetoString(volume.getClosedDate()), id))
                return volume;
        }
        throw new ConfigurationException("failed to lookup a volume from index information {id='" + id + "'}",
                logger, ChainedException.Level.DEBUG);
    }

    public synchronized Volume getLegacyVolume(String uniqueId) throws ConfigurationException {
        if (uniqueId == null)
            throw new ConfigurationException("uniqueid cannot be null", logger);

        Date date = null;
        try {
            date = DateUtil.convertStringToDate(uniqueId);
        } catch (Exception e) {
            logger.warn("failed to parse uniqueid {id='" + uniqueId + "'}");
        }

        for (Volume volume : volumes) {
            if (volume.getStatus() == Volume.Status.UNUSED)
                throw new ConfigurationException("failed to lookup legacy volumes {uniqueId='" + uniqueId + "'}",
                        logger, ChainedException.Level.DEBUG);
            if (volume != null && volume.getCreatedDate() != null && volume.getClosedDate().compareTo(date) >= 0) {
                return volume;

            }
        }
        return null;
    }

    public synchronized Volume closeVolume(Volume volume) throws ConfigurationException {
        volume.setStatus(Volume.Status.CLOSED);
        volume.setClosedDate(new Date());
        logger.debug("volume is now closed {" + volume + "}");
        return volume;
    }

    public synchronized void closeVolume(int index) throws ConfigurationException {
        Volume volume = volumes.get(index);
        if (volume == null)
            throw new ConfigurationException("failed to close volume. no such volume exists", logger);
        closeVolume(volume);
    }

    public synchronized Volume activateVolume(Volume volume) throws ConfigurationException {
        volume.readyFileSystem();
        volume.setStatus(Volume.Status.ACTIVE); // make the new one active
        volume.setCreatedDate(new Date());
        Collections.sort(volumes);
        volume.save();
        Config.getConfig().saveConfiguration(MailArchivaPrincipal.SYSTEM_PRINCIPAL);
        return volume;
    }

    public synchronized void unmountVolume(int index) throws ConfigurationException {
        Object o = volumes.get(index);
        if (o == null)
            throw new ConfigurationException("failed to unmount volume. no such volume exists", logger);
        Volume vs = (Volume) o;
        vs.setStatus(Volume.Status.UNMOUNTED);
        Collections.sort(volumes);
        vs.save();
        logger.debug("volume is now unmounted {" + vs + "}");
    }

    public synchronized Volume getVolume(Volume.Status status) {
        for (Volume volume : volumes) {
            if (volume.getStatus() == status) {
                //logger.debug("getActiveVolume() {"+volume+"}");
                return volume;
            }
        }
        return null;
    }

    public synchronized void activateUnusedVolume() {
        Volume unusedVolume = getVolume(Volume.Status.UNUSED);
        if (unusedVolume != null) {
            try {
                activateVolume(unusedVolume);
            } catch (ConfigurationException e) {
                logger.error("failed to active volume:" + e.getMessage() + " {" + unusedVolume + "}", e);
            }
        }
    }

    public synchronized void readyActiveVolume() {
        try {
            spaceCheck();
        } catch (ConfigurationException ce) {
            logger.error("error occurred during space check procedure:" + ce.getMessage(), ce);
        }
        Volume activeVolume = getVolume(Volume.Status.ACTIVE);
        if (activeVolume == null) {
            activateUnusedVolume();
        }
    }

    public synchronized void spaceCheck() throws ConfigurationException {
        Volume activeVolume = getVolume(Volume.Status.ACTIVE);
        AutoCreateEvent autoCreateEvent = Config.getConfig().getVolumes().getAutoCreateEvent();
        if (activeVolume != null) {
            if (Config.getConfig().getVolumes().getAutoCreate()
                    && autoCreateEvent != Volumes.AutoCreateEvent.WHENFULL
                    && shouldRotateActive(activeVolume, autoCreateEvent)) {
                closeVolume(activeVolume);
                activateUnusedVolume();
                Volume newVolume = newVolume();
                newVolume.save();
                saveAllVolumeInfo(false);
                // createNewVolumeIfNone();
                return;
            }
            switch (activeVolume.enoughDiskSpace()) {
            case ENOUGH:
                // logger.debug("volume has enough disk space {"+activeVolume+"}");
                break;
            case LOW:
                if (Config.getConfig().getVolumes().getAutoCreate()
                        && autoCreateEvent == Volumes.AutoCreateEvent.WHENFULL) {
                    createNewVolumeIfNone();
                }
                break;
            case RUNOUT:
                logger.info("closing volume. volume has run out of disk space. {" + activeVolume + "}");
                closeVolume(activeVolume);
                activateUnusedVolume();
                saveAllVolumeInfo(false);
                break;
            }
        } else {
            if (Config.getConfig().getVolumes().getAutoCreate()) {
                createNewVolumeIfNone();
            }
        }
    }

    private boolean shouldRotateActive(Volume activeVolume, AutoCreateEvent event) {
        Date baseDate = activeVolume.getCreatedDate();
        // Check volumes from last to first
        for (ListIterator<Volume> li = getVolumes().listIterator(getVolumes().size()); li.hasPrevious();) {
            Volume vol = li.previous();
            if (vol.getStatus() != Status.CLOSED) { // Skip unclosed volumes
                continue;
            }

            if (vol.enoughDiskSpace() != Space.RUNOUT) { // Scheduled close
                baseDate = vol.getClosedDate();
                break;
            } else {
                // Unscheduled
                baseDate = vol.getCreatedDate();
            }
        }

        Date now = new Date();
        Calendar dateToClose = Calendar.getInstance();
        dateToClose.setTime(baseDate);
        dateToClose.add(event.intervalField, event.intervalAmount);
        return now.after(dateToClose.getTime());
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
    }

    public void saveSettings(String prefix, Settings prop, String suffix) {
        logger.debug("saving volumes");
        int c = 1;
        for (Volume volume : volumes) {
            volume.saveSettings(null, prop, "." + c++);
        }

        prop.setProperty(diskSpaceCheckWaitKey, Integer.toString(getDiskSpaceCheckWait()));
        prop.setProperty(diskSpaceWarnKey, Integer.toString(getDiskSpaceWarn()));
        prop.setProperty(diskSpaceThresholdKey, Integer.toString(getDiskSpaceThreshold()));
        prop.setProperty(diskSpaceCheckKey, ConfigUtil.getYesNo(getDiskSpaceChecking()));
        prop.setProperty(autoCreateKey, ConfigUtil.getYesNo(getAutoCreate()));
        prop.setProperty(autoCreateEventKey, getAutoCreateEvent().toString().toLowerCase(Locale.ENGLISH));

    }

    public boolean loadSettings(String prefix, Settings prop, String suffix) {
        logger.debug("loading volumes");
        clearAllVolumes();
        int c = 1;
        boolean load = false;
        do {
            Volume vol = new Volume();
            load = vol.loadSettings(null, prop, "." + c++);
            if (load) {
                addVolume(vol);
            }
        } while (load);

        int diskSpaceCheckWait = ConfigUtil.getInteger(prop.getProperty(diskSpaceCheckWaitKey),
                defaultDiskSpaceCheckWait);
        int diskSpaceWarn = ConfigUtil.getInteger(prop.getProperty(diskSpaceWarnKey), defaultDiskSpaceWarn);
        int diskSpaceThreshold = ConfigUtil.getInteger(prop.getProperty(diskSpaceThresholdKey),
                defaultDiskSpaceThreshold);
        boolean diskSpaceCheck = ConfigUtil.getBoolean(prop.getProperty(diskSpaceCheckKey), defaultDiskSpaceCheck);

        setDiskSpaceChecking(diskSpaceCheck);
        setDiskSpaceThreshold(diskSpaceThreshold);
        setDiskSpaceWarn(diskSpaceWarn);
        setDiskSpaceCheckWait(diskSpaceCheckWait);

        String newAutoCreateEvent = AutoCreateEvent.WHENFULL.toString().toLowerCase(Locale.ENGLISH);
        try {
            newAutoCreateEvent = prop.getProperty(autoCreateEventKey);
            if (newAutoCreateEvent == null) {
                logger.info("config load: new auto create was not found in server.conf. defaulting to whenfull.");
                autoCreateEvent = AutoCreateEvent.WHENFULL;
            } else
                autoCreateEvent = AutoCreateEvent.valueOf(newAutoCreateEvent.trim().toUpperCase(Locale.ENGLISH));

        } catch (IllegalArgumentException iae) {
            logger.error(
                    "failed to set auto create event field. auto create event is set to an illegal value {field='"
                            + newAutoCreateEvent + "'}");
            logger.info("auto create event is set to 'to' by default (error recovery)");
        }

        boolean autoCreate = ConfigUtil.getBoolean(prop.getProperty(autoCreateKey), defaultAutoCreate);
        setAutoCreate(autoCreate);
        return true;
    }

    public void out(String heading) {
        for (Volume v : volumes) {
            System.out.println(v.toString());
        }
    }

    public Volumes clone() {
        Volumes volumes = new Volumes();
        volumes.setDiskSpaceCheckWait(getDiskSpaceCheckWait());
        volumes.setDiskSpaceWarn(getDiskSpaceWarn());
        volumes.setDiskSpaceThreshold(getDiskSpaceThreshold());
        volumes.setDiskSpaceChecking(getDiskSpaceChecking());
        volumes.setAutoCreate(getAutoCreate());
        volumes.setAutoCreateEvent(getAutoCreateEvent());
        for (Volume sourceVolume : getVolumes()) {
            volumes.addVolume(sourceVolume.clone());
        }
        return volumes;
    }

}