Remove old ReportServer and move site related files to ReportSite
directory
@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.mineplex</groupId>
|
|
||||||
<artifactId>mineplex-app</artifactId>
|
|
||||||
<version>dev-SNAPSHOT</version>
|
|
||||||
<relativePath>../app.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<name>ReportServer</name>
|
|
||||||
<artifactId>mineplex-reportserver</artifactId>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>redis.clients</groupId>
|
|
||||||
<artifactId>jedis</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-pool2</artifactId>
|
|
||||||
<version>2.4.2</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-cli</groupId>
|
|
||||||
<artifactId>commons-cli</artifactId>
|
|
||||||
<version>1.3.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-lang3</artifactId>
|
|
||||||
<version>3.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.mineplex</groupId>
|
|
||||||
<artifactId>mineplex-serverdata</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifestEntries>
|
|
||||||
<Main-Class>mineplex.reportserver.ReportServer</Main-Class>
|
|
||||||
</manifestEntries>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
@ -1,57 +0,0 @@
|
|||||||
package mineplex.reportserver;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible for deleting old files (file age is determined by the "last modified" value).
|
|
||||||
*/
|
|
||||||
public class FilePurger implements Runnable
|
|
||||||
{
|
|
||||||
private static final FileFilter FILE_FILTER = file -> file.isFile() && file.getName().endsWith(".json");
|
|
||||||
|
|
||||||
private final File _dataDir;
|
|
||||||
private final Logger _logger;
|
|
||||||
|
|
||||||
public FilePurger(File dataDir, Logger logger)
|
|
||||||
{
|
|
||||||
_dataDir = dataDir;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
Validate.notNull(_dataDir, "Data directory cannot be null.");
|
|
||||||
Validate.isTrue(_dataDir.exists() && dataDir.isDirectory(), "Path non-existent or not a directory: %s", _dataDir.getAbsolutePath());
|
|
||||||
Validate.notNull(_logger, "Logger cannot be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
int purgeCount = 0;
|
|
||||||
|
|
||||||
for (File file : _dataDir.listFiles(FILE_FILTER))
|
|
||||||
{
|
|
||||||
long lastModified = file.lastModified();
|
|
||||||
long timeSince = System.currentTimeMillis() - lastModified;
|
|
||||||
int days = (int) TimeUnit.MILLISECONDS.toDays(timeSince);
|
|
||||||
|
|
||||||
if (days >= 15) // keep files for 15 days
|
|
||||||
{
|
|
||||||
if (!file.delete())
|
|
||||||
{
|
|
||||||
_logger.warning("Cannot delete file: " + file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
purgeCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.info("Purged " + purgeCount + " old chat snapshots.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,142 +0,0 @@
|
|||||||
package mineplex.reportserver;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import redis.clients.jedis.JedisPubSub;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for commands from Redis (such as "deploy" or "destroy") and executes them.
|
|
||||||
*/
|
|
||||||
public class RedisCommandHandler extends JedisPubSub
|
|
||||||
{
|
|
||||||
private static final Gson _gson = new GsonBuilder()
|
|
||||||
.setPrettyPrinting()
|
|
||||||
.create();
|
|
||||||
|
|
||||||
private static final JsonParser _jsonParser = new JsonParser();
|
|
||||||
|
|
||||||
private final File _directory;
|
|
||||||
private final Logger _logger;
|
|
||||||
|
|
||||||
private final ExecutorService _executorService = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
public RedisCommandHandler(File directory, Logger logger)
|
|
||||||
{
|
|
||||||
_directory = directory;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
Validate.notNull(_directory, "Directory cannot be null.");
|
|
||||||
Validate.isTrue(directory.exists() && directory.isDirectory(), "Path non-existent or not a directory: %s", directory.getPath());
|
|
||||||
Validate.notNull(_logger, "Logger cannot be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessage(String channel, String dataString)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (channel.equals(ReportServer.CHANNEL_DEPLOY))
|
|
||||||
{
|
|
||||||
String json = dataString;
|
|
||||||
JsonObject jsonObject = _jsonParser.parse(json).getAsJsonObject();
|
|
||||||
String token = jsonObject.get("token").getAsString();
|
|
||||||
|
|
||||||
File target = new File(_directory, token + ".json");
|
|
||||||
_logger.info("Chat snapshot received [" + token + "], writing to file.");
|
|
||||||
|
|
||||||
if (target.exists() && !jsonObject.has("snapshots"))
|
|
||||||
{
|
|
||||||
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(target)))
|
|
||||||
{
|
|
||||||
JsonObject originalJsonObject = _jsonParser.parse(bufferedReader).getAsJsonObject();
|
|
||||||
JsonObject usernamesObject = jsonObject.get("usernames").getAsJsonObject();
|
|
||||||
|
|
||||||
// retrieve snapshots from original file and add to jsonObject
|
|
||||||
jsonObject.add("snapshots", originalJsonObject.get("snapshots").getAsJsonArray());
|
|
||||||
|
|
||||||
// add new UUID->Usernames, update existing usernames
|
|
||||||
for (Map.Entry<String, JsonElement> entry : originalJsonObject.get("usernames").getAsJsonObject().entrySet())
|
|
||||||
{
|
|
||||||
usernamesObject.addProperty(entry.getKey(), entry.getValue().getAsJsonPrimitive().getAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-write json after updating
|
|
||||||
json = _gson.toJson(jsonObject);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.log(Level.SEVERE, "Exception whilst updating an original snapshot.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
writeFile(target, json);
|
|
||||||
}
|
|
||||||
else if (channel.equals(ReportServer.CHANNEL_DESTROY))
|
|
||||||
{
|
|
||||||
// dataString = token
|
|
||||||
File target = new File(_directory, dataString + ".json");
|
|
||||||
_logger.info("Destroy command received [" + dataString + "].");
|
|
||||||
|
|
||||||
if (target.exists() && !target.delete())
|
|
||||||
{
|
|
||||||
_logger.warning("Failed to delete: " + target.getPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.log(Level.SEVERE, "Error whilst receiving redis message.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeFile(File file, String json)
|
|
||||||
{
|
|
||||||
_executorService.submit(() -> Files.write(file.toPath(), Arrays.asList(json.split("\n"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPMessage(String s, String s1, String s2)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(String s, int i)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUnsubscribe(String s, int i)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPUnsubscribe(String s, int i)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPSubscribe(String s, int i)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
package mineplex.reportserver;
|
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
|
||||||
import redis.clients.jedis.Jedis;
|
|
||||||
import redis.clients.jedis.JedisPool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Establishes and maintains a connection to Redis.
|
|
||||||
*/
|
|
||||||
public class RedisConnectionHandler implements Runnable
|
|
||||||
{
|
|
||||||
private final String _name;
|
|
||||||
private final JedisPool _jedisPool;
|
|
||||||
private final RedisCommandHandler _handler;
|
|
||||||
private final String[] _channels;
|
|
||||||
private final Logger _logger;
|
|
||||||
|
|
||||||
private long _lastConnectionMillis = -1;
|
|
||||||
private Throwable _lastThrowable = null;
|
|
||||||
|
|
||||||
public RedisConnectionHandler(String name, JedisPool jedisPool, RedisCommandHandler handler, String[] channels, Logger logger)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
_jedisPool = jedisPool;
|
|
||||||
_handler = handler;
|
|
||||||
_channels = channels;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
Validate.isTrue(channels.length > 0, "Must provide at least one channel.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
while (!Thread.interrupted())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
registerChannelHandlers();
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
// Only log new errors (prevents same error being spammed)
|
|
||||||
if (_lastThrowable == null || !e.getClass().equals(_lastThrowable.getClass()))
|
|
||||||
{
|
|
||||||
if (_lastThrowable == null) // connection just failed
|
|
||||||
{
|
|
||||||
_lastConnectionMillis = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.log(Level.SEVERE, prefixMessage(
|
|
||||||
"Exception in Redis connection"
|
|
||||||
+ (_lastConnectionMillis != -1 ? " (no connection for " + getLastConnectionDuration() + ")" : "")
|
|
||||||
+ ", attempting to regain connection."
|
|
||||||
), e);
|
|
||||||
|
|
||||||
_lastThrowable = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1000 * 5);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_jedisPool.destroy();
|
|
||||||
_logger.warning("Thread interrupted, end of connection.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerChannelHandlers()
|
|
||||||
{
|
|
||||||
try (Jedis jedis = _jedisPool.getResource())
|
|
||||||
{
|
|
||||||
connectionEstablished();
|
|
||||||
jedis.subscribe(_handler, _channels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void connectionEstablished()
|
|
||||||
{
|
|
||||||
// subscribe blocks so we need to do all this before
|
|
||||||
_logger.info(
|
|
||||||
_lastThrowable == null
|
|
||||||
? prefixMessage("Connected.")
|
|
||||||
: prefixMessage(String.format("Connected after %s.", getLastConnectionDuration()))
|
|
||||||
);
|
|
||||||
|
|
||||||
_lastThrowable = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String prefixMessage(String message)
|
|
||||||
{
|
|
||||||
return String.format("[%s] %s", _name, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getLastConnectionDuration()
|
|
||||||
{
|
|
||||||
return DurationFormatUtils.formatDurationWords(System.currentTimeMillis() - _lastConnectionMillis, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
package mineplex.reportserver;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import mineplex.serverdata.Utility;
|
|
||||||
import mineplex.serverdata.redis.RedisConfig;
|
|
||||||
import mineplex.serverdata.servers.ConnectionData;
|
|
||||||
import mineplex.serverdata.servers.ServerManager;
|
|
||||||
import org.apache.commons.cli.CommandLine;
|
|
||||||
import org.apache.commons.cli.CommandLineParser;
|
|
||||||
import org.apache.commons.cli.DefaultParser;
|
|
||||||
import org.apache.commons.cli.Option;
|
|
||||||
import org.apache.commons.cli.Options;
|
|
||||||
import org.apache.commons.cli.ParseException;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import redis.clients.jedis.JedisPool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main class for the Report server, parses command line arguments and initializes the Report server.
|
|
||||||
*/
|
|
||||||
public class ReportServer
|
|
||||||
{
|
|
||||||
public static final String CHANNEL_DEPLOY = "reportserver:deploy";
|
|
||||||
public static final String CHANNEL_DESTROY = "reportserver:destroy";
|
|
||||||
|
|
||||||
private static final String[] CHANNELS = new String[]{CHANNEL_DEPLOY, CHANNEL_DESTROY};
|
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
{
|
|
||||||
System.setProperty("java.util.logging.SimpleFormatter.format", "%4$s: %5$s%6$s%n"); // Nicer log output
|
|
||||||
|
|
||||||
Logger logger = Logger.getLogger("ReportServer");
|
|
||||||
logger.info("Starting report server.");
|
|
||||||
|
|
||||||
Options options = new Options();
|
|
||||||
|
|
||||||
Option dirOption = Option.builder("dataDir")
|
|
||||||
.hasArg()
|
|
||||||
.longOpt("dataDirectory")
|
|
||||||
.desc("Sets the data directory where the JSON files will be stored.")
|
|
||||||
.type(File.class)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
options.addOption(dirOption);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CommandLineParser parser = new DefaultParser();
|
|
||||||
CommandLine cmd = parser.parse(options, args);
|
|
||||||
File dataDirectory = (File) cmd.getParsedOptionValue(dirOption.getOpt());
|
|
||||||
|
|
||||||
if (dataDirectory == null)
|
|
||||||
{
|
|
||||||
dataDirectory = new File("data");
|
|
||||||
}
|
|
||||||
|
|
||||||
new ReportServer(ServerManager.getDefaultConfig(), dataDirectory, logger);
|
|
||||||
}
|
|
||||||
catch (ParseException e)
|
|
||||||
{
|
|
||||||
logger.log(Level.SEVERE, "Failed to parse arguments.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final File _dataDirectory;
|
|
||||||
private final Logger _logger;
|
|
||||||
|
|
||||||
private final RedisCommandHandler _handler;
|
|
||||||
private final ScheduledExecutorService _executorService = Executors.newScheduledThreadPool(1);
|
|
||||||
|
|
||||||
public ReportServer(RedisConfig redisConfig, File dataDirectory, Logger logger)
|
|
||||||
{
|
|
||||||
_dataDirectory = dataDirectory;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
Validate.notNull(_dataDirectory, "Data directory cannot be null.");
|
|
||||||
|
|
||||||
// thrown if path exists but is not a directory
|
|
||||||
Validate.isTrue(!_dataDirectory.exists() || _dataDirectory.isDirectory(), "Not a directory: %s", _dataDirectory.getPath());
|
|
||||||
|
|
||||||
// throws if directory doesn't exist and cannot be created
|
|
||||||
Validate.isTrue(_dataDirectory.exists() || _dataDirectory.mkdir(), "Unable to create directory: " + _dataDirectory.getPath());
|
|
||||||
|
|
||||||
_handler = new RedisCommandHandler(_dataDirectory, _logger);
|
|
||||||
initializeConnectionsConfig(redisConfig);
|
|
||||||
schedulePurgeTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeConnectionsConfig(RedisConfig redisConfig)
|
|
||||||
{
|
|
||||||
redisConfig.getConnections(false, null).forEach(this::initializeConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeConnection(ConnectionData connectionData)
|
|
||||||
{
|
|
||||||
JedisPool jedisPool = Utility.generatePool(connectionData);
|
|
||||||
String connectionName = connectionData.getName();
|
|
||||||
Thread thread = new Thread(new RedisConnectionHandler(connectionName, jedisPool, _handler, CHANNELS, _logger), connectionName + " - Redis PubSub Thread");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void schedulePurgeTask()
|
|
||||||
{
|
|
||||||
_executorService.scheduleAtFixedRate(new FilePurger(_dataDirectory, _logger), 0, 30, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 227 KiB After Width: | Height: | Size: 227 KiB |
Before Width: | Height: | Size: 971 KiB After Width: | Height: | Size: 971 KiB |