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 |