001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.joran.spi;
015
016import ch.qos.logback.core.spi.ContextAwareBase;
017
018import java.io.File;
019import java.net.URL;
020import java.net.URLDecoder;
021import java.util.ArrayList;
022import java.util.List;
023
024import static ch.qos.logback.core.CoreConstants.PROPERTIES_FILE_EXTENSION;
025
026/**
027 * @author Ceki Gülcü
028 */
029public class ConfigurationWatchList extends ContextAwareBase {
030
031    URL mainURL;
032    List<File> fileWatchList = new ArrayList<File>();
033    List<Long> lastModifiedList = new ArrayList<Long>();
034
035    public ConfigurationWatchList buildClone() {
036        ConfigurationWatchList out = new ConfigurationWatchList();
037        out.mainURL = this.mainURL;
038        out.fileWatchList = new ArrayList<File>(this.fileWatchList);
039        out.lastModifiedList = new ArrayList<Long>(this.lastModifiedList);
040        return out;
041    }
042
043    public void clear() {
044        this.mainURL = null;
045        lastModifiedList.clear();
046        fileWatchList.clear();
047    }
048
049    /**
050     * The mainURL for the configuration file. Null values are allowed.
051     * 
052     * @param mainURL
053     */
054    public void setMainURL(URL mainURL) {
055        // main url can be null
056        this.mainURL = mainURL;
057        if (mainURL != null)
058            addAsFileToWatch(mainURL);
059    }
060
061    public boolean watchPredicateFulfilled() {
062        if(hasMainURLAndNonEmptyFileList()) {
063            return  true;
064        }
065
066        return fileWatchListContainsProperties();
067
068    }
069
070    private boolean hasMainURLAndNonEmptyFileList() {
071        return mainURL != null && !fileWatchList.isEmpty();
072    }
073
074    private boolean fileWatchListContainsProperties() {
075        return fileWatchList.stream().anyMatch(file -> file.getName().endsWith(PROPERTIES_FILE_EXTENSION));
076
077    }
078
079    private void addAsFileToWatch(URL url) {
080        File file = convertToFile(url);
081        if (file != null) {
082            fileWatchList.add(file);
083            lastModifiedList.add(file.lastModified());
084        }
085    }
086
087    /**
088     * Add the url but only if it is file://.
089     * @param url should be a file
090     */
091
092    public void addToWatchList(URL url) {
093        addAsFileToWatch(url);
094    }
095
096    public URL getMainURL() {
097        return mainURL;
098    }
099
100    public List<File> getCopyOfFileWatchList() {
101        return new ArrayList<File>(fileWatchList);
102    }
103
104    public File changeDetected() {
105        int len = fileWatchList.size();
106
107        for (int i = 0; i < len; i++) {
108            long lastModified = lastModifiedList.get(i);
109            File file = fileWatchList.get(i);
110            long actualModificationDate = file.lastModified();
111
112            if (lastModified != actualModificationDate) {
113                // update modification date in case this instance is reused
114                lastModifiedList.set(i, actualModificationDate);
115                return file;
116            }
117        }
118        return null;
119    }
120
121    @SuppressWarnings("deprecation")
122    File convertToFile(URL url) {
123        String protocol = url.getProtocol();
124        if ("file".equals(protocol)) {
125            return new File(URLDecoder.decode(url.getFile()));
126        } else {
127            addInfo("URL [" + url + "] is not of type file");
128            return null;
129        }
130    }
131
132    /**
133     * Returns true if there are watchable files, false otherwise.
134     * @return true if there are watchable files,  false otherwise.
135     * @since 1.5.8
136     */
137    public boolean hasAtLeastOneWatchableFile() {
138        return !fileWatchList.isEmpty();
139    }
140}