diff --git a/pom.xml b/pom.xml
index c356eb2..3a412fd 100755
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
org.parabot
client
2.6.4
-
+
jar
@@ -73,7 +73,7 @@
org.parabot
internal-api
- 1.4.46
+ 1.51.1
diff --git a/src/main/java/org/parabot/core/Core.java b/src/main/java/org/parabot/core/Core.java
index 16f0204..240b8be 100644
--- a/src/main/java/org/parabot/core/Core.java
+++ b/src/main/java/org/parabot/core/Core.java
@@ -20,6 +20,7 @@ import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Date;
/**
* The core of parabot
@@ -206,31 +207,11 @@ public class Core {
}
/**
- * Validates the cache and removes the cache contents if required
+ * Method that removes the cache contents after 3 days
*/
private static void validateCache() {
- File[] cache = Directories.getCachePath().listFiles();
- Integer lowest = null;
- if (cache != null) {
- for (File f : cache) {
- int date = (int) (f.lastModified() / 1000);
- if (lowest == null || date < lowest) {
- lowest = date;
- }
- }
- }
-
- try {
- JSONObject object = (JSONObject) WebUtil.getJsonParser().parse(WebUtil.getContents("http://bdn.parabot.org/api/v2/bot/cache", "date=" + lowest));
- if ((boolean) object.get("result")) {
- Core.verbose("Making space for the latest cache files");
- Directories.clearCache();
- } else {
- Core.verbose("Cache is up to date");
- }
- } catch (MalformedURLException | ParseException e) {
- e.printStackTrace();
- }
+ // Already handled by Directories initiating
+ // Method will be used once BDN V3 has a functionality for this
}
public static void downloadNewVersion() {
diff --git a/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java b/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java
index 40fc7a4..8de3935 100644
--- a/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java
+++ b/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java
@@ -1,5 +1,6 @@
package org.parabot.core.asm.redirect;
+import org.parabot.core.Core;
import org.parabot.core.asm.RedirectClassAdapter;
import org.parabot.environment.scripts.Script;
@@ -11,9 +12,11 @@ import java.lang.reflect.Method;
public class ClassRedirect {
public static Object newInstance(Class> c) throws IllegalAccessException, InstantiationException {
- if (validStack()){
+ if (validStack()) {
return c.newInstance();
}
+
+ System.err.println(c.getName() + ".newInstance() Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
@@ -22,7 +25,7 @@ public class ClassRedirect {
return c.getDeclaredField(s);
}
- System.out.println(c.getName() + "." + c.getDeclaredField(s) + " Blocked.");
+ System.err.println(c.getName() + "." + c.getDeclaredField(s) + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
@@ -31,17 +34,20 @@ public class ClassRedirect {
return c.getDeclaredMethod(name, params);
}
- System.out.println(c.getName() + "#" + c.getDeclaredMethod(name, params) + " Blocked.");
+ System.err.println(c.getName() + "#" + c.getDeclaredMethod(name, params) + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
public static Class> forName(String name) throws ClassNotFoundException {
- if (name.contains("parabot"))
+ if (name.contains("parabot")) {
throw new ClassNotFoundException();
+ }
+ Core.verbose("Received #forName(" + name + ") call");
return Class.forName(name);
}
public static ClassLoader getClassLoader(Class> c) {
+ System.err.println(c.getName() + "#getClassLoader()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
@@ -49,7 +55,7 @@ public class ClassRedirect {
if (validStack()) {
return c.getDeclaredFields();
}
- System.out.println(c.getName() + "#getDeclaredFields()" + " Blocked.");
+ System.err.println(c.getName() + "#getDeclaredFields()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
@@ -57,31 +63,31 @@ public class ClassRedirect {
if (validStack()) {
return c.getDeclaredMethods();
}
- System.out.println(c.getName() + "#getDeclaredMethods()" + " Blocked.");
+ System.err.println(c.getName() + "#getDeclaredMethods()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
- public static Field[] getFields(Class> c){
+ public static Field[] getFields(Class> c) {
if (validStack()) {
return c.getFields();
}
- System.out.println(c.getName() + "#getFields()" + " Blocked.");
+ System.err.println(c.getName() + "#getFields()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
- public static Annotation[] getAnnotations(Class> c){
- if (validStack()){
+ public static Annotation[] getAnnotations(Class> c) {
+ if (validStack()) {
return c.getAnnotations();
}
- System.out.println(c.getName() + "#getFields()" + " Blocked.");
+ System.err.println(c.getName() + "#getFields()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
- public static Method[] getMethods(Class> c){
+ public static Method[] getMethods(Class> c) {
if (validStack()) {
return c.getMethods();
}
- System.out.println(c.getName() + "#getMethods()" + " Blocked.");
+ System.err.println(c.getName() + "#getMethods()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
@@ -89,7 +95,7 @@ public class ClassRedirect {
if (validStack()) {
return c.getMethod(name, params);
}
- System.out.println(c.getName() + "#getMethod()" + " Blocked.");
+ System.err.println(c.getName() + "#getMethod()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
@@ -98,13 +104,14 @@ public class ClassRedirect {
if (validStack()) {
return c.getField(name);
}
- System.out.println(c.getName() + "#getField()" + " Blocked.");
+ System.err.println(c.getName() + "#getField()" + " Blocked.");
throw RedirectClassAdapter.createSecurityException();
}
public static String getName(Class> c) {
- if (c.getName().contains("parabot"))
+ if (c.getName().contains("parabot")) {
return "java.lang.String";
+ }
return c.getName();
}
diff --git a/src/main/java/org/parabot/core/asm/redirect/ProcessBuilderRedirect.java b/src/main/java/org/parabot/core/asm/redirect/ProcessBuilderRedirect.java
index 9df397d..ddee308 100644
--- a/src/main/java/org/parabot/core/asm/redirect/ProcessBuilderRedirect.java
+++ b/src/main/java/org/parabot/core/asm/redirect/ProcessBuilderRedirect.java
@@ -2,4 +2,7 @@ package org.parabot.core.asm.redirect;
public class ProcessBuilderRedirect {
+ public static ProcessBuilder redirectErrorStream(ProcessBuilder pb, boolean redirectErrorStream) {
+ return pb;
+ }
}
diff --git a/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java b/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java
index 41f48ef..f5074c2 100644
--- a/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java
+++ b/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java
@@ -5,35 +5,40 @@ import org.parabot.core.asm.RedirectClassAdapter;
import java.io.IOException;
public class RuntimeRedirect {
-
- public static Runtime getRuntime(){
- return Runtime.getRuntime();
- }
-
- public static int availableProcessors(Runtime r){
- return 2;
- }
- public static long totalMemory(Runtime runtime){
- return (long) 1024;
- }
+ public static Runtime getRuntime() {
+ return Runtime.getRuntime();
+ }
- public static long freeMemory(Runtime runtime){
- return (long) 1024;
- }
-
- public static Process exec(Runtime r,String s){
- if (s.contains("ping")){
- System.out.println("Faked attempted command: " + s);
- try {
- return r.exec("ping 127.0.0.1");
- } catch (IOException e) {
- throw RedirectClassAdapter.createSecurityException();
- }
- }else{
- System.out.println("Blocked attempted command: " + s);
- throw RedirectClassAdapter.createSecurityException();
- }
- }
+ public static int availableProcessors(Runtime r) {
+ return 2;
+ }
+
+ public static long totalMemory(Runtime runtime) {
+ return (long) 1024;
+ }
+
+ public static long freeMemory(Runtime runtime) {
+ return (long) 1024;
+ }
+
+ public static Process exec(Runtime r, String[] s) {
+ System.out.println("Blocked attempted command: " + s);
+ throw RedirectClassAdapter.createSecurityException();
+ }
+
+ public static Process exec(Runtime r, String s) {
+ if (s.contains("ping")) {
+ System.out.println("Faked attempted command: " + s);
+ try {
+ return r.exec("ping 127.0.0.1");
+ } catch (IOException e) {
+ throw RedirectClassAdapter.createSecurityException();
+ }
+ } else {
+ System.out.println("Blocked attempted command: " + s);
+ throw RedirectClassAdapter.createSecurityException();
+ }
+ }
}
diff --git a/src/main/java/org/parabot/core/asm/redirect/SystemRedirect.java b/src/main/java/org/parabot/core/asm/redirect/SystemRedirect.java
index f18fe1f..5ddfe88 100644
--- a/src/main/java/org/parabot/core/asm/redirect/SystemRedirect.java
+++ b/src/main/java/org/parabot/core/asm/redirect/SystemRedirect.java
@@ -26,14 +26,12 @@ public class SystemRedirect {
public static String getProperty(String s) {
String value;
switch (s) {
- case "user.home":
- value = Directories.getCachePath().getAbsolutePath();
- break;
case "java.class.path":
value = ".";
break;
default:
value = System.getProperty(s);
+ break;
}
System.out.printf("GetSystemProp %s = %s\n", s, value);
return value;
@@ -42,9 +40,6 @@ public class SystemRedirect {
public static String getProperty(String s, String s2) {
String value = null;
switch (s2) {
- case "user.home":
- value = Directories.getCachePath().getAbsolutePath();
- break;
case "java.class.path":
value = ".";
break;
@@ -52,14 +47,12 @@ public class SystemRedirect {
if (value == null) {
switch (s) {
- case "user.home":
- value = Directories.getCachePath().getAbsolutePath();
- break;
case "java.class.path":
value = ".";
break;
default:
value = System.getProperty(s);
+ break;
}
}
System.out.printf("GetSystemProp %s = %s\n", s, value);
diff --git a/src/main/java/org/parabot/core/build/BuildPath.java b/src/main/java/org/parabot/core/build/BuildPath.java
index b80e78e..5ca4971 100644
--- a/src/main/java/org/parabot/core/build/BuildPath.java
+++ b/src/main/java/org/parabot/core/build/BuildPath.java
@@ -1,9 +1,5 @@
package org.parabot.core.build;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.net.URLClassLoader;
-
/**
*
* Class used for adding urls to the buildpath
@@ -11,16 +7,6 @@ import java.net.URLClassLoader;
* @author Everel
*
*/
-public class BuildPath {
-
- public static void add(final URL url) {
- try {
- Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
- method.setAccessible(true);
- method.invoke((URLClassLoader) ClassLoader.getSystemClassLoader(), url);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+public class BuildPath extends org.parabot.api.io.build.BuildPath {
}
diff --git a/src/main/java/org/parabot/core/lib/Library.java b/src/main/java/org/parabot/core/lib/Library.java
index 9ac49e5..c8a7645 100644
--- a/src/main/java/org/parabot/core/lib/Library.java
+++ b/src/main/java/org/parabot/core/lib/Library.java
@@ -1,67 +1,10 @@
package org.parabot.core.lib;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-
/**
*
* @author Everel
*
*/
-public abstract class Library {
-
- /**
- * Determines if this library jar has already been downloaded.
- * @return false if library jar has not been downloaded, otherwise true
- */
- public boolean hasJar() {
- return getJarFile().exists();
- }
-
- /**
- * Adds the library to the buildpath and validates if it has been added
- */
- public abstract void init();
-
- /**
- * Determines if library has been added to the buildpath
- * @return true if library has been added to the buildpath, otherwise false.
- */
- public abstract boolean isAdded();
-
- /**
- * Gets the local file target/location of the jar file
- * @return local file (target) to library
- */
- public abstract File getJarFile();
-
- /**
- * Gets download url to the library
- * @return url
- */
- public abstract URL getDownloadLink();
-
- /**
- * Defines if the system requires a jar
- * @return boolean
- */
- public abstract boolean requiresJar();
-
-
- /**
- * Fetches URL from {@link Library#getJarFile()}
- * @return URL to local library jar file
- */
- public URL getJarFileURL() {
- try {
- return getJarFile().toURI().toURL();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public abstract String getLibraryName();
+public abstract class Library extends org.parabot.api.io.libraries.Library {
}
diff --git a/src/main/java/org/parabot/core/ui/BotUI.java b/src/main/java/org/parabot/core/ui/BotUI.java
index e2f8b1c..8ec5532 100644
--- a/src/main/java/org/parabot/core/ui/BotUI.java
+++ b/src/main/java/org/parabot/core/ui/BotUI.java
@@ -1,10 +1,12 @@
package org.parabot.core.ui;
+import javafx.application.Application;
import org.parabot.core.Configuration;
import org.parabot.core.Context;
import org.parabot.core.Directories;
import org.parabot.core.ui.components.GamePanel;
import org.parabot.core.ui.components.VerboseLoader;
+import org.parabot.core.ui.components.notifications.NotificationUI;
import org.parabot.core.ui.images.Images;
import org.parabot.core.ui.utils.SwingUtil;
import org.parabot.environment.OperatingSystem;
@@ -105,6 +107,9 @@ public class BotUI extends JFrame implements ActionListener, ComponentListener,
JMenuItem cacheClear = new JMenuItem("Clear cache");
cacheClear.setIcon(new ImageIcon(Images.getResource("/storage/images/trash.png")));
+ JMenuItem notifications = new JMenuItem("Notifications");
+ notifications.setIcon(new ImageIcon(Images.getResource("/storage/images/bell.png")));
+
screenshot.addActionListener(this);
proxy.addActionListener(this);
randoms.addActionListener(this);
@@ -113,6 +118,7 @@ public class BotUI extends JFrame implements ActionListener, ComponentListener,
explorer.addActionListener(this);
exit.addActionListener(this);
cacheClear.addActionListener(this);
+ notifications.addActionListener(this);
run.addActionListener(this);
pause.addActionListener(this);
@@ -131,6 +137,7 @@ public class BotUI extends JFrame implements ActionListener, ComponentListener,
scripts.add(stop);
features.add(cacheClear);
+ features.add(notifications);
menuBar.add(file);
menuBar.add(scripts);
@@ -237,6 +244,9 @@ public class BotUI extends JFrame implements ActionListener, ComponentListener,
case "Clear cache":
Directories.clearCache();
break;
+ case "Notifications":
+ Application.launch(NotificationUI.class);
+ break;
default:
System.out.println("Invalid command: " + command);
}
diff --git a/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java b/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java
new file mode 100644
index 0000000..bc55311
--- /dev/null
+++ b/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java
@@ -0,0 +1,20 @@
+package org.parabot.core.ui.components.notifications;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+import org.parabot.core.Configuration;
+
+public class NotificationUI extends Application {
+
+ @Override
+ public void start(Stage stage) throws Exception {
+ //noinspection RedundantCast
+ BorderPane root = (BorderPane) FXMLLoader.load(this.getClass().getResource("/storage/ui/notifications.fxml"));
+ stage.setTitle(Configuration.BOT_TITLE);
+ stage.setScene(new Scene(root));
+ stage.show();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/parabot/core/ui/components/notifications/NotificationUIController.java b/src/main/java/org/parabot/core/ui/components/notifications/NotificationUIController.java
new file mode 100644
index 0000000..bc7a42d
--- /dev/null
+++ b/src/main/java/org/parabot/core/ui/components/notifications/NotificationUIController.java
@@ -0,0 +1,72 @@
+package org.parabot.core.ui.components.notifications;
+
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.ListView;
+import javafx.stage.Stage;
+import org.parabot.api.notifications.NotificationManager;
+import org.parabot.api.notifications.types.NotificationType;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * @author JKetelaar
+ */
+public class NotificationUIController implements Initializable {
+
+ @FXML // fx:id="availableTypes"
+ private ListView availableTypes;
+
+ @FXML // fx:id="enabledTypes"
+ private ListView enabledTypes;
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ this.refreshTypes();
+ }
+
+ private void refreshTypes() {
+ availableTypes.getItems().clear();
+ for (NotificationType notificationType : NotificationManager.getContext().getAvailableNotificationTypes()) {
+ availableTypes.getItems().add(notificationType.getName());
+ }
+
+ enabledTypes.getItems().clear();
+ for (NotificationType notificationType : NotificationManager.getContext().getEnabledTypes()) {
+ enabledTypes.getItems().add(notificationType.getName());
+ }
+ }
+
+ @FXML
+ private void enableType() {
+ Object object = availableTypes.getSelectionModel().getSelectedItem();
+ if (object != null) {
+ String name = (String) object;
+
+ NotificationType type = NotificationManager.getContext().getNotificationType(name);
+ NotificationManager.getContext().enableNotificationType(type);
+
+ this.refreshTypes();
+ }
+ }
+
+ @FXML
+ private void disableType() {
+ Object object = enabledTypes.getSelectionModel().getSelectedItem();
+ if (object != null) {
+ String name = (String) object;
+
+ NotificationType type = NotificationManager.getContext().getNotificationType(name);
+ NotificationManager.getContext().disableNotificationType(type);
+
+ this.refreshTypes();
+ }
+ }
+
+ @FXML
+ private void save() {
+ Stage stage = (Stage) this.enabledTypes.getScene().getWindow();
+ stage.close();
+ }
+}
diff --git a/src/main/java/org/parabot/environment/Environment.java b/src/main/java/org/parabot/environment/Environment.java
index e17d0fd..75b7bf8 100644
--- a/src/main/java/org/parabot/environment/Environment.java
+++ b/src/main/java/org/parabot/environment/Environment.java
@@ -12,40 +12,51 @@ import java.util.LinkedList;
/**
- *
- * Initiliazes the bot environment
- *
- * @author Everel
- *
+ * Initializes the bot environment
+ *
+ * @author Everel, JKetelaar
*/
-public class Environment {
+public class Environment extends org.parabot.api.io.libraries.Environment {
- /**
- * Loads a new environment
- *
- * @param desc
- */
- public static void load(final ServerDescription desc) {
-
- LinkedList libs = new LinkedList<>();
- libs.add(new JavaFX());
-
- for(Library lib : libs) {
- if (lib.requiresJar()) {
- if (!lib.hasJar()) {
- Core.verbose("Downloading " + lib.getLibraryName() + "...");
- VerboseLoader.setState("Downloading " + lib.getLibraryName() + "...");
- WebUtil.downloadFile(lib.getDownloadLink(), lib.getJarFile(), VerboseLoader.get());
- Core.verbose("Downloaded " + lib.getLibraryName() + ".");
- }
- Core.verbose("Initializing " + lib.getLibraryName());
- lib.init();
- }
- }
-
- Core.verbose("Loading server: " + desc.toString() + "...");
+ private static LinkedList libs = new LinkedList<>();
- ServerParser.SERVER_CACHE.get(desc).run();
-
- }
+ static {
+ libs.add(new JavaFX());
+ }
+
+ /**
+ * Loads a new environment
+ *
+ * @param desc
+ */
+ public static void load(final ServerDescription desc) {
+ for (Library lib : libs) {
+ loadLibrary(lib, true);
+ }
+
+ Core.verbose("Loading server: " + desc.toString() + "...");
+
+ ServerParser.SERVER_CACHE.get(desc).run();
+ }
+
+ /**
+ * Loads library into environment
+ *
+ * @param library
+ * @param verboseLoader defines if verboseLoader should be enabled
+ */
+ public static void loadLibrary(Library library, boolean verboseLoader) {
+ if (library.requiresJar()) {
+ if (!library.hasJar()) {
+ Core.verbose("Downloading " + library.getLibraryName() + "...");
+ if (verboseLoader) {
+ VerboseLoader.setState("Downloading " + library.getLibraryName() + "...");
+ }
+ WebUtil.downloadFile(library.getDownloadLink(), library.getJarFile(), VerboseLoader.get());
+ Core.verbose("Downloaded " + library.getLibraryName() + ".");
+ }
+ Core.verbose("Initializing " + library.getLibraryName());
+ library.init();
+ }
+ }
}
diff --git a/src/main/java/org/parabot/environment/api/utils/StringUtils.java b/src/main/java/org/parabot/environment/api/utils/StringUtils.java
index 892c75b..8911317 100644
--- a/src/main/java/org/parabot/environment/api/utils/StringUtils.java
+++ b/src/main/java/org/parabot/environment/api/utils/StringUtils.java
@@ -1,52 +1,10 @@
package org.parabot.environment.api.utils;
+import org.parabot.api.misc.StringUtil;
+
/**
* @author mkyong, JKetelaar
*/
-public class StringUtils {
+public class StringUtils extends StringUtil {
- private static java.util.Random random = new java.util.Random();
- private static char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
-
- public static String convertHexToString(String hex) {
-
- StringBuilder sb = new StringBuilder();
- StringBuilder temp = new StringBuilder();
-
- for (int i = 0; i < hex.length() - 1; i += 2) {
-
- // grab the hex in pairs
- String output = hex.substring(i, (i + 2));
- // convert hex to decimal
- int decimal = Integer.parseInt(output, 16);
- // convert the decimal to character
- sb.append((char) decimal);
-
- temp.append(decimal);
- }
-
- return sb.toString();
- }
-
- public static String implode(String separator, String... data) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < data.length - 1; i++) {
- //data.length - 1 => to not add separator at the end
- if (!data[i].matches(" *")) {//empty string are ""; " "; " "; and so on
- sb.append(data[i]);
- sb.append(separator);
- }
- }
- sb.append(data[data.length - 1].trim());
- return sb.toString();
- }
-
- public static String randomString(final int length) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < 20; i++) {
- char c = chars[random.nextInt(chars.length)];
- sb.append(c);
- }
- return sb.toString();
- }
}
diff --git a/src/main/java/org/parabot/environment/randoms/RandomType.java b/src/main/java/org/parabot/environment/randoms/RandomType.java
index 250bd9a..3743812 100644
--- a/src/main/java/org/parabot/environment/randoms/RandomType.java
+++ b/src/main/java/org/parabot/environment/randoms/RandomType.java
@@ -6,9 +6,9 @@ package org.parabot.environment.randoms;
public enum RandomType {
SCRIPT(0, "Script"),
- ON_SCRIPT_START(0, "On script start"),
- ON_SERVER_START(0, "On server start"),
- ON_SCRIPT_FINISH(0, "On script finish");
+ ON_SCRIPT_START(1, "On script start"),
+ ON_SERVER_START(2, "On server start"),
+ ON_SCRIPT_FINISH(3, "On script finish");
private int id;
private String name;
diff --git a/src/main/resources/storage/images/bell.png b/src/main/resources/storage/images/bell.png
new file mode 100644
index 0000000..5aab10f
Binary files /dev/null and b/src/main/resources/storage/images/bell.png differ
diff --git a/src/main/resources/storage/ui/notifications.fxml b/src/main/resources/storage/ui/notifications.fxml
new file mode 100644
index 0000000..1e6ac7b
--- /dev/null
+++ b/src/main/resources/storage/ui/notifications.fxml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/org/parabot/CacheValidationTest.java b/src/test/java/org/parabot/CacheValidationTest.java
new file mode 100644
index 0000000..24aa1de
--- /dev/null
+++ b/src/test/java/org/parabot/CacheValidationTest.java
@@ -0,0 +1,32 @@
+package org.parabot;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.parabot.core.Directories;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author JKetelaar
+ */
+public class CacheValidationTest {
+
+ @Test
+ public void test() throws IOException {
+ Directories.validate();
+
+ File fileOne = new File(Directories.getCachePath(), "should-exist.tmp");
+ File fileTwo = new File(Directories.getCachePath(), "should-not-exist.tmp");
+
+ fileOne.createNewFile();
+ fileTwo.createNewFile();
+
+ fileTwo.setLastModified(System.currentTimeMillis() / 1000 - 350000);
+
+ Directories.clearCache(259200, false);
+
+ Assert.assertTrue(fileOne.exists());
+ Assert.assertTrue(!fileTwo.exists());
+ }
+}