Refactored crawl code. Add crawl journal page.

This commit is contained in:
Christian P. MOMON 2021-05-17 13:24:10 +02:00
parent 47d4a62ac7
commit bf81404746
24 changed files with 1600 additions and 398 deletions

View file

@ -27,6 +27,9 @@ import fr.devinsy.statoolinfos.core.Factory;
import fr.devinsy.statoolinfos.core.Federation;
import fr.devinsy.statoolinfos.core.StatoolInfosException;
import fr.devinsy.statoolinfos.crawl.CrawlCache;
import fr.devinsy.statoolinfos.crawl.CrawlJournal;
import fr.devinsy.statoolinfos.crawl.CrawlJournalFile;
import fr.devinsy.statoolinfos.crawl.Crawler;
/**
* The Class Manager.
@ -44,6 +47,7 @@ public class HtmlizerContext
private Federation federation;
private Categories categories;
private CrawlCache cache;
private CrawlJournal crawlJournal;
/**
* Instantiates a new manager.
@ -65,20 +69,23 @@ public class HtmlizerContext
this.configuration = Factory.loadConfiguration(configurationFile);
logger.info("Cache setting: {}", this.configuration.getCrawlCachePath());
logger.info("Htmlize input setting: {}", this.configuration.getHtmlizeInputPath());
logger.info("Htmlize input setting: {}", this.configuration.getHtmlizeInputURL());
logger.info("Htmlize directory setting: {}", this.configuration.getHtmlizeDirectoryPath());
File htmlizeInput = this.configuration.getHtmlizeInput();
this.cache = new CrawlCache(this.configuration.getCrawlCacheDirectory());
this.crawlJournal = CrawlJournalFile.load(this.cache.restoreFile(Crawler.getJournalURL()));
File htmlizeInputFile = this.cache.restoreFile(this.configuration.getHtmlizeInputURL());
File htmlizeDirectory = this.configuration.getHtmlizeDirectory();
if (htmlizeInput == null)
if (htmlizeInputFile == null)
{
throw new IllegalArgumentException("Htmlize input undefined.");
}
else if (!htmlizeInput.exists())
else if (!htmlizeInputFile.exists())
{
throw new IllegalArgumentException("Htmlize input is missing.");
}
else if (htmlizeInput.isDirectory())
else if (htmlizeInputFile.isDirectory())
{
throw new IllegalArgumentException("Htmlize input is a directory.");
}
@ -98,8 +105,7 @@ public class HtmlizerContext
{
if (this.configuration.isFederation())
{
this.cache = this.configuration.getCrawlCache();
this.federation = Factory.loadFederation(this.configuration.getHtmlizeInput(), this.cache);
this.federation = Factory.loadFederation(htmlizeInputFile, this.cache);
this.categories = Factory.loadCategories(this.configuration.getCategoryFile(), this.federation);
}
else
@ -154,6 +160,11 @@ public class HtmlizerContext
return result;
}
public CrawlJournal getCrawlJournal()
{
return this.crawlJournal;
}
/**
* Gets the federation.
*

View file

@ -95,7 +95,7 @@ public class PropertyChecker
this.federationRules.add(METRICS_WEEKS, WEEKS, PropertyMode.OPTIONAL);
this.federationRules.add(METRICS_DAYS, DAYS, PropertyMode.OPTIONAL);
// this.federationRules.add(CRAWL, ALL, PropertyMode.MANDATORY);
this.federationRules.add(CRAWL, ALL, PropertyMode.MANDATORY);
//
this.organizationRules = new PropertyRules();

View file

@ -19,10 +19,11 @@
package fr.devinsy.statoolinfos.core;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.lang3.StringUtils;
import fr.devinsy.statoolinfos.crawl.CrawlCache;
import fr.devinsy.statoolinfos.properties.PathProperties;
import fr.devinsy.statoolinfos.properties.PathPropertyList;
import fr.devinsy.strings.StringList;
@ -177,18 +178,15 @@ public class Configuration extends PathPropertyList
}
/**
* Gets the cache.
* Gets the crawl cache directory.
*
* @return the cache
* @throws StatoolInfosException
* @return the crawl cache directory
*/
public CrawlCache getCrawlCache() throws StatoolInfosException
public File getCrawlCacheDirectory()
{
CrawlCache result;
File result;
String path = getCrawlCachePath();
result = new CrawlCache(new File(path));
result = new File(get("conf.crawl.cache"));
//
return result;
@ -209,29 +207,6 @@ public class Configuration extends PathPropertyList
return result;
}
/**
* Gets the crawl input.
*
* @return the crawl input
*/
public File getCrawlInputFile()
{
File result;
String path = getCrawlInputPath();
if (StringUtils.isBlank(path))
{
result = null;
}
else
{
result = new File(path);
}
//
return result;
}
/**
* Gets the crawl input path.
*
@ -247,6 +222,37 @@ public class Configuration extends PathPropertyList
return result;
}
/**
* Gets the crawl input.
*
* @return the crawl input
*/
public URL getCrawlInputURL()
{
URL result;
try
{
String path = getCrawlInputPath();
if (StringUtils.isBlank(path))
{
result = null;
}
else
{
result = new URL(path);
}
}
catch (MalformedURLException exception)
{
exception.printStackTrace();
result = null;
}
//
return result;
}
/**
* Gets the edito directory.
*
@ -309,39 +315,17 @@ public class Configuration extends PathPropertyList
return result;
}
/**
* Gets the htmlize input.
*
* @return the htmlize input
*/
public File getHtmlizeInput()
{
File result;
String path = getHtmlizeInputPath();
if (StringUtils.isBlank(path))
{
result = null;
}
else
{
result = new File(path);
}
//
return result;
}
/**
* Gets the htmlize input path.
*
* @return the htmlize input path
* @throws MalformedURLException
*/
public String getHtmlizeInputPath()
public URL getHtmlizeInputURL() throws MalformedURLException
{
String result;
URL result;
result = get("conf.htmlize.input");
result = new URL(get("conf.htmlize.input"));
//
return result;

View file

@ -0,0 +1,125 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.core;
import org.apache.commons.lang3.StringUtils;
public enum PropertyClassType
{
FEDERATION,
ORGANIZATION,
SERVICE,
METRICS;
/**
* Checks if is child of.
*
* @param parent
* the parent
* @return true, if is child of
*/
public boolean isChildOf(final PropertyClassType parent)
{
boolean result;
switch (this)
{
case FEDERATION:
if (parent == null)
{
result = true;
}
else
{
result = false;
}
break;
case ORGANIZATION:
if (parent == FEDERATION)
{
result = true;
}
else
{
result = false;
}
break;
case SERVICE:
if (parent == ORGANIZATION)
{
result = true;
}
else
{
result = false;
}
break;
case METRICS:
result = true;
break;
default:
result = false;
}
//
return result;
}
/**
* Of.
*
* @param value
* the value
* @return the property class type
*/
public static PropertyClassType of(final String value)
{
PropertyClassType result;
String target = StringUtils.trim(StringUtils.toRootLowerCase(value));
if (target == null)
{
result = null;
}
else if (StringUtils.equals(target, "federation"))
{
result = FEDERATION;
}
else if (StringUtils.equals(target, "organization"))
{
result = ORGANIZATION;
}
else if (StringUtils.equals(target, "service"))
{
result = SERVICE;
}
else if (StringUtils.equals(target, "metrics"))
{
result = METRICS;
}
else
{
result = null;
}
//
return result;
}
}

View file

@ -74,7 +74,7 @@ public class StatoolInfos
Configuration configuration = Factory.loadConfiguration(configurationFile);
Builder.clear(configuration);
Crawler.clear(configuration);
new Crawler(configuration.getCrawlCacheDirectory()).clear();
Htmlizer.clear(configuration);
}
@ -90,7 +90,10 @@ public class StatoolInfos
*/
public static void crawl(final File configurationFile) throws StatoolInfosException, IOException
{
Crawler.crawl(configurationFile);
Configuration configuration = Factory.loadConfiguration(configurationFile);
Crawler crawler = new Crawler(configuration.getCrawlCacheDirectory());
crawler.crawl(configuration.getCrawlInputURL());
crawler.storeJournal();
}
/**

View file

@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -40,6 +41,7 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
@ -119,7 +121,7 @@ public class StatoolInfosUtils
// Because Tika failed to recognize SVG file without xml header
// line.
if (result.equals(".txt") && (StringUtils.startsWithIgnoreCase(FileUtils.readFileToString(file, "UTF8"), "<svg")))
if (result.equals(".txt") && (StringUtils.startsWithIgnoreCase(FileUtils.readFileToString(file, StandardCharsets.UTF_8), "<svg")))
{
result = ".svg";
}
@ -221,6 +223,25 @@ public class StatoolInfosUtils
return result;
}
/**
* Sha1sum a file.
*
* @param file
* the file
* @return the string
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static String sha1sum(final File file) throws IOException
{
String result;
result = DigestUtils.sha1Hex(FileUtils.readFileToByteArray(file));
//
return result;
}
/**
* Split day values.
*

View file

@ -21,7 +21,6 @@ package fr.devinsy.statoolinfos.crawl;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
@ -69,11 +68,11 @@ public class CrawlCache
}
else if (StringUtils.isBlank(directory.getName()))
{
throw new IllegalArgumentException("Undefined directory.");
throw new IllegalArgumentException("Crawl cache directory undefined.");
}
else if (!directory.exists())
{
throw new IllegalArgumentException("Directory does not exist.");
throw new IllegalArgumentException("Crawl cache directory does not exist.");
}
else
{
@ -88,11 +87,11 @@ public class CrawlCache
* the key
* @return the file
*/
private File buildFile(final String key)
private File buildFile(final URL url)
{
File result;
result = new File(this.directory, DigestUtils.md5Hex(key));
result = new File(this.directory, DigestUtils.md5Hex(url.toString()));
//
return result;
@ -113,6 +112,11 @@ public class CrawlCache
}
}
public File getDirectory()
{
return this.directory;
}
/**
* Gets the extension.
*
@ -138,39 +142,11 @@ public class CrawlCache
return result;
}
/**
* Restore file.
*
* @param key
* the key
* @return the file
*/
public File restoreFile(final String key)
{
File result;
if (StringUtils.isBlank(key))
{
result = null;
}
else
{
result = buildFile(key);
if (!result.exists())
{
result = null;
}
}
//
return result;
}
/**
* Restore file.
*
* @param url
* the url
* the key
* @return the file
*/
public File restoreFile(final URL url)
@ -183,32 +159,17 @@ public class CrawlCache
}
else
{
result = restoreFile(url.toString());
result = buildFile(url);
if (!result.exists())
{
result = null;
}
}
//
return result;
}
/**
* Restore file to.
*
* @param key
* the key
* @param target
* the target
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void restoreFileTo(final String key, final File target) throws IOException
{
File logoFile = restoreFile(key);
if (logoFile != null)
{
FileUtils.copyFile(logoFile, target);
}
}
/**
* Restore file to.
*
@ -221,7 +182,11 @@ public class CrawlCache
*/
public void restoreFileTo(final URL url, final File target) throws IOException
{
restoreFile(url.toString());
File source = restoreFile(url);
if (source != null)
{
FileUtils.copyFile(source, target);
}
}
/**
@ -246,11 +211,13 @@ public class CrawlCache
}
else
{
if (url == null)
logger.info("CatGeneratoring from {}", url);
File logoFile = restoreFile(url);
if (logoFile == null)
{
try
{
logger.info("CatGeneratoring cat avatar (1): {}", target.getAbsoluteFile());
logger.info("CatGeneratoring cat avatar: {}", target.getAbsoluteFile());
if ((generator == null) || (generator == DefaultLogoGenerator.CAT))
{
CatGenerator.buildAvatarTo(seed, target);
@ -268,62 +235,11 @@ public class CrawlCache
}
else
{
File logoFile = restoreFile(url);
if (logoFile == null)
{
try
{
logger.info("CatGeneratoring cat avatar (2): {}", target.getAbsoluteFile());
if ((generator == null) || (generator == DefaultLogoGenerator.CAT))
{
CatGenerator.buildAvatarTo(seed, target);
}
else
{
BirdGenerator.buildAvatarTo(seed, target);
}
}
catch (IOException exception)
{
logger.warn("CatGeneratoring failed for {}: {}", seed, exception.getMessage());
URLUtils.copyResource("/fr/devinsy/statoolinfos/htmlize/stuff/default-organization-logo.png", target);
}
}
else
{
FileUtils.copyFile(logoFile, target);
}
FileUtils.copyFile(logoFile, target);
}
}
}
/**
* Restore properties.
*
* @param url
* the url
* @return the path property list
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public PathProperties restoreProperties(final URL url) throws IOException
{
PathProperties result;
if (url == null)
{
result = new PathPropertyList();
}
else
{
File file = buildFile(url.toString() + ".properties");
result = PathPropertyUtils.load(file);
}
//
return result;
}
/**
* @param url
* @return
@ -339,7 +255,7 @@ public class CrawlCache
}
else
{
File file = buildFile(url.toString());
File file = buildFile(url);
result = PathPropertyUtils.load(file);
}
@ -347,104 +263,29 @@ public class CrawlCache
return result;
}
/**
* Store.
*
* @param key
* the key
* @return the file
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public File store(final String key, final File source) throws IOException
{
File result;
if (StringUtils.isBlank(key))
{
result = null;
}
else
{
result = buildFile(key);
FileUtils.copyFile(source, result);
}
//
return result;
}
/**
* Store.
*
* @param url
* the url
* @return the file
* @throws IOException
*/
public File store(final URL url) throws IOException
{
File result;
if (StringUtils.startsWith(url.getProtocol(), "http"))
{
final int TIMEOUT = 5000;
result = buildFile(url.toString());
File temp = Files.createTempFile("tmp-", ".statoolsinfos").toFile();
FileUtils.copyURLToFile(url, temp, TIMEOUT, TIMEOUT);
if (temp.length() == 0)
{
if (result.exists())
{
logger.warn("WARNING: empty file crawled and ignored for [{}]", url);
result = null;
}
else
{
logger.warn("WARNING: empty file crawled and copied for [{}]", url);
FileUtils.copyFile(temp, result);
}
}
else
{
FileUtils.copyFile(temp, result);
}
temp.delete();
}
else
{
logger.warn("WARNING: crawl failed because bad http+ protocol for [{}]", url);
result = null;
}
//
return result;
}
/**
* Store properties.
*
* @param key
* the key
* @param properties
* the properties
* @param source
* the source
* @return the file
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public File storeProperties(final String key, final PathProperties properties) throws IOException
public File store(final URL url, final File source) throws IOException
{
File result;
if (StringUtils.isBlank(key))
if ((url == null) || (!StringUtils.startsWith(url.getProtocol(), "http")))
{
result = null;
}
else
{
result = buildFile(key);
PathPropertyUtils.save(result, properties);
result = buildFile(url);
FileUtils.copyFile(source, result);
}
//
@ -472,7 +313,7 @@ public class CrawlCache
}
else
{
result = buildFile(url.toString());
result = buildFile(url);
PathPropertyUtils.save(result, properties);
}
@ -488,7 +329,7 @@ public class CrawlCache
* the url
* @return the file
*/
public File storeQuietly(final URL url)
public File storeQuietly2(final URL url)
{
File result;
@ -501,7 +342,7 @@ public class CrawlCache
else
{
final int TIMEOUT = 5000;
result = buildFile(url.toString());
result = buildFile(url);
FileUtils.copyURLToFile(url, result, TIMEOUT, TIMEOUT);
logger.info("Crawled {}", url);
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.crawl;
import java.time.LocalDateTime;
/**
* The Class CrawlJournal.
*/
public class CrawlJournal extends CrawlLogs
{
private static final long serialVersionUID = -7855320365496351766L;
private LocalDateTime datetime;
/**
* Instantiates a new crawl journal.
*/
public CrawlJournal()
{
super();
this.datetime = LocalDateTime.now();
}
/**
* Gets the date.
*
* @return the date
*/
public LocalDateTime getDatetime()
{
return this.datetime;
}
/**
* Sets the date.
*
* @param date
* the new date
*/
public void setDatetime(final LocalDateTime date)
{
this.datetime = date;
}
}

View file

@ -0,0 +1,236 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.crawl;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class CrawlFile.
*/
public class CrawlJournalFile
{
private static Logger logger = LoggerFactory.getLogger(CrawlJournalFile.class);
/**
* Instantiates a new crawl file.
*/
private CrawlJournalFile()
{
super();
}
/**
* Load.
*
* @param file
* the file
* @return the path property list
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static CrawlJournal load(final File file) throws IOException
{
CrawlJournal result;
result = load(file, StandardCharsets.UTF_8);
//
return result;
}
/**
* Load.
*
* @param file
* the file
* @param charset
* the charset name
* @return the path properties
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static CrawlJournal load(final File file, final Charset charset) throws IOException
{
CrawlJournal result;
if (file == null)
{
throw new IllegalArgumentException("File parameter is null.");
}
else
{
BufferedReader in = null;
try
{
in = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
result = read(in);
}
finally
{
IOUtils.closeQuietly(in);
}
result.setDatetime(LocalDateTime.ofEpochSecond(file.lastModified() / 1000, 0, ZoneOffset.UTC));
}
//
return result;
}
/**
* Read.
*
* @param in
* the in
* @return the crawl logs
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static CrawlJournal read(final BufferedReader in) throws IOException
{
CrawlJournal result;
result = new CrawlJournal();
boolean ended = false;
while (!ended)
{
String line = in.readLine();
if (line == null)
{
ended = true;
}
else
{
CrawlLog log = valueOf(line);
result.add(log);
}
}
//
return result;
}
/**
* Save.
*
* @param file
* the file
* @param source
* the source
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void save(final File file, final CrawlJournal source) throws IOException
{
PrintWriter out = null;
try
{
out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
write(out, source);
}
finally
{
//
IOUtils.closeQuietly(out);
}
}
/**
* Value of.
*
* @param line
* the line
* @return the path property
*/
public static CrawlLog valueOf(final String line)
{
CrawlLog result;
if (line == null)
{
result = null;
}
else
{
String[] tokens = line.split(" ", 2);
CrawlStatus status = CrawlStatus.valueOf(tokens[0].toUpperCase());
URL url;
try
{
url = new URL(tokens[1].trim());
}
catch (MalformedURLException exception)
{
logger.error("Error valuing [{}]", line);
exception.printStackTrace();
url = null;
}
result = new CrawlLog(url, status);
}
//
return result;
}
/**
* Write.
*
* @param out
* the out
* @param source
* the source
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void write(final PrintWriter out, final CrawlJournal journal) throws IOException
{
if (journal != null)
{
for (CrawlLog log : journal)
{
String line = log.getStatus() + " " + log.getUrl();
out.write(line);
out.write("\n");
}
}
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.crawl;
import java.net.URL;
/**
* The Class CrawlLog.
*/
public class CrawlLog
{
private URL url;
private CrawlStatus status;
/**
* Instantiates a new crawl log.
*
* @param url
* the url
* @param status
* the status
*/
public CrawlLog(final URL url, final CrawlStatus status)
{
this.url = url;
this.status = status;
}
public CrawlStatus getStatus()
{
return this.status;
}
public URL getUrl()
{
return this.url;
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.crawl;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
/**
* The Class CrawlLogs.
*/
public class CrawlLogs extends ArrayList<CrawlLog>
{
private static final long serialVersionUID = -8749217049690008582L;
/**
* Instantiates a new crawl logs.
*/
public CrawlLogs()
{
super();
}
/**
* Adds the.
*
* @param url
* the url
* @param status
* the status
*/
public void add(final URL url, final CrawlStatus status)
{
this.add(new CrawlLog(url, status));
}
/**
* Find by software.
*
* @param softwareName
* the software name
* @return the category
*/
public CrawlLogs findByUrl(final URL url)
{
CrawlLogs result;
result = new CrawlLogs();
for (CrawlLog log : this)
{
if (StringUtils.equals(log.getUrl().toString(), url.toString()))
{
result.add(log);
}
}
//
return result;
}
/**
* Gets the errors.
*
* @return the errors
*/
public CrawlLogs getErrors()
{
CrawlLogs result;
result = new CrawlLogs();
for (CrawlLog log : this)
{
if (log.getStatus().isError())
{
result.add(log);
}
}
//
return result;
}
/**
* Gets the success.
*
* @return the success
*/
public CrawlLogs getSuccess()
{
CrawlLogs result;
result = new CrawlLogs();
for (CrawlLog log : this)
{
if (!log.getStatus().isError())
{
result.add(log);
}
}
//
return result;
}
/**
* Reverse.
*
* @return the categories
*/
public CrawlLogs reverse()
{
CrawlLogs result;
Collections.reverse(this);
result = this;
//
return result;
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.crawl;
public enum CrawlStatus
{
BADCHILDCLASS,
BADURLFORMAT,
CONNECTERROR,
DOWNLOADERROR,
EMPTY,
IOERROR,
MISSING,
SUCCESS,
UPDATED,
URLNOTFOUND;
public boolean isError()
{
boolean result;
if ((this == CrawlStatus.SUCCESS) || (this == CrawlStatus.UPDATED))
{
result = false;
}
else
{
result = true;
}
//
return result;
}
}

View file

@ -21,18 +21,19 @@ package fr.devinsy.statoolinfos.crawl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.devinsy.statoolinfos.core.Configuration;
import fr.devinsy.statoolinfos.core.Factory;
import fr.devinsy.statoolinfos.core.PropertyClassType;
import fr.devinsy.statoolinfos.core.StatoolInfosException;
import fr.devinsy.statoolinfos.core.StatoolInfosUtils;
import fr.devinsy.statoolinfos.properties.PathProperties;
@ -47,107 +48,48 @@ public class Crawler
{
private static Logger logger = LoggerFactory.getLogger(Crawler.class);
private CrawlCache cache;
private CrawlJournal journal;
/**
* Instantiates a new crawler.
*
* @param rootDirectory
* the root directory
* @throws StatoolInfosException
* the statool infos exception
*/
public Crawler(final File rootDirectory) throws StatoolInfosException
{
logger.info("Crawl cache setting: {}", rootDirectory);
this.cache = new CrawlCache(rootDirectory);
this.journal = new CrawlJournal();
}
/**
* Clear.
*
* @param configuration
* the configuration
* @throws StatoolInfosException
* the statool infos exception
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void clear(final Configuration configuration) throws StatoolInfosException, IOException
public void clear() throws StatoolInfosException
{
logger.info("Cache setting: {}", configuration.getCrawlCachePath());
String path = configuration.getCrawlCachePath();
if (StringUtils.isBlank(path))
{
logger.warn("Undefined crawl cache.");
}
else if (!new File(path).exists())
{
logger.warn("Crawl cache does not exist: {}.", path);
}
else
{
CrawlCache cache = configuration.getCrawlCache();
cache.clear();
}
this.cache.clear();
}
/**
* Crawl.
*
* @param configuration
* the configuration
* @param url
* the input url
* @throws StatoolInfosException
* the statool infos exception
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void crawl(final Configuration configuration) throws StatoolInfosException, IOException
public void crawl(final URL url) throws StatoolInfosException, IOException
{
logger.info("Crawl input setting: {}", configuration.getCrawlInputPath());
logger.info("Crawl cache setting: {}", configuration.getCrawlCachePath());
CrawlCache cache = configuration.getCrawlCache();
PathProperties input = PathPropertyUtils.load(configuration.getCrawlInputFile());
if (configuration.isFederation())
{
cache.store(input.get("federation.name"), configuration.getCrawlInputFile());
cache.storeQuietly(input.getURL("federation.logo"));
}
else if (configuration.isOrganization())
{
cache.store(input.get("organization.name"), configuration.getCrawlInputFile());
cache.storeQuietly(input.getURL("organization.logo"));
}
PathProperties subs = input.getByPrefix("subs");
for (PathProperty property : subs)
{
if (StringUtils.isNotBlank(property.getValue()))
{
try
{
URL subUrl = new URL(property.getValue());
crawl(subUrl, cache, input.get("file.class"));
}
catch (java.net.MalformedURLException exception)
{
logger.error("ERROR: subcrawl failed for [{}][{}]: {}", property.getPath(), property.getValue(), exception.getMessage());
exception.printStackTrace();
}
catch (IOException exception)
{
logger.error("ERROR: subcrawl failed for [{}][{}]: {}", property.getPath(), property.getValue(), exception.getMessage());
exception.printStackTrace();
}
}
}
}
/**
* Crawl.
*
* @param configurationFile
* the input
* @throws StatoolInfosException
* the statool infos exception
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void crawl(final File configurationFile) throws StatoolInfosException, IOException
{
logger.info("Crawl {}", configurationFile.getAbsolutePath());
Configuration configuration = Factory.loadConfiguration(configurationFile);
crawl(configuration);
crawl(url, null);
}
/**
@ -162,64 +104,287 @@ public class Crawler
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void crawl(final URL url, final CrawlCache cache, final String parentFileClass) throws StatoolInfosException, IOException
public void crawl(final URL url, final PropertyClassType parent)
{
logger.info("Crawling " + url);
logger.info("Crawling {}", url);
// Crawl.
File file = cache.store(url);
if (file != null)
try
{
// Build crawl data.
PathProperties crawlSection = new PathPropertyList();
crawlSection.put("crawl.crawler", "StatoolInfos");
crawlSection.put("crawl.datetime", LocalDateTime.now().toString());
crawlSection.put("crawl.url", url.toString());
crawlSection.put("crawl.file.size", FileUtils.sizeOf(file));
crawlSection.put("crawl.file.datetime", StatoolInfosUtils.urlLastModified(url).toString());
crawlSection.put("crawl.file.sha1", DigestUtils.sha1Hex(FileUtils.readFileToByteArray(file)));
// Add crawl data in crawled file.
String lines = crawlSection.toStringListFormatted().toStringSeparatedBy('\n');
FileUtils.write(file, FileUtils.readFileToString(file, StandardCharsets.UTF_8) + "\n" + lines, StandardCharsets.UTF_8);
// Crawl another resources.
PathProperties properties = PathPropertyUtils.load(file);
cache.storeQuietly(properties.getURL("organization.logo"));
cache.storeQuietly(properties.getURL("service.logo"));
// Crawl subs.
String fileClass = properties.get("file.class");
if (StringUtils.equalsIgnoreCase(fileClass, parentFileClass))
File downloadFile;
try
{
logger.warn("WARNING: file class same than parent for [{}]", url);
downloadFile = download(url);
}
else
catch (java.net.ConnectException exception)
{
PathProperties subs = properties.getByPrefix("subs");
for (PathProperty property : subs)
logger.error("ERROR: crawl failed for [{}]: {}", url.toString(), exception.getMessage());
this.journal.add(url, CrawlStatus.CONNECTERROR);
downloadFile = null;
exception.printStackTrace();
}
catch (FileNotFoundException exception)
{
logger.error("ERROR: crawl failed for [{}]: {}", url.toString(), exception.getMessage());
this.journal.add(url, CrawlStatus.URLNOTFOUND);
downloadFile = null;
exception.printStackTrace();
}
catch (IOException exception)
{
logger.error("ERROR: crawl failed for [{}]: {}", url.toString(), exception.getMessage());
this.journal.add(url, CrawlStatus.DOWNLOADERROR);
downloadFile = null;
exception.printStackTrace();
}
if (downloadFile != null)
{
if (!downloadFile.exists())
{
if (StringUtils.isNotBlank(property.getValue()))
logger.error("ERROR: download missing.");
this.journal.add(url, CrawlStatus.MISSING);
}
else if (downloadFile.length() == 0)
{
logger.error("ERROR: download empty.");
this.journal.add(url, CrawlStatus.EMPTY);
}
else
{
PathProperties downloadProperties = PathPropertyUtils.load(downloadFile);
PropertyClassType downloadClass = PropertyClassType.of(downloadProperties.get("file.class"));
if ((downloadClass == null) || (!downloadClass.isChildOf(parent)))
{
try
logger.error("ERROR: bad child class [{}][{}].", downloadClass, parent);
this.journal.add(url, CrawlStatus.BADCHILDCLASS);
}
else
{
File storedFile = this.cache.restoreFile(url);
String storedSha;
if (storedFile == null)
{
URL subUrl = new URL(property.getValue());
crawl(subUrl, cache, fileClass);
storedSha = null;
}
catch (java.net.MalformedURLException exception)
else
{
logger.error("ERROR: subcrawl failed for [{}][{}][{}]: {}", url.toString(), property.getPath(), property.getValue(), exception.getMessage());
exception.printStackTrace();
PathProperties storedProperties = PathPropertyUtils.load(storedFile);
storedSha = storedProperties.get("crawl.file.sha1");
}
catch (java.net.ConnectException | FileNotFoundException exception)
String downloadSha = StatoolInfosUtils.sha1sum(downloadFile);
if (StringUtils.equals(downloadSha, storedSha))
{
logger.error("ERROR: subcrawl failed for [{}][{}][{}]: {}", url.toString(), property.getPath(), property.getValue(), exception.getMessage());
exception.printStackTrace();
this.journal.add(url, CrawlStatus.SUCCESS);
}
else
{
// Build crawl data.
PathProperties crawlSection = new PathPropertyList();
crawlSection.put("crawl.crawler", "StatoolInfos");
crawlSection.put("crawl.datetime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYY-MM-dd'T'HH:mm:ss")));
crawlSection.put("crawl.url", url.toString());
crawlSection.put("crawl.file.size", FileUtils.sizeOf(downloadFile));
crawlSection.put("crawl.file.datetime", StatoolInfosUtils.urlLastModified(url).toString());
crawlSection.put("crawl.file.sha1", downloadSha);
String crawlSectionLines = crawlSection.toStringListFormatted().toStringSeparatedBy('\n');
// Add crawl data in crawled file.
String downloadExtendedLines = FileUtils.readFileToString(downloadFile, StandardCharsets.UTF_8) + "\n" + crawlSectionLines;
FileUtils.write(downloadFile, downloadExtendedLines, StandardCharsets.UTF_8);
// Store in cache.
this.cache.store(url, downloadFile);
downloadFile.delete();
//
this.journal.add(url, CrawlStatus.UPDATED);
}
// Cache another resources.
crawlLogo(downloadProperties.getURL("federation.logo"));
crawlLogo(downloadProperties.getURL("organization.logo"));
crawlLogo(downloadProperties.getURL("service.logo"));
// Do subs.
PathProperties subs = downloadProperties.getByPrefix("subs");
for (PathProperty property : subs)
{
if (StringUtils.isNotBlank(property.getValue()))
{
try
{
URL subUrl = new URL(property.getValue());
crawl(subUrl, downloadClass);
}
catch (java.net.MalformedURLException exception)
{
logger.error("ERROR: subcrawl failed for [{}][{}][{}]: {}", url.toString(), property.getPath(), property.getValue(), exception.getMessage());
this.journal.add(url, CrawlStatus.BADURLFORMAT);
exception.printStackTrace();
}
}
}
}
}
}
}
catch (IOException exception)
{
this.journal.add(url, CrawlStatus.IOERROR);
}
}
/**
* Crawl logo.
*
* @param url
* the url
* @return the file
*/
public File crawlLogo(final URL url)
{
File result;
try
{
if ((url == null) || (!StringUtils.startsWithIgnoreCase(url.getProtocol(), "http")))
{
result = null;
}
else
{
logger.info("Crawling {}", url);
File logoFile;
try
{
logoFile = download(url);
}
catch (java.net.ConnectException exception)
{
logger.error("ERROR: crawl failed (1) for [{}]: {}", url.toString(), exception.getMessage());
this.journal.add(url, CrawlStatus.CONNECTERROR);
logoFile = null;
}
catch (FileNotFoundException exception)
{
logger.error("ERROR: crawl failed (2) for [{}]: {}", url.toString(), exception.getMessage());
this.journal.add(url, CrawlStatus.URLNOTFOUND);
logoFile = null;
}
catch (IOException exception)
{
logger.error("ERROR: crawl failed (3) for [{}]: {}", url.toString(), exception.getMessage());
this.journal.add(url, CrawlStatus.DOWNLOADERROR);
logoFile = null;
}
if (logoFile == null)
{
result = null;
}
else
{
result = this.cache.store(url, logoFile);
this.journal.add(url, CrawlStatus.SUCCESS);
logoFile.delete();
}
}
}
catch (IOException exception)
{
logger.info("Store failed for {}: {}", url, exception.getMessage());
result = null;
}
//
return result;
}
/**
* Download.
*
* @param url
* the url
* @return the file
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public File download(final URL url) throws IOException
{
File result;
if (!StringUtils.startsWith(url.getProtocol(), "http"))
{
logger.warn("WARNING: crawl failed because bad http+ protocol for [{}]", url);
result = null;
}
else
{
final int TIMEOUT = 5000;
result = Files.createTempFile("tmp-", ".statoolsinfos").toFile();
FileUtils.copyURLToFile(url, result, TIMEOUT, TIMEOUT);
}
//
return result;
}
/**
* Restore journal.
*
* @return the crawl journal
* @throws IOException
*/
public CrawlJournal restoreJournal() throws IOException
{
CrawlJournal result;
logger.info("Restoring crawl journal.");
File journalFile = this.cache.restoreFile(getJournalURL());
result = CrawlJournalFile.load(journalFile);
//
return result;
}
/**
* Store journal.
*/
public void storeJournal()
{
try
{
logger.info("Storing crawl journal.");
File file = Files.createTempFile("tmp-", ".statoolsinfos").toFile();
CrawlJournalFile.save(file, this.journal);
this.cache.store(getJournalURL(), file);
file.delete();
}
catch (IOException exception)
{
exception.printStackTrace();
}
}
/**
* Gets the journal URL.
*
* @return the journal URL
* @throws MalformedURLException
*/
public static URL getJournalURL() throws MalformedURLException
{
URL result;
result = new URL("http://localhost/crawl.journal");
//
return result;
}
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2021 Christian Pierre MOMON <christian@momon.org>
*
* This file is part of StatoolInfos, simple service statistics tool.
*
* StatoolInfos is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* StatoolInfos 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with StatoolInfos. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.devinsy.statoolinfos.htmlize;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.devinsy.statoolinfos.HtmlizerContext;
import fr.devinsy.statoolinfos.core.Federation;
import fr.devinsy.statoolinfos.core.StatoolInfosException;
import fr.devinsy.statoolinfos.crawl.CrawlCache;
import fr.devinsy.statoolinfos.crawl.CrawlJournal;
import fr.devinsy.statoolinfos.crawl.CrawlLog;
import fr.devinsy.xidyn.XidynException;
import fr.devinsy.xidyn.data.TagDataManager;
import fr.devinsy.xidyn.presenters.PresenterUtils;
/**
* The Class CrawlJournalPage.
*/
public class CrawlJournalPage
{
private static Logger logger = LoggerFactory.getLogger(CrawlJournalPage.class);
/**
* Builds the all.
*
* @throws StatoolInfosException
* @throws IOException
*/
public static void buildAll() throws StatoolInfosException, IOException
{
Federation federation = HtmlizerContext.instance().getFederation();
CrawlCache cache = HtmlizerContext.instance().getCache();
File htmlizeDirectory = HtmlizerContext.instance().getHtmlizeDirectory();
logger.info("Htmlize Crawl Journal pages.");
CrawlJournal journal = HtmlizerContext.instance().getCrawlJournal();
String page = htmlize("Journal des téléchargements", journal);
FileUtils.write(new File(htmlizeDirectory, federation.getTechnicalName() + "-crawl.xhtml"), page, StandardCharsets.UTF_8);
}
/**
* Htmlize.
*
* @param title
* the title
* @param journal
* the journal
* @return the string
* @throws StatoolInfosException
* the statool infos exception
*/
public static String htmlize(final String title, final CrawlJournal journal) throws StatoolInfosException
{
String result;
try
{
logger.debug("Building Crawl journal page…");
TagDataManager data = new TagDataManager();
data.setEscapedContent("title", title);
data.setContent("date", journal.getDatetime().toString());
data.setContent("totalCount", journal.size());
data.setContent("errorCount", journal.getErrors().size());
int index = 0;
for (CrawlLog log : journal)
{
data.setEscapedContent("crawlLogLine", index, "crawlLogLineUrlLink", log.getUrl().toString());
data.setEscapedAttribute("crawlLogLine", index, "crawlLogLineUrlLink", "href", log.getUrl().toString());
data.setContent("crawlLogLine", index, "crawlLogLineStatus", log.getStatus().toString());
if (log.getStatus().isError())
{
data.setAttribute("crawlLogLine", index, "crawlLogLineStatus", "style", "background-color: red;");
}
else
{
data.setAttribute("crawlLogLine", index, "crawlLogLineStatus", "style", "background-color: lime;");
}
index += 1;
}
String content = PresenterUtils.dynamize("/fr/devinsy/statoolinfos/htmlize/crawlJournal.xhtml", data).toString();
BreadcrumbTrail trail = new BreadcrumbTrail();
result = WebCharterView.build(content, trail);
}
catch (XidynException exception)
{
throw new StatoolInfosException("Error building crawl journal page: " + exception.getMessage(), exception);
}
//
return result;
}
}

View file

@ -107,9 +107,19 @@ public class FederationPage
data.setAttribute("rawLink", "href", federation.getTechnicalName() + ".properties");
data.setAttribute("rawCheckLink", "href", federation.getTechnicalName() + "-check.xhtml");
data.setAttribute("statsLink", "href", federation.getTechnicalName() + "-stats.xhtml");
data.setAttribute("crawlLink", "href", federation.getTechnicalName() + "-crawl.xhtml");
if (HtmlizerContext.instance().getCrawlJournal().getErrors().isEmpty())
{
data.setAttribute("crawlLinkImg", "src", "circle-icons/download-mono.svg");
}
else
{
data.setAttribute("crawlLinkImg", "src", "circle-icons/download.svg");
}
{
PropertyChecks checks = federation.getInputChecksAll();

View file

@ -193,6 +193,7 @@ public class Htmlizer
AboutPage.build();
CategoriesPage.build();
CategoryPage.buildAll();
CrawlJournalPage.buildAll();
EditoPage.build();
ExportsPage.build();
FederationPage.build();

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>StatoolInfos</title>
<meta charset="UTF-8" />
<meta name="keywords" content="statoolinfos,devinsy,federation" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="statoolinfos.css" />
<script src="sorttable.js" />
<script src="Chart.bundle.min.js"></script>
</head>
<body>
<div class="center_table" style="width: 1100px;">
<div class="center">
<h2 id="title">Journal des téléchargements</h2>
<div>Nombre de téléchargements : <span id="totalCount">n/a</span></div>
<div>Nombre d'erreurs : <span id="errorCount">n/a</span></div>
<div>Date : <span id="date">n/a</span></div>
</div>
<br/>
<div class="center_table" style="width: 900px;">
<table id="crawlLogs" class="table_classic left">
<thead>
<tr>
<th>URL</th>
<th style="width: 200px;">Statut</th>
</tr>
</thead>
<tbody>
<tr id="crawlLogLine">
<td id="crawlLogLineUrl"><a href="#" id="crawlLogLineUrlLink">n/a</a></td>
<td id="crawlLogLineStatus" class="td_center center">n/a</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
$('#crawlLogs').DataTable(
{
paging: false,
ordering: true,
"order": [[ 0, "asc" ]],
language: dataTableFrench,
column:
[ {},{},
{
"bSortable": false
},
]
});
</script>
</body>
</html>

View file

@ -31,6 +31,7 @@
<a id="technicalDocLink" href="#"><img id="technicalDocLinkImg" src="circle-icons/tools.svg" class="disabled" title="Documentation technique"/></a>
<a id="rawCheckLink" href="#"><img id="rawCheckLinkImg" src="circle-icons/clipboard-mono.svg" title="Fichier propriétés analysé"/></a>
<a id="rawLink" href="#"><img id="rawLinkImg" src="circle-icons/document-mono.svg" title="Fichier propriétés"/></a>
<a id="crawlLink" href="#"><img id="crawlLinkImg" src="circle-icons/download-mono.svg" title="Statut des téléchargements"/></a>
<a id="statsLink" href="#"><img id="statsLinkImg" src="circle-icons/barchart-mono.svg" title="Statistiques"/></a>
<div style="display: inline-block; vertical-align: middle; font-size: smaller; margin-left: 2px; width: 35px;">
<a id="alertLink" href="#" style="text-decoration: none;">

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 85.333336 85.333336"
height="85.333336"
width="85.333336"
xml:space="preserve"
id="svg2"
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,64 H 64 V 0 H 0 Z" /></clipPath><clipPath
id="clipPath30"
clipPathUnits="userSpaceOnUse"><path
id="path28"
d="M 24,52.9823 H 40 V 52.8209 H 24 Z" /></clipPath><clipPath
id="clipPath50"
clipPathUnits="userSpaceOnUse"><path
id="path48"
d="M 8,46.7566 H 56 V 16 H 8 Z" /></clipPath></defs><g
transform="matrix(1.3333333,0,0,-1.3333333,0,85.333333)"
id="g10"><g
id="g12"><g
clip-path="url(#clipPath18)"
id="g14"><g
transform="translate(64,32)"
id="g20"><path
id="path22"
style="fill:#c75c5c;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 0,-17.673 -14.327,-32 -32,-32 -17.673,0 -32,14.327 -32,32 0,17.673 14.327,32 32,32 C -14.327,32 0,17.673 0,0" /></g><g
id="g24"><g
id="g26" /><g
id="g42"><g
style="opacity:0.19999701"
id="g40"
clip-path="url(#clipPath30)"><g
id="g34"
transform="translate(39.9836,52.8257)"><path
id="path32"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0" /></g><g
id="g38"
transform="translate(24,52.8834)"><path
id="path36"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0" /></g></g></g></g><g
id="g44"><g
id="g46" /><g
id="g58"><g
style="opacity:0.19999701"
id="g56"
clip-path="url(#clipPath50)"><g
id="g54"
transform="translate(48,32)"><path
id="path52"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8" /></g></g></g></g><g
transform="translate(48,34)"
id="g60"><path
id="path62"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8" /></g><g
transform="translate(39.9836,54.8257)"
id="g64"><path
id="path66"
style="fill:#8c81d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0" /></g><g
transform="translate(24,54.8834)"
id="g68"><path
id="path70"
style="fill:#8c81d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 85.333336 85.333336"
height="85.333336"
width="85.333336"
xml:space="preserve"
id="svg2"
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,64 H 64 V 0 H 0 Z" /></clipPath></defs><g
transform="matrix(1.3333333,0,0,-1.3333333,0,85.333333)"
id="g10"><g
id="g12"><g
clip-path="url(#clipPath18)"
id="g14"><g
transform="translate(48,34)"
id="g20"><path
id="path22"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8" /></g><g
transform="translate(24,54.8834)"
id="g24"><path
id="path26"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0" /></g><g
transform="translate(39.9836,54.8257)"
id="g28"><path
id="path30"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0" /></g><g
transform="translate(48,18)"
id="g32"><path
id="path34"
style="fill:#4f5d73;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 h -30 c -5.523,0 -10,4.477 -10,10 0,5.009 3.687,9.146 8.492,9.874 1.175,4.715 4.437,8.6 8.748,10.62 0.002,0.001 0.005,-0.002 0.007,-0.001 1.249,0.533 2.753,-0.505 2.753,-1.61 v -8.5 C -20,19.555 -20.672,19 -21.5,19 h -3.061 c -1.472,0 -1.883,-1.127 -0.914,-2.362 l 7.713,-9.829 c 0.969,-1.234 2.555,-1.234 3.524,0 l 7.713,9.829 C -5.556,17.873 -5.967,19 -7.439,19 H -10.5 c -0.828,0 -1.5,0.555 -1.5,1.383 v 8.5 c 0,1.071 0.904,2.39 2.63,1.666 C -3.847,28.027 0,22.469 0,16 4.418,16 8,12.418 8,8 8,3.582 4.418,0 0,0 m -23.982,36.821 c -0.003,0.018 -0.018,0.047 -0.018,0.062 0,0.163 0.006,0.112 0.018,-0.062 M -8,36.883 C -8,36.869 -8.014,36.842 -8.016,36.826 -8.005,36.996 -8,37.044 -8,36.883 M -16,46 c -17.673,0 -32,-14.327 -32,-32 0,-17.673 14.327,-32 32,-32 17.673,0 32,14.327 32,32 0,17.673 -14.327,32 -32,32" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 85.333336 85.333336"
height="85.333336"
width="85.333336"
xml:space="preserve"
id="svg2"
version="1.1"
sodipodi:docname="download-red.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1388"
inkscape:window-height="998"
id="namedview36"
showgrid="false"
inkscape:zoom="8.2265626"
inkscape:cx="61.943276"
inkscape:cy="42.666669"
inkscape:window-x="438"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,64 H 64 V 0 H 0 Z" /></clipPath><clipPath
id="clipPath30"
clipPathUnits="userSpaceOnUse"><path
id="path28"
d="M 24,52.9823 H 40 V 52.8209 H 24 Z" /></clipPath><clipPath
id="clipPath50"
clipPathUnits="userSpaceOnUse"><path
id="path48"
d="M 8,46.7566 H 56 V 16 H 8 Z" /></clipPath></defs><g
id="g271"><g
id="g245"><g
id="g220"><g
id="g196"><path
id="path22-3"
style="fill:#76c2af;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333325"
d="m 85.333331,42.666667 c 0,23.563999 -19.102666,42.666665 -42.666665,42.666665 C 19.102666,85.333332 -2e-7,66.230666 -2e-7,42.666667 -2e-7,19.102667 19.102666,10e-7 42.666666,10e-7 c 23.563999,0 42.666665,19.102666 42.666665,42.666666"
inkscape:connector-curvature="0" /><g
id="g24"
transform="matrix(1.3333333,0,0,-1.3333333,0,85.333333)"><g
id="g26" /><g
id="g42"><g
clip-path="url(#clipPath30)"
id="g40"
style="opacity:0.19999701"><g
transform="translate(39.9836,52.8257)"
id="g34"><path
inkscape:connector-curvature="0"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path32" /></g><g
transform="translate(24,52.8834)"
id="g38"><path
inkscape:connector-curvature="0"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path36" /></g></g></g></g><g
id="g44"
transform="matrix(1.3333333,0,0,-1.3333333,0,85.333333)"><g
id="g46" /><g
id="g58"><g
clip-path="url(#clipPath50)"
id="g56"
style="opacity:0.19999701"><g
transform="translate(48,32)"
id="g54"><path
inkscape:connector-curvature="0"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path52" /></g></g></g></g><g
id="g60"
transform="matrix(1.3333333,0,0,-1.3333333,63.999998,40.000001)"><path
inkscape:connector-curvature="0"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path62" /></g><g
id="g64"
transform="matrix(1.3333333,0,0,-1.3333333,53.311465,12.232401)"><path
inkscape:connector-curvature="0"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0"
style="fill:#8c81d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path66" /></g><g
id="g68"
transform="matrix(1.3333333,0,0,-1.3333333,31.999999,12.155468)"><path
inkscape:connector-curvature="0"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0"
style="fill:#8c81d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path70" /></g></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 85.333336 85.333336"
height="85.333336"
width="85.333336"
xml:space="preserve"
id="svg2"
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,64 H 64 V 0 H 0 Z" /></clipPath><clipPath
id="clipPath30"
clipPathUnits="userSpaceOnUse"><path
id="path28"
d="M 24,52.9823 H 40 V 52.8209 H 24 Z" /></clipPath><clipPath
id="clipPath50"
clipPathUnits="userSpaceOnUse"><path
id="path48"
d="M 8,46.7566 H 56 V 16 H 8 Z" /></clipPath></defs><g
transform="matrix(1.3333333,0,0,-1.3333333,0,85.333333)"
id="g10"><g
id="g12"><g
clip-path="url(#clipPath18)"
id="g14"><g
transform="translate(64,32)"
id="g20"><path
id="path22"
style="fill:#c75c5c;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 0,-17.673 -14.327,-32 -32,-32 -17.673,0 -32,14.327 -32,32 0,17.673 14.327,32 32,32 C -14.327,32 0,17.673 0,0" /></g><g
id="g24"><g
id="g26" /><g
id="g42"><g
style="opacity:0.19999701"
id="g40"
clip-path="url(#clipPath30)"><g
id="g34"
transform="translate(39.9836,52.8257)"><path
id="path32"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0" /></g><g
id="g38"
transform="translate(24,52.8834)"><path
id="path36"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0" /></g></g></g></g><g
id="g44"><g
id="g46" /><g
id="g58"><g
style="opacity:0.19999701"
id="g56"
clip-path="url(#clipPath50)"><g
id="g54"
transform="translate(48,32)"><path
id="path52"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8" /></g></g></g></g><g
transform="translate(48,34)"
id="g60"><path
id="path62"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,6.469 -3.847,12.027 -9.37,14.549 -11.096,15.273 -12,13.954 -12,12.883 v -8.5 C -12,3.555 -11.328,3 -10.5,3 h 3.061 c 1.472,0 1.883,-1.127 0.914,-2.362 l -7.713,-9.829 c -0.969,-1.234 -2.555,-1.234 -3.524,0 l -7.713,9.829 C -26.444,1.873 -26.033,3 -24.561,3 h 3.061 c 0.828,0 1.5,0.555 1.5,1.383 v 8.5 c 0,1.105 -1.504,2.143 -2.753,1.61 -0.002,-10e-4 -0.005,0.002 -0.007,10e-4 -4.311,-2.02 -7.573,-5.905 -8.748,-10.62 C -36.313,3.146 -40,-0.991 -40,-6 c 0,-5.523 4.477,-10 10,-10 H 0 c 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8" /></g><g
transform="translate(39.9836,54.8257)"
id="g64"><path
id="path66"
style="fill:#8c81d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0.002,0.017 0.016,0.043 0.016,0.058 0.016,0.219 0.011,0.17 0,0" /></g><g
transform="translate(24,54.8834)"
id="g68"><path
id="path70"
style="fill:#8c81d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 0,-0.016 0.015,-0.044 0.018,-0.062 0.006,0.112 0,0.163 0,0" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -35,7 +35,7 @@ public interface PathProperties extends Iterable<PathProperty>
boolean add(PathProperty property);
/**
* h Gets the.
* Gets the.
*
* @param path
* the path

View file

@ -27,6 +27,8 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -43,8 +45,6 @@ public class PathPropertyUtils
{
private static final Logger logger = LoggerFactory.getLogger(PathPropertyUtils.class);
public static final String DEFAULT_CHARSET_NAME = "UTF-8";
/**
* Checks if is property line.
*
@ -82,7 +82,7 @@ public class PathPropertyUtils
{
PathProperties result;
result = load(file, DEFAULT_CHARSET_NAME);
result = load(file, StandardCharsets.UTF_8);
//
return result;
@ -93,13 +93,13 @@ public class PathPropertyUtils
*
* @param file
* the file
* @param charsetName
* @param charset
* the charset name
* @return the path properties
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static PathProperties load(final File file, final String charsetName) throws IOException
public static PathProperties load(final File file, final Charset charset) throws IOException
{
PathProperties result;
@ -114,7 +114,7 @@ public class PathPropertyUtils
BufferedReader in = null;
try
{
in = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName));
in = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
result = read(in);
}
finally