-
Notifications
You must be signed in to change notification settings - Fork 3
Using the Object Cache
To get started with an Object Cache, you first need to make your own object to store data in:
(or just implement the ObjectCacheable
interface provided by Payload)
For this example, I'm going to be using a simple Faction object.
import com.jonahseguin.payload.object.obj.ObjectCacheable;
import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
@Entity("factions")
public class Faction implements ObjectCacheable {
@Id private ObjectId id = new ObjectId();
private String name;
public Faction(String name) {
this.name = name;
}
@Override
public String getIdentifier() {
return this.name;
// For factions we will use the faction's name as the identifier, because no two factions
// can have the same name.
// For other objects you would probably want to use an ID or other unique field.
// Objects are stored by their identifier, so whatever you want to look up the object by,
// make that the identifier.
}
@Override
public boolean persist() {
return true; // Persist this!
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ObjectId getId() {
return id;
}
}
It is very important that you add the @Entity("collectionName")
annotation to your object. Also, fields that you wish to persist cannot be final. We use Morphia for Object Mapping (ORM). Visit their GitHub/website for more information on how to set up your objects.
The next step is to set up your Object Cache instance.
First, we will need to provide our database credentials and instances into a CacheDatabase
object. Of course, you should use your own credentials. (don't write them in plain-text! use a config!).
import com.jonahseguin.payload.common.cache.CacheDatabase;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import redis.clients.jedis.Jedis;
import org.bukkit.plugin.java.JavaPlugin;
public class FactionManager {
public FactionManager(JavaPlugin plugin) {
MongoClient mongoClient = new MongoClient("localhost");
MongoDatabase database = mongoClient.getDatabase("myDatabase");
Morphia morphia = new Morphia();
Jedis jedis = new Jedis("localhost");
morphia.mapPackage("com.jonahseguin.payloadtest"); // Maps all my objects in this package
Datastore datastore = morphia.createDatastore(mongoClient, "myDatabase");
CacheDatabase cacheDatabase = new CacheDatabase(mongoClient, database, jedis, morphia, datastore); // Our cache database we will provide to Payload!
}
}
Next, we will go about the creation of our PayloadObjectCache
, using the provided builder:
this.factionCache = new ObjectCacheBuilder<>(plugin, Faction.class)
.withCreateOnNull(false)
.withDatabase(cacheDatabase)
.withDebugger(new MyDebugger())
.withRedisKey("factions")
.withRedis(true)
.withSaveAfterLoad(false)
.withMongo(true)
.withMongoIdentifier("name") // our 'name' field in our Faction object is our identifier
.withObjectInstantiator(new ObjectInstantiator<Faction>() {
@Override
public Faction instantiate(String s) {
return new Faction(s);
}
@Override
public Faction instantiate() {
return new Faction("defaultName"); // this name will be changed by Morphia
}
})
.build();
You're probably wondering about the MyDebugger class. You need to pass your own Debugger for Payload to use to handle errors, exceptions, and debug messages. Simply implement the CacheDebugger
class:
I highly recommend using the Sentry.io error capturing library for all your projects!, however you can handle the errors however you want (in-game stack traces, etc.) Don't suppress your errors, or debugging will be a nightmare!
import com.jonahseguin.payload.common.cache.CacheDebugger;
import io.sentry.Sentry;
import io.sentry.event.Event;
import io.sentry.event.EventBuilder;
import io.sentry.event.interfaces.ExceptionInterface;
import org.bukkit.Bukkit;
public class MyDebugger implements CacheDebugger {
@Override
public void debug(String s) {
Sentry.capture(s);
}
@Override
public void error(Exception e) {
Sentry.capture(e);
}
@Override
public void error(String s) {
Sentry.capture(s);
}
@Override
public void error(Exception e, String s) {
capture(e, s);
}
@Override
public boolean onStartupFailure() {
Sentry.capture("Startup failed for Payload cache!");
return true; // Return whether or not to shut down the cache on failure (True = shutdown)
}
public static void capture(String message) {
// using Sentry.io for error capturing
Bukkit.getServer().getLogger().warning("[Error] " + message);
Sentry.capture(
new EventBuilder()
.withMessage(message)
.withLevel(Event.Level.ERROR)
);
}
public static void capture(Throwable throwable, String message) {
// using Sentry.io for error capturing
Bukkit.getServer().getLogger().warning("[Error] " + message);
Sentry.capture(
new EventBuilder()
.withMessage(message)
.withLevel(Event.Level.ERROR)
.withSentryInterface(new ExceptionInterface(throwable))
);
}
Don't forget to initialize your cache at startup!
this.factionCache.init(); // Do not forget this part! Initialize your cache!
And make sure to call shutdown()
during your plugin's onDisable()
public void onDisable() {
// Call this after you have saved everything.
this.factionManager.getCache().shutdown();
}
And now you're ready to start using your object cache!
Faction faction = factionObjectCache.get("factionName");
factionObjectCache.getFrom(OLayerType.REDIS, "factionName");
factionObjectCache.uncache(faction);
// or
factionObjectCache.uncache("factionName");
factionObjectCache.saveEverywhere(faction);
factionObjectCache.deleteEverywhere("factionName");
factionObjectCache.getLayerController().getLocalLayer().provide("factionName");
// or delete:
factionObjectCache.getLayerController().getLocalLayer().remove("factionName");
// from any layer
factionObjectCache.getLayerController().getRedisLayer()...
factionObjectCache.getLayerController().getMongoLayer()...
You can even access the controller for each cached object to see how they were cached, or manually load the object
OLayerType cacheSource = factionObjectCache.getController("factionName").getLoadedFrom(); // Local / Redis / Mongo
// or manually load it / cache it
Faction faction = factionObjectCache.getController("factionName").cache(); // If no controller exists, one is automatically created
if (faction == null) {
// doesn't exist; create the faction [there is also a setting to allow Payload to do this automatically: 'createOnNull']
faction = new Faction("factionName");
factionObjectCache.saveEverywhere(faction);
}
Developed by Jonah Seguin
Visit my website @ jonahseguin.com to contact me if you are interested in working with me.
Copyright (c) 2018 Jonah Seguin. All rights reserved.
asynchronous fail-safe profile & object caching
Read First: Getting Started
How To: Profile Cache
How To: Object Cache
by Jonah Seguin
Copyright (c) 2018 Jonah Seguin. All rights reserved.