MassiveStore improvements
This commit is contained in:
		
							parent
							
								
									525d904f8f
								
							
						
					
					
						commit
						e16333b822
					
				| @ -3,8 +3,10 @@ package com.massivecraft.massivecore; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.Map; | ||||
| import java.util.Map.Entry; | ||||
| import java.util.Set; | ||||
| import java.util.TreeSet; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| 
 | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.command.CommandSender; | ||||
| @ -14,6 +16,8 @@ import org.bukkit.event.EventPriority; | ||||
| import org.bukkit.event.entity.EntityDamageByBlockEvent; | ||||
| import org.bukkit.event.entity.EntityDamageEvent.DamageCause; | ||||
| import org.bukkit.event.player.AsyncPlayerChatEvent; | ||||
| import org.bukkit.event.player.AsyncPlayerPreLoginEvent; | ||||
| import org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result; | ||||
| import org.bukkit.event.player.PlayerKickEvent; | ||||
| import org.bukkit.event.player.PlayerLoginEvent; | ||||
| import org.bukkit.event.player.PlayerChatTabCompleteEvent; | ||||
| @ -34,6 +38,7 @@ import com.massivecraft.massivecore.store.Coll; | ||||
| import com.massivecraft.massivecore.store.SenderColl; | ||||
| import com.massivecraft.massivecore.util.IdUtil; | ||||
| import com.massivecraft.massivecore.util.SmokeUtil; | ||||
| import com.massivecraft.massivecore.xlib.gson.JsonElement; | ||||
| 
 | ||||
| public class MassiveCoreEngineMain extends EngineAbstract | ||||
| { | ||||
| @ -320,35 +325,114 @@ public class MassiveCoreEngineMain extends EngineAbstract | ||||
| 		}); | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// MASSIVE STORE: LOGIN SYNC | ||||
| 	// -------------------------------------------- // | ||||
| 	// This section handles the automatic sync of a players corresponding massive store entries on login. | ||||
| 	// If possible the database IO is made during the AsyncPlayerPreLoginEvent to offloat the main server thread. | ||||
| 	 | ||||
| 	protected Map<String, Map<SenderColl<?>, Entry<JsonElement, Long>>> idToRemoteEntries = new ConcurrentHashMap<String, Map<SenderColl<?>, Entry<JsonElement, Long>>>(); | ||||
| 	 | ||||
| 	// Intended to be ran asynchronously. | ||||
| 	public void storeRemoteEntries(String playerId) | ||||
| 	{ | ||||
| 		// Create remote entries ... | ||||
| 		Map<SenderColl<?>, Entry<JsonElement, Long>> remoteEntries = createRemoteEntries(playerId); | ||||
| 		 | ||||
| 		// ... and store them. | ||||
| 		this.idToRemoteEntries.put(playerId, remoteEntries); | ||||
| 	} | ||||
| 	 | ||||
| 	// Intended to be ran synchronously. | ||||
| 	// It will use remoteEntries from AsyncPlayerPreLoginEvent if possible. | ||||
| 	// If no such remoteEntries are available it will create them and thus lock the main server thread a bit. | ||||
| 	public Map<SenderColl<?>, Entry<JsonElement, Long>> getRemoteEntries(String playerId) | ||||
| 	{ | ||||
| 		// If there are stored remote entries we used those ... | ||||
| 		Map<SenderColl<?>, Entry<JsonElement, Long>> ret = idToRemoteEntries.remove(playerId);	 | ||||
| 		if (ret != null) return ret; | ||||
| 		 | ||||
| 		// ... otherwise we create brand new ones. | ||||
| 		return createRemoteEntries(playerId); | ||||
| 	} | ||||
| 	 | ||||
| 	// Used by the two methods above. | ||||
| 	public Map<SenderColl<?>, Entry<JsonElement, Long>> createRemoteEntries(String playerId) | ||||
| 	{ | ||||
| 		// Create Ret | ||||
| 		Map<SenderColl<?>, Entry<JsonElement, Long>> ret = new HashMap<SenderColl<?>, Entry<JsonElement, Long>>(); | ||||
| 		 | ||||
| 		// Fill Ret | ||||
| 		for (final SenderColl<?> coll : Coll.getSenderInstances()) | ||||
| 		{ | ||||
| 			Entry<JsonElement, Long> remoteEntry = coll.getDb().load(coll, playerId); | ||||
| 			ret.put(coll, remoteEntry); | ||||
| 		} | ||||
| 		 | ||||
| 		// Return Ret | ||||
| 		return ret; | ||||
| 	} | ||||
| 	 | ||||
| 	@EventHandler(priority = EventPriority.MONITOR) | ||||
| 	public void massiveStoreLoginSync(AsyncPlayerPreLoginEvent event) | ||||
| 	{ | ||||
| 		// DEBUG | ||||
| 		// long before = System.nanoTime(); | ||||
| 		 | ||||
| 		// If the login was allowed ... | ||||
| 		if (event.getLoginResult() != Result.ALLOWED) return; | ||||
| 		 | ||||
| 		// ... get player id ... | ||||
| 		final String playerId = event.getUniqueId().toString(); | ||||
| 		 | ||||
| 		// ... and store the remote entries. | ||||
| 		this.storeRemoteEntries(playerId); | ||||
| 		 | ||||
| 		// DEBUG | ||||
| 		// long after = System.nanoTime(); | ||||
| 		// long duration = after - before; | ||||
| 		// double ms = (double)duration / 1000000D; | ||||
| 		// String message = Txt.parse("<i>AsyncPlayerPreLoginEvent for %s <i>took <h>%.2f <i>ms.", event.getName(), ms); | ||||
| 		// MassiveCore.get().log(message); | ||||
| 		// NOTE: I get values between 5 and 20 ms. | ||||
| 	} | ||||
| 	 | ||||
| 	// Can not be cancelled. | ||||
| 	@EventHandler(priority = EventPriority.LOWEST) | ||||
| 	public void massiveStoreLoginSync(PlayerLoginEvent event) | ||||
| 	{ | ||||
| 		// Get player id ... | ||||
| 		final String playerId = event.getPlayer().getUniqueId().toString(); | ||||
| 		 | ||||
| 		// ... get remote entries ... | ||||
| 		Map<SenderColl<?>, Entry<JsonElement, Long>> remoteEntries = getRemoteEntries(playerId); | ||||
| 		 | ||||
| 		// ... and sync each of them. | ||||
| 		for (Entry<SenderColl<?>, Entry<JsonElement, Long>> entry : remoteEntries.entrySet()) | ||||
| 		{ | ||||
| 			SenderColl<?> coll = entry.getKey(); | ||||
| 			Entry<JsonElement, Long> remoteEntry = entry.getValue(); | ||||
| 			coll.syncId(playerId, null, remoteEntry); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// SYNC PLAYER ON LOGON AND LEAVE | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) | ||||
| 	public void syncOnPlayerLogin(PlayerLoginEvent event) | ||||
| 	{ | ||||
| 		//MassiveCore.get().log("LOWEST syncOnPlayerLogin", event.getPlayer().getName()); | ||||
| 		this.syncAllForPlayer(event.getPlayer()); | ||||
| 	} | ||||
| 	 | ||||
| 	@EventHandler(priority = EventPriority.MONITOR) | ||||
| 	public void syncOnPlayerLeave(EventMassiveCorePlayerLeave event) | ||||
| 	{ | ||||
| 		//MassiveCore.get().log("MONITOR syncOnPlayerLeave", event.getPlayer().getName()); | ||||
| 		// TODO: This is going to take quite a bit of power :( | ||||
| 		this.syncAllForPlayer(event.getPlayer()); | ||||
| 	} | ||||
| 	 | ||||
| 	public void syncAllForPlayer(Player player) | ||||
| 	{ | ||||
| 		// TODO: For now we sync them both! | ||||
| 		String playerName = player.getName(); | ||||
| 		String playerId = player.getUniqueId().toString(); | ||||
| 		for (Coll<?> coll : Coll.getInstances()) | ||||
| 		for (SenderColl<?> coll : Coll.getSenderInstances()) | ||||
| 		{ | ||||
| 			if (!(coll instanceof SenderColl)) continue; | ||||
| 			SenderColl<?> pcoll = (SenderColl<?>)coll; | ||||
| 			pcoll.syncId(playerName); | ||||
| 			pcoll.syncId(playerId); | ||||
| 			coll.syncId(playerId); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| @ -10,7 +10,6 @@ import com.massivecraft.massivecore.cmd.MassiveCommand; | ||||
| import com.massivecraft.massivecore.cmd.req.ReqHasPerm; | ||||
| import com.massivecraft.massivecore.store.Coll; | ||||
| import com.massivecraft.massivecore.store.Db; | ||||
| import com.massivecraft.massivecore.store.Driver; | ||||
| import com.massivecraft.massivecore.store.MStore; | ||||
| import com.massivecraft.massivecore.xlib.gson.JsonElement; | ||||
| 
 | ||||
| @ -58,9 +57,6 @@ public class CmdMassiveCoreStoreCopydb extends MassiveCommand | ||||
| 		} | ||||
| 		 | ||||
| 		// Prepare | ||||
| 		final Driver fromDriver = fromDb.getDriver(); | ||||
| 		final Driver toDriver = toDb.getDriver(); | ||||
| 		 | ||||
| 		Set<String> collnames = fromDb.getCollnames(); | ||||
| 		 | ||||
| 		// Statistics | ||||
| @ -76,11 +72,11 @@ public class CmdMassiveCoreStoreCopydb extends MassiveCommand | ||||
| 			final Coll<?> fromColl = new Coll<Object>(collname, Object.class, fromDb, MassiveCore.get()); | ||||
| 			final Coll<?> toColl = new Coll<Object>(collname, Object.class, toDb, MassiveCore.get()); | ||||
| 			 | ||||
| 			Collection<String> ids = fromDriver.getIds(fromColl); | ||||
| 			Collection<String> ids = fromDb.getIds(fromColl); | ||||
| 			msg("<i>Now copying collection <h>%d/%d %s <i>with <h>%d <i>documents.", countCollCurrent, countCollTotal, collname, ids.size()); | ||||
| 			 | ||||
| 			// Do a load check to verify we have access to this folder. | ||||
| 			if (ids.size() > 0 && fromDriver.load(fromColl, ids.iterator().next()) == null) | ||||
| 			if (ids.size() > 0 && fromDb.load(fromColl, ids.iterator().next()) == null) | ||||
| 			{ | ||||
| 				msg("<b>Skipping <h>%s <b>since could not load data.", collname); | ||||
| 				continue; | ||||
| @ -88,8 +84,8 @@ public class CmdMassiveCoreStoreCopydb extends MassiveCommand | ||||
| 			 | ||||
| 			for (String id : ids) | ||||
| 			{ | ||||
| 				Entry<JsonElement, Long> data = fromDriver.load(fromColl, id); | ||||
| 				toDriver.save(toColl, id, data.getKey()); | ||||
| 				Entry<JsonElement, Long> data = fromDb.load(fromColl, id); | ||||
| 				toDb.save(toColl, id, data.getKey()); | ||||
| 			} | ||||
| 		} | ||||
| 		long after = System.currentTimeMillis(); | ||||
|  | ||||
| @ -54,7 +54,7 @@ public class CmdMassiveCoreStoreListcolls extends MassiveCommand | ||||
| 		collnames.addAll(db.getCollnames()); | ||||
| 		 | ||||
| 		// Do it! | ||||
| 		msg(Txt.titleize("Collections in "+db.getName())); | ||||
| 		msg(Txt.titleize("Collections in "+db.getDbName())); | ||||
| 		for (String collname : collnames) | ||||
| 		{ | ||||
| 			String message = Txt.parse("<h>") + collname; | ||||
| @ -63,7 +63,7 @@ public class CmdMassiveCoreStoreListcolls extends MassiveCommand | ||||
| 			 | ||||
| 			for (Coll<?> collCandidate : Coll.getInstances()) | ||||
| 			{ | ||||
| 				if (!collCandidate.getName().equals(collname)) continue; | ||||
| 				if ( ! collCandidate.getName().equals(collname)) continue; | ||||
| 				if (collCandidate.getDb() != db) continue; | ||||
| 				coll = collCandidate; | ||||
| 				break; | ||||
|  | ||||
| @ -72,8 +72,8 @@ public class CmdMassiveCoreStoreStats extends MassiveCommand | ||||
| 		msg("<k>Entity Count: <v>%d", coll.getIds().size()); | ||||
| 		msg("<k>Entity Class: <v>%s", coll.getEntityClass().getName()); | ||||
| 		msg("<k>Plugin: <v>%s", coll.getPlugin().getDescription().getFullName()); | ||||
| 		msg("<k>Database: <v>%s", coll.getDb().getName()); | ||||
| 		msg("<k>Driver: <v>%s", coll.getDriver().getName()); | ||||
| 		msg("<k>Database: <v>%s", coll.getDb().getDbName()); | ||||
| 		msg("<k>Driver: <v>%s", coll.getDb().getDriverName()); | ||||
| 		 | ||||
| 		int limit; | ||||
| 		 | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package com.massivecraft.massivecore.store; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.Comparator; | ||||
| @ -44,6 +45,17 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	public static Map<String, Coll<?>> getMap() { return umap; } | ||||
| 	public static Set<String> getNames() { return unames; } | ||||
| 	public static Collection<Coll<?>> getInstances() { return uinstances; } | ||||
| 	public static Collection<SenderColl<?>> getSenderInstances() | ||||
| 	{ | ||||
| 		List<SenderColl<?>> ret = new ArrayList<SenderColl<?>>(); | ||||
| 		for (Coll<?> coll : getInstances()) | ||||
| 		{ | ||||
| 			if ( ! (coll instanceof SenderColl)) continue; | ||||
| 			SenderColl<?> senderColl = (SenderColl<?>)coll; | ||||
| 			ret.add(senderColl); | ||||
| 		} | ||||
| 		return ret; | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// WHAT DO WE HANDLE? | ||||
| @ -81,7 +93,6 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	 | ||||
| 	protected Db db; | ||||
| 	@Override public Db getDb() { return this.db; } | ||||
| 	@Override public Driver getDriver() { return this.db.getDriver(); } | ||||
| 	 | ||||
| 	protected Object collDriverObject; | ||||
| 	@Override public Object getCollDriverObject() { return this.collDriverObject; } | ||||
| @ -94,8 +105,6 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	protected Map<String, E> id2entity; | ||||
| 	protected Map<E, String> entity2id; | ||||
| 	 | ||||
| 	@Override public Collection<String> getIds() { return Collections.unmodifiableCollection(this.id2entity.keySet()); } | ||||
| 	 | ||||
| 	@Override public Map<String, E> getId2entity() { return Collections.unmodifiableMap(this.id2entity); }  | ||||
| 	@Override  | ||||
| 	public E get(Object oid)  | ||||
| @ -107,18 +116,18 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	{ | ||||
| 		return this.get(oid, creative, true); | ||||
| 	} | ||||
| 	protected E get(Object oid, boolean creative, boolean noteChange) | ||||
| 	protected E get(Object oid, boolean creative, boolean noteModification) | ||||
| 	{ | ||||
| 		String id = this.fixId(oid); | ||||
| 		if (id == null) return null; | ||||
| 		E ret = this.id2entity.get(id); | ||||
| 		if (ret != null) return ret; | ||||
| 		if ( ! creative) return null; | ||||
| 		return this.create(id, noteChange); | ||||
| 		return this.create(id, noteModification); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override public Collection<String> getIdsLoaded() { return Collections.unmodifiableCollection(this.id2entity.keySet()); } | ||||
| 	@Override public Collection<String> getIdsRemote() { return this.getDb().getDriver().getIds(this); } | ||||
| 	@Override public Collection<String> getIds() { return Collections.unmodifiableCollection(this.id2entity.keySet()); } | ||||
| 	@Override public Collection<String> getIdsRemote() { return this.getDb().getIds(this); } | ||||
| 	@Override | ||||
| 	public boolean containsId(Object oid) | ||||
| 	{ | ||||
| @ -270,10 +279,10 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		return this.create(oid, true); | ||||
| 	} | ||||
| 	 | ||||
| 	public synchronized E create(Object oid, boolean noteChange) | ||||
| 	public synchronized E create(Object oid, boolean noteModification) | ||||
| 	{ | ||||
| 		E entity = this.createNewInstance(); | ||||
| 		if (this.attach(entity, oid, noteChange) == null) return null; | ||||
| 		if (this.attach(entity, oid, noteModification) == null) return null; | ||||
| 		return entity; | ||||
| 	} | ||||
| 	 | ||||
| @ -294,7 +303,7 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	} | ||||
| 
 | ||||
| 	@SuppressWarnings({ "unchecked", "rawtypes" }) | ||||
| 	protected synchronized String attach(E entity, Object oid, boolean noteChange) | ||||
| 	protected synchronized String attach(E entity, Object oid, boolean noteModification) | ||||
| 	{ | ||||
| 		// Check entity | ||||
| 		if (entity == null) return null; | ||||
| @ -327,11 +336,10 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		this.id2entity.put(id, entity); | ||||
| 		this.entity2id.put(entity, id); | ||||
| 		 | ||||
| 		// Make note of the change | ||||
| 		if (noteChange) | ||||
| 		// Identify Modification | ||||
| 		if (noteModification) | ||||
| 		{ | ||||
| 			this.localAttachIds.add(id); | ||||
| 			this.changedIds.add(id); | ||||
| 			this.identifiedModifications.put(id, Modification.LOCAL_ATTACH); | ||||
| 		} | ||||
| 		 | ||||
| 		// POST | ||||
| @ -383,9 +391,8 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		// Remove @ local | ||||
| 		this.removeAtLocal(id); | ||||
| 		 | ||||
| 		// Identify the change | ||||
| 		this.localDetachIds.add(id); | ||||
| 		this.changedIds.add(id); | ||||
| 		// Identify Modification | ||||
| 		this.identifiedModifications.put(id, Modification.LOCAL_DETACH); | ||||
| 		 | ||||
| 		// POST | ||||
| 		this.postDetach(entity, id); | ||||
| @ -428,21 +435,16 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// IDENTIFIED CHANGES | ||||
| 	// IDENTIFIED MODIFICATIONS | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	protected Set<String> localAttachIds; | ||||
| 	protected Set<String> localDetachIds; | ||||
| 	protected Set<String> changedIds; | ||||
| 	protected Map<String, Modification> identifiedModifications; | ||||
| 	 | ||||
| 	protected synchronized void clearIdentifiedChanges(Object oid) | ||||
| 	protected void removeIdentifiedModification(Object oid) | ||||
| 	{ | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		 | ||||
| 		String id = this.fixId(oid); | ||||
| 		this.localAttachIds.remove(id); | ||||
| 		this.localDetachIds.remove(id); | ||||
| 		this.changedIds.remove(id); | ||||
| 		this.identifiedModifications.remove(id); | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| @ -463,7 +465,7 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		this.lastDefault.remove(id); | ||||
| 	} | ||||
| 	 | ||||
| 	// Log database syncronization for display in the "/massivecore mstore stats" command. | ||||
| 	// Log database synchronization for display in the "/massivecore mstore stats" command. | ||||
| 	private Map<String, Long> id2out = new TreeMap<String, Long>(); | ||||
| 	private Map<String, Long> id2in = new TreeMap<String, Long>(); | ||||
| 	 | ||||
| @ -494,11 +496,11 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	@Override | ||||
| 	public synchronized E removeAtLocal(Object oid) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		 | ||||
| 		String id = this.fixId(oid); | ||||
| 		 | ||||
| 		this.clearIdentifiedChanges(id); | ||||
| 		this.removeIdentifiedModification(id); | ||||
| 		this.clearSynclog(id); | ||||
| 		 | ||||
| 		E entity = this.id2entity.remove(id); | ||||
| @ -519,24 +521,24 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	@Override | ||||
| 	public synchronized void removeAtRemote(Object oid) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		 | ||||
| 		String id = this.fixId(oid); | ||||
| 		 | ||||
| 		this.clearIdentifiedChanges(id); | ||||
| 		this.removeIdentifiedModification(id); | ||||
| 		this.clearSynclog(id); | ||||
| 		 | ||||
| 		this.getDb().getDriver().delete(this, id); | ||||
| 		this.getDb().delete(this, id); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public synchronized void saveToRemote(Object oid) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		 | ||||
| 		String id = this.fixId(oid); | ||||
| 		 | ||||
| 		this.clearIdentifiedChanges(id); | ||||
| 		this.removeIdentifiedModification(id); | ||||
| 		this.clearSynclog(id); | ||||
| 		 | ||||
| 		E entity = this.id2entity.get(id); | ||||
| @ -547,31 +549,32 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		 | ||||
| 		if (this.isDefault(entity) && isCustomDataDefault(entity)) | ||||
| 		{ | ||||
| 			this.db.getDriver().delete(this, id); | ||||
| 			this.getDb().delete(this, id); | ||||
| 			this.lastDefault.add(id); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			Long mtime = this.db.getDriver().save(this, id, raw); | ||||
| 			if (mtime == null) return; // This fail should not happen often. We could handle it better though. | ||||
| 			long mtime = this.getDb().save(this, id, raw); | ||||
| 			if (mtime == 0) return; // This fail should not happen often. We could handle it better though. | ||||
| 			this.lastMtime.put(id, mtime); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@SuppressWarnings("unchecked") | ||||
| 	@Override | ||||
| 	public synchronized void loadFromRemote(Object oid, Entry<JsonElement, Long> entry, boolean entrySupplied) | ||||
| 	public synchronized void loadFromRemote(Object oid, Entry<JsonElement, Long> remoteEntry) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		String id = this.fixId(oid); | ||||
| 		 | ||||
| 		this.clearIdentifiedChanges(id); | ||||
| 		this.removeIdentifiedModification(id); | ||||
| 		 | ||||
| 		if ( ! entrySupplied) | ||||
| 		if (remoteEntry == null) | ||||
| 		{ | ||||
| 			try | ||||
| 			{ | ||||
| 				entry = this.getDriver().load(this, id); | ||||
| 				remoteEntry = this.getDb().load(this, id); | ||||
| 			} | ||||
| 			catch (Exception e) | ||||
| 			{ | ||||
| @ -580,13 +583,20 @@ public class Coll<E> implements CollInterface<E> | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		if (entry == null) | ||||
| 		Long mtime = remoteEntry.getValue(); | ||||
| 		if (mtime == null) | ||||
| 		{ | ||||
| 			logLoadError(id, "MStore driver could not load data entry. The file might not be readable or simply not exist."); | ||||
| 			logLoadError(id, "Last modification time (mtime) was null. The file might not be readable or simply not exist."); | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		JsonElement raw = entry.getKey(); | ||||
| 		if (mtime == 0) | ||||
| 		{ | ||||
| 			logLoadError(id, "Last modification time (mtime) was 0. The file might not be readable or simply not exist."); | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		JsonElement raw = remoteEntry.getKey(); | ||||
| 		if (raw == null) | ||||
| 		{ | ||||
| 			logLoadError(id, "Raw data was null. Is the file completely empty?"); | ||||
| @ -598,13 +608,6 @@ public class Coll<E> implements CollInterface<E> | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		Long mtime = entry.getValue(); | ||||
| 		if (mtime == null) | ||||
| 		{ | ||||
| 			logLoadError(id, "Last modification time (mtime) was null."); | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		// Calculate temp but handle raw cases. | ||||
| 		E temp = null; | ||||
| 		if (this.getEntityClass().isAssignableFrom(JsonObject.class)) | ||||
| @ -651,61 +654,63 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	@Override | ||||
| 	public ModificationState examineId(Object oid) | ||||
| 	public Modification examineId(Object oid) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		String id = this.fixId(oid); | ||||
| 		return this.examineId(id, null, false); | ||||
| 		 | ||||
| 		return this.examineId(id, null); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public ModificationState examineId(Object oid, Long remoteMtime) | ||||
| 	{ | ||||
| 		String id = this.fixId(oid); | ||||
| 		return this.examineId(id, remoteMtime, true); | ||||
| 	} | ||||
| 	 | ||||
| 	protected ModificationState examineId(Object oid, Long remoteMtime, boolean remoteMtimeSupplied) | ||||
| 	public Modification examineId(Object oid, Long remoteMtime) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		String id = this.fixId(oid); | ||||
| 		 | ||||
| 		if (this.localDetachIds.contains(id)) return ModificationState.LOCAL_DETACH; | ||||
| 		if (this.localAttachIds.contains(id)) return ModificationState.LOCAL_ATTACH; | ||||
| 		// Local Attach and Detach has the top priority. | ||||
| 		// Otherwise newly attached entities would be removed thinking it was a remote detach. | ||||
| 		// Otherwise newly detached entities would be loaded thinking it was a remote attach. | ||||
| 		Modification ret = this.identifiedModifications.get(id); | ||||
| 		if (ret == Modification.LOCAL_ATTACH || ret == Modification.LOCAL_DETACH) return ret; | ||||
| 		 | ||||
| 		E localEntity = this.id2entity.get(id); | ||||
| 		if ( ! remoteMtimeSupplied) | ||||
| 		if (remoteMtime == null) | ||||
| 		{ | ||||
| 			remoteMtime = this.getDriver().getMtime(this, id); | ||||
| 			remoteMtime = this.getDb().getMtime(this, id); | ||||
| 		} | ||||
| 		 | ||||
| 		boolean existsLocal = (localEntity != null); | ||||
| 		boolean existsRemote = (remoteMtime != null); | ||||
| 		boolean existsRemote = (remoteMtime != 0); | ||||
| 		 | ||||
| 		if ( ! existsLocal && ! existsRemote) return ModificationState.UNKNOWN; | ||||
| 		if ( ! existsLocal && ! existsRemote) return Modification.UNKNOWN; | ||||
| 		 | ||||
| 		if (existsLocal && existsRemote) | ||||
| 		{ | ||||
| 			Long lastMtime = this.lastMtime.get(id); | ||||
| 			if (remoteMtime.equals(lastMtime) == false) return ModificationState.REMOTE_ALTER; | ||||
| 			if (remoteMtime.equals(lastMtime) == false) return Modification.REMOTE_ALTER; | ||||
| 			 | ||||
| 			if (this.examineHasLocalAlter(id, localEntity)) return ModificationState.LOCAL_ALTER; | ||||
| 			if (this.examineHasLocalAlter(id, localEntity)) return Modification.LOCAL_ALTER; | ||||
| 		} | ||||
| 		else if (existsLocal) | ||||
| 		{ | ||||
| 			if (this.lastDefault.contains(id)) | ||||
| 			{ | ||||
| 				if (this.examineHasLocalAlter(id, localEntity)) return ModificationState.LOCAL_ALTER; | ||||
| 				if (this.examineHasLocalAlter(id, localEntity)) return Modification.LOCAL_ALTER; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				return ModificationState.REMOTE_DETACH; | ||||
| 				return Modification.REMOTE_DETACH; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (existsRemote) | ||||
| 		{ | ||||
| 			return ModificationState.REMOTE_ATTACH; | ||||
| 			return Modification.REMOTE_ATTACH; | ||||
| 		} | ||||
| 		 | ||||
| 		return ModificationState.NONE; | ||||
| 		return Modification.NONE; | ||||
| 	} | ||||
| 	 | ||||
| 	protected boolean examineHasLocalAlter(String id, E entity) | ||||
| @ -730,15 +735,36 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public ModificationState syncId(Object oid) | ||||
| 	public Modification syncId(Object oid) | ||||
| 	{ | ||||
| 		return this.syncId(oid, null); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Modification syncId(Object oid, Modification modification) | ||||
| 	{ | ||||
| 		return this.syncId(oid, modification, null); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Modification syncId(Object oid, Modification modification, Entry<JsonElement, Long> remoteEntry) | ||||
| 	{ | ||||
| 		// Fix Id | ||||
| 		if (oid == null) throw new NullPointerException("oid"); | ||||
| 		String id = this.fixId(oid); | ||||
| 		 | ||||
| 		ModificationState mstate = this.examineId(id); | ||||
| 		if (modification == null || modification == Modification.UNKNOWN) | ||||
| 		{ | ||||
| 			Long remoteMtime = null; | ||||
| 			if (remoteEntry != null) remoteMtime = remoteEntry.getValue(); | ||||
| 			 | ||||
| 			modification = this.examineId(id, remoteMtime); | ||||
| 		} | ||||
| 		 | ||||
| 		//mplugin.log("syncId: It seems", id, "has state", mstate); | ||||
| 		// DEBUG | ||||
| 		// MassiveCore.get().log(Txt.parse("<k>Coll: <v>%s <k>Entity: <v>%s <k>Modification: <v>%s", this.getName(), id, modification)); | ||||
| 		 | ||||
| 		switch (mstate) | ||||
| 		switch (modification) | ||||
| 		{ | ||||
| 			case LOCAL_ALTER: | ||||
| 			case LOCAL_ATTACH: | ||||
| @ -759,7 +785,7 @@ public class Coll<E> implements CollInterface<E> | ||||
| 			break; | ||||
| 			case REMOTE_ALTER: | ||||
| 			case REMOTE_ATTACH: | ||||
| 				this.loadFromRemote(id, null, false); | ||||
| 				this.loadFromRemote(id, remoteEntry); | ||||
| 				if (this.inited()) | ||||
| 				{ | ||||
| 					this.addSyncCount(TOTAL, true); | ||||
| @ -775,47 +801,18 @@ public class Coll<E> implements CollInterface<E> | ||||
| 				} | ||||
| 			break; | ||||
| 			default: | ||||
| 				this.clearIdentifiedChanges(id); | ||||
| 				this.removeIdentifiedModification(id); | ||||
| 			break; | ||||
| 		} | ||||
| 		 | ||||
| 		return mstate; | ||||
| 		return modification; | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void syncSuspects() | ||||
| 	public void identifyModifications() | ||||
| 	{ | ||||
| 		/*if (MassiveCore.get().doderp) | ||||
| 		{ | ||||
| 			if (this.changedIds.size() > 0) | ||||
| 			{ | ||||
| 				System.out.println("Coll " + this.getName() + " had suspects " + Txt.implode(this.changedIds, " ")); | ||||
| 			} | ||||
| 		}*/ | ||||
| 		 | ||||
| 		for (String id : this.changedIds) | ||||
| 		{ | ||||
| 			this.syncId(id); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void syncAll() | ||||
| 	{ | ||||
| 		// Find all ids | ||||
| 		Set<String> allids = new HashSet<String>(this.id2entity.keySet()); | ||||
| 		allids.addAll(this.getDriver().getIds(this)); | ||||
| 		for (String id : allids) | ||||
| 		{ | ||||
| 			this.syncId(id); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void findSuspects() | ||||
| 	{ | ||||
| 		// Get remote id and mtime snapshot | ||||
| 		Map<String, Long> id2RemoteMtime = this.getDb().getDriver().getId2mtime(this); | ||||
| 		// Get remote id2mtime snapshot | ||||
| 		Map<String, Long> id2RemoteMtime = this.getDb().getId2mtime(this); | ||||
| 		 | ||||
| 		// Compile a list of all ids (both remote and local) | ||||
| 		Set<String> allids = new HashSet<String>(); | ||||
| @ -826,27 +823,45 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		for (String id : allids) | ||||
| 		{ | ||||
| 			Long remoteMtime = id2RemoteMtime.get(id); | ||||
| 			ModificationState state = this.examineId(id, remoteMtime); | ||||
| 			//mplugin.log("findSuspects: It seems", id, "has state", state); | ||||
| 			if (state.isModified()) | ||||
| 			if (remoteMtime == null) remoteMtime = 0L; | ||||
| 			 | ||||
| 			Modification modification = this.examineId(id, remoteMtime); | ||||
| 			if (modification.isModified()) | ||||
| 			{ | ||||
| 				//System.out.println("It seems "+id+" has state "+state); | ||||
| 				this.changedIds.add(id); | ||||
| 				this.identifiedModifications.put(id, modification); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void syncIdentified() | ||||
| 	{ | ||||
| 		for (Entry<String, Modification> entry : this.identifiedModifications.entrySet()) | ||||
| 		{ | ||||
| 			String id = entry.getKey(); | ||||
| 			Modification modification = entry.getValue(); | ||||
| 			this.syncId(id, modification); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void syncAll() | ||||
| 	{ | ||||
| 		this.identifyModifications(); | ||||
| 		this.syncIdentified(); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public void initLoadAllFromRemote() | ||||
| 	{ | ||||
| 		Map<String, Entry<JsonElement, Long>> idToEntryMap = this.getDb().getDriver().loadAll(this); | ||||
| 		Map<String, Entry<JsonElement, Long>> idToEntryMap = this.getDb().loadAll(this); | ||||
| 		if (idToEntryMap == null) return; | ||||
| 		 | ||||
| 		for (Entry<String, Entry<JsonElement, Long>> idToEntry : idToEntryMap.entrySet()) | ||||
| 		{ | ||||
| 			String id = idToEntry.getKey(); | ||||
| 			Entry<JsonElement, Long> entry = idToEntry.getValue(); | ||||
| 			loadFromRemote(id, entry, true); | ||||
| 			Entry<JsonElement, Long> remoteEntry = idToEntry.getValue(); | ||||
| 			loadFromRemote(id, remoteEntry); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| @ -859,7 +874,7 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	@Override | ||||
| 	public void onTick() | ||||
| 	{ | ||||
| 		this.syncSuspects(); | ||||
| 		this.syncIdentified(); | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| @ -889,7 +904,7 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		// SUPPORTING SYSTEM | ||||
| 		this.plugin = plugin; | ||||
| 		this.db = db; | ||||
| 		this.collDriverObject = db.getCollDriverObject(this); | ||||
| 		this.collDriverObject = db.createCollDriverObject(this); | ||||
| 		 | ||||
| 		// STORAGE | ||||
| 		if (entityComparator == null && !Comparable.class.isAssignableFrom(entityClass)) | ||||
| @ -900,10 +915,8 @@ public class Coll<E> implements CollInterface<E> | ||||
| 		this.id2entity = new ConcurrentSkipListMap<String, E>(idComparator); | ||||
| 		this.entity2id = new ConcurrentSkipListMap<E, String>(entityComparator); | ||||
| 		 | ||||
| 		// IDENTIFIED CHANGES | ||||
| 		this.localAttachIds = new ConcurrentSkipListSet<String>(idComparator); | ||||
| 		this.localDetachIds = new ConcurrentSkipListSet<String>(idComparator); | ||||
| 		this.changedIds = new ConcurrentSkipListSet<String>(idComparator); | ||||
| 		// IDENTIFIED MODIFICATIONS | ||||
| 		this.identifiedModifications = new ConcurrentSkipListMap<String, Modification>(idComparator); | ||||
| 		 | ||||
| 		// SYNCLOG | ||||
| 		this.lastMtime = new ConcurrentSkipListMap<String, Long>(idComparator); | ||||
| @ -941,7 +954,7 @@ public class Coll<E> implements CollInterface<E> | ||||
| 	@Override | ||||
| 	public void deinit() | ||||
| 	{ | ||||
| 		if (!this.inited()) return; | ||||
| 		if ( ! this.inited()) return; | ||||
| 		 | ||||
| 		// TODO: Save outwards only? We may want to avoid loads at this stage... | ||||
| 		this.syncAll(); | ||||
|  | ||||
| @ -27,7 +27,6 @@ public interface CollInterface<E> | ||||
| 	public Plugin getPlugin(); | ||||
| 	 | ||||
| 	public Db getDb(); | ||||
| 	public Driver getDriver(); | ||||
| 	public Object getCollDriverObject(); | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| @ -36,9 +35,8 @@ public interface CollInterface<E> | ||||
| 	public Map<String, E> getId2entity(); | ||||
| 	public E get(Object oid); | ||||
| 	public E get(Object oid, boolean creative); | ||||
| 	public Collection<String> getIds(); // All ideas we know of whether they are loaded or not | ||||
| 	public Collection<String> getIdsRemote(); // All remote ids loaded sync via driver | ||||
| 	public Collection<String> getIdsLoaded(); // All locally loaded ids | ||||
| 	public Collection<String> getIds(); | ||||
| 	public Collection<String> getIdsRemote(); | ||||
| 	public boolean containsId(Object oid); | ||||
| 	 | ||||
| 	public Map<E, String> getEntity2id(); | ||||
| @ -130,19 +128,21 @@ public interface CollInterface<E> | ||||
| 	public E removeAtLocal(Object oid); | ||||
| 	public void removeAtRemote(Object oid); | ||||
| 	public void saveToRemote(Object oid); | ||||
| 	public void loadFromRemote(Object oid, Entry<JsonElement, Long> entry, boolean entrySupplied); | ||||
| 	public void loadFromRemote(Object oid, Entry<JsonElement, Long> remoteEntry); | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// SYNC EXAMINE AND DO | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	public ModificationState examineId(Object oid); | ||||
| 	public ModificationState examineId(Object oid, Long remoteMtime); | ||||
| 	public Modification examineId(Object oid); | ||||
| 	public Modification examineId(Object oid, Long remoteMtime); | ||||
| 	 | ||||
| 	public ModificationState syncId(Object oid); | ||||
| 	public void syncSuspects(); | ||||
| 	public Modification syncId(Object oid); | ||||
| 	public Modification syncId(Object oid, Modification modificationState); | ||||
| 	public Modification syncId(Object oid, Modification modificationState, Entry<JsonElement, Long> remoteEntry); | ||||
| 	public void syncIdentified(); | ||||
| 	public void syncAll(); | ||||
| 	public void findSuspects(); | ||||
| 	public void identifyModifications(); | ||||
| 	public void initLoadAllFromRemote(); | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
|  | ||||
| @ -1,16 +1,44 @@ | ||||
| package com.massivecraft.massivecore.store; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.Map.Entry; | ||||
| 
 | ||||
| import com.massivecraft.massivecore.xlib.gson.JsonElement; | ||||
| 
 | ||||
| public interface Db | ||||
| {	 | ||||
| 	public String getName(); | ||||
| { | ||||
| 	// -------------------------------------------- // | ||||
| 	// FIELDS | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	public boolean drop(); | ||||
| 	 | ||||
| 	public Set<String> getCollnames(); | ||||
| 	// Returns the name of the database. | ||||
| 	public String getDbName(); | ||||
| 	 | ||||
| 	// Returns the driver running this database. | ||||
| 	public Driver getDriver(); | ||||
| 	 | ||||
| 	public Object getCollDriverObject(Coll<?> coll); | ||||
| 	// Creates a new collection driver object. | ||||
| 	// This object will be stored inside the Coll. | ||||
| 	public Object createCollDriverObject(Coll<?> coll); | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// DRIVER | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	public String getDriverName(); | ||||
| 	public Db getDb(String uri); | ||||
| 	public boolean dropDb(); | ||||
| 	public Set<String> getCollnames(); | ||||
| 	public boolean renameColl(String from, String to); | ||||
| 	public boolean containsId(Coll<?> coll, String id); | ||||
| 	public long getMtime(Coll<?> coll, String id); | ||||
| 	public Collection<String> getIds(Coll<?> coll); | ||||
| 	public Map<String, Long> getId2mtime(Coll<?> coll); | ||||
| 	public Entry<JsonElement, Long> load(Coll<?> coll, String id); | ||||
| 	public Map<String, Entry<JsonElement, Long>> loadAll(Coll<?> coll); | ||||
| 	public long save(Coll<?> coll, String id, JsonElement data); | ||||
| 	public void delete(Coll<?> coll, String id); | ||||
| 	 | ||||
| } | ||||
| @ -1,12 +1,80 @@ | ||||
| package com.massivecraft.massivecore.store; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.Map.Entry; | ||||
| 
 | ||||
| import com.massivecraft.massivecore.xlib.gson.JsonElement; | ||||
| 
 | ||||
| public abstract class DbAbstract implements Db | ||||
| {	 | ||||
| 	@Override | ||||
| 	// -------------------------------------------- // | ||||
| 	// DRIVER | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	public String getDriverName() | ||||
| 	{ | ||||
| 		return this.getDriver().getDriverName(); | ||||
| 	} | ||||
| 	 | ||||
| 	public Db getDb(String uri) | ||||
| 	{ | ||||
| 		return this.getDriver().getDb(uri); | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean dropDb() | ||||
| 	{ | ||||
| 		return this.getDriver().dropDb(this); | ||||
| 	} | ||||
| 	 | ||||
| 	public Set<String> getCollnames() | ||||
| 	{ | ||||
| 		return this.getDriver().getCollnames(this); | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean renameColl(String from, String to) | ||||
| 	{ | ||||
| 		return this.getDriver().renameColl(this, from, to); | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean containsId(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		return this.getDriver().containsId(coll, id); | ||||
| 	} | ||||
| 	 | ||||
| 	public long getMtime(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		return this.getDriver().getMtime(coll, id); | ||||
| 	} | ||||
| 	 | ||||
| 	public Collection<String> getIds(Coll<?> coll) | ||||
| 	{ | ||||
| 		return this.getDriver().getIds(coll); | ||||
| 	} | ||||
| 	 | ||||
| 	public Map<String, Long> getId2mtime(Coll<?> coll) | ||||
| 	{ | ||||
| 		return this.getDriver().getId2mtime(coll); | ||||
| 	} | ||||
| 	 | ||||
| 	public Entry<JsonElement, Long> load(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		return this.getDriver().load(coll, id); | ||||
| 	} | ||||
| 	 | ||||
| 	public Map<String, Entry<JsonElement, Long>> loadAll(Coll<?> coll) | ||||
| 	{ | ||||
| 		return this.getDriver().loadAll(coll); | ||||
| 	} | ||||
| 	 | ||||
| 	public long save(Coll<?> coll, String id, JsonElement data) | ||||
| 	{ | ||||
| 		return this.getDriver().save(coll, id, data); | ||||
| 	} | ||||
| 	 | ||||
| 	public void delete(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		this.getDriver().delete(coll, id); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -2,56 +2,41 @@ package com.massivecraft.massivecore.store; | ||||
| 
 | ||||
| import java.io.File; | ||||
| 
 | ||||
| import com.massivecraft.massivecore.util.DiscUtil; | ||||
| 
 | ||||
| public class DbFlatfile extends DbAbstract | ||||
| { | ||||
| 	// -------------------------------------------- // | ||||
| 	// FIELDS | ||||
| 	// -------------------------------------------- // | ||||
| 
 | ||||
| 	public File dir; | ||||
| 	public File directory; | ||||
| 	 | ||||
| 	protected DriverFlatfile driver; | ||||
| 	@Override public DriverFlatfile getDriver() { return driver; } | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// CONSTRUCTORS | ||||
| 	// CONSTRUCT | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	public DbFlatfile(DriverFlatfile driver, File folder) | ||||
| 	public DbFlatfile(DriverFlatfile driver, File directory) | ||||
| 	{ | ||||
| 		this.driver = driver; | ||||
| 		this.dir = folder; | ||||
| 		this.directory = directory; | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// IMPLEMENTATION | ||||
| 	// OVERRIDE | ||||
| 	// -------------------------------------------- // | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getName() | ||||
| 	public String getDbName() | ||||
| 	{ | ||||
| 		return dir.getAbsolutePath(); | ||||
| 		return directory.getAbsolutePath(); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public boolean drop() | ||||
| 	public Object createCollDriverObject(Coll<?> coll) | ||||
| 	{ | ||||
| 		try | ||||
| 		{ | ||||
| 			return DiscUtil.deleteRecursive(this.dir); | ||||
| 		} | ||||
| 		catch (Exception e) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Object getCollDriverObject(Coll<?> coll) | ||||
| 	{ | ||||
| 		return new File(dir, coll.getName()); | ||||
| 		return new File(directory, coll.getName()); | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
|  | ||||
| @ -24,32 +24,19 @@ public class DbMongo extends DbAbstract | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// IMPLEMENTATION | ||||
| 	// OVERRIDE | ||||
| 	// -------------------------------------------- // | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getName() | ||||
| 	public String getDbName() | ||||
| 	{ | ||||
| 		return db.getName(); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public boolean drop() | ||||
| 	{ | ||||
| 		try | ||||
| 		{ | ||||
| 			this.db.dropDatabase(); | ||||
| 			return true; | ||||
| 		} | ||||
| 		catch (Exception e) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Object getCollDriverObject(Coll<?> coll) | ||||
| 	public Object createCollDriverObject(Coll<?> coll) | ||||
| 	{ | ||||
| 		return db.getCollection(coll.getName()); | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
|  | ||||
| @ -10,12 +10,15 @@ import com.massivecraft.massivecore.xlib.gson.JsonElement; | ||||
| public interface Driver | ||||
| { | ||||
| 	// Returns the name of the driver. | ||||
| 	public String getName(); | ||||
| 	public String getDriverName(); | ||||
| 	 | ||||
| 	// Get a database instance from the driver | ||||
| 	public Db getDb(String uri); | ||||
| 	 | ||||
| 	// What collections are in the database?		 | ||||
| 	// This will delete the whole database and all collections therein. | ||||
| 	public boolean dropDb(Db db); | ||||
| 	 | ||||
| 	// What collections are in the database? | ||||
| 	public Set<String> getCollnames(Db db); | ||||
| 	 | ||||
| 	// Rename a collection | ||||
| @ -25,7 +28,10 @@ public interface Driver | ||||
| 	public boolean containsId(Coll<?> coll, String id); | ||||
| 	 | ||||
| 	// When was X last altered? | ||||
| 	public Long getMtime(Coll<?> coll, String id); | ||||
| 	// return == null will never happen. | ||||
| 	// return != 0 means X exists and return is when it last was altered. | ||||
| 	// return == 0 means X does not exist in the database. | ||||
| 	public long getMtime(Coll<?> coll, String id); | ||||
| 		 | ||||
| 	// What ids are in the collection? | ||||
| 	public Collection<String> getIds(Coll<?> coll); | ||||
| @ -34,15 +40,20 @@ public interface Driver | ||||
| 	public Map<String, Long> getId2mtime(Coll<?> coll); | ||||
| 	 | ||||
| 	// Load the raw data for X. The second part of the entry is the remote mtime at the load. | ||||
| 	// return == null will never happen. | ||||
| 	// return.getKey() == null || return.getValue() == 0 means something failed. | ||||
| 	public Entry<JsonElement, Long> load(Coll<?> coll, String id); | ||||
| 	 | ||||
| 	// Load all database content at once | ||||
| 	// NOTE: This method is assumed to be based on the one above. | ||||
| 	// NOTE: Values where JsonElement == null and Long == 0 may occur. | ||||
| 	public Map<String, Entry<JsonElement, Long>> loadAll(Coll<?> coll); | ||||
| 	 | ||||
| 	// Save raw data as X | ||||
| 	// Return value is the new mtime (we caused the change). | ||||
| 	// If the mtime is null something failed. | ||||
| 	public Long save(Coll<?> coll, String id, JsonElement data); | ||||
| 	// return == null will never happen. | ||||
| 	// return == 0 means something failed. Usually failures are not catched, though. System.currentTimeMillis() is returned most of the time. | ||||
| 	public long save(Coll<?> coll, String id, JsonElement data); | ||||
| 	 | ||||
| 	// Delete X | ||||
| 	public void delete(Coll<?> coll, String id); | ||||
|  | ||||
| @ -7,7 +7,7 @@ public abstract class DriverAbstract implements Driver | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	private final String name; | ||||
| 	@Override public String getName() { return this.name; } | ||||
| 	@Override public String getDriverName() { return this.name; } | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
| 	// CONSTRUCT | ||||
|  | ||||
| @ -41,9 +41,25 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	public Db getDb(String uri) | ||||
| 	{ | ||||
| 		// "flatfile://" is 8+3=11 chars | ||||
| 		File folder = new File(uri.substring(NAME.length() + 3)); | ||||
| 		folder.mkdirs(); | ||||
| 		return new DbFlatfile(this, folder); | ||||
| 		File directory = new File(uri.substring(NAME.length() + 3)); | ||||
| 		directory.mkdirs(); | ||||
| 		return new DbFlatfile(this, directory); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public boolean dropDb(Db db) | ||||
| 	{ | ||||
| 		if ( ! (db instanceof DbFlatfile)) throw new IllegalArgumentException("db"); | ||||
| 		DbFlatfile dbFlatfile = (DbFlatfile)db; | ||||
| 		 | ||||
| 		try | ||||
| 		{ | ||||
| 			return DiscUtil.deleteRecursive(dbFlatfile.directory); | ||||
| 		} | ||||
| 		catch (Exception e) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| @ -51,10 +67,10 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	{ | ||||
| 		Set<String> ret = new LinkedHashSet<String>(); | ||||
| 		 | ||||
| 		for (File f : ((DbFlatfile)db).dir.listFiles()) | ||||
| 		for (File file : ((DbFlatfile)db).directory.listFiles()) | ||||
| 		{ | ||||
| 			if ( ! f.isDirectory()) continue; | ||||
| 			ret.add(f.getName()); | ||||
| 			if ( ! file.isDirectory()) continue; | ||||
| 			ret.add(file.getName()); | ||||
| 		} | ||||
| 	 | ||||
| 		return ret; | ||||
| @ -63,7 +79,7 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	@Override | ||||
| 	public boolean renameColl(Db db, String from, String to) | ||||
| 	{ | ||||
| 		File dir = ((DbFlatfile)db).dir; | ||||
| 		File dir = ((DbFlatfile)db).directory; | ||||
| 		File fileFrom = new File(dir, from); | ||||
| 		File fileTo = new File(dir, to); | ||||
| 		return fileFrom.renameTo(fileTo); | ||||
| @ -76,10 +92,10 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Long getMtime(Coll<?> coll, String id) | ||||
| 	public long getMtime(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		File file = fileFromId(coll, id); | ||||
| 		if ( ! file.isFile()) return null; | ||||
| 		if ( ! file.isFile()) return 0; | ||||
| 		return file.lastModified(); | ||||
| 	} | ||||
| 	 | ||||
| @ -89,7 +105,7 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 		List<String> ret = new ArrayList<String>(); | ||||
| 		 | ||||
| 		// Scan the collection folder for .json files | ||||
| 		File collDir = getCollDir(coll); | ||||
| 		File collDir = getDirectory(coll); | ||||
| 		if ( ! collDir.isDirectory()) return ret; | ||||
| 		for (File file : collDir.listFiles(JsonFileFilter.get())) | ||||
| 		{ | ||||
| @ -105,15 +121,16 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 		// Create Ret | ||||
| 		Map<String, Long> ret = new HashMap<String, Long>(); | ||||
| 		 | ||||
| 		// Get collection directory | ||||
| 		File collDir = getCollDir(coll); | ||||
| 		if (!collDir.isDirectory()) return ret; | ||||
| 		// Get Directory | ||||
| 		File directory = getDirectory(coll); | ||||
| 		if ( ! directory.isDirectory()) return ret; | ||||
| 		 | ||||
| 		// For each .json file | ||||
| 		for (File file : collDir.listFiles(JsonFileFilter.get())) | ||||
| 		for (File file : directory.listFiles(JsonFileFilter.get())) | ||||
| 		{ | ||||
| 			String id = idFromFile(file); | ||||
| 			long mtime = file.lastModified(); | ||||
| 			// TODO: Check is 0 here? | ||||
| 			ret.put(id, mtime); | ||||
| 		} | ||||
| 		 | ||||
| @ -130,11 +147,8 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	 | ||||
| 	public Entry<JsonElement, Long> loadFile(File file) | ||||
| 	{ | ||||
| 		Long mtime = file.lastModified(); | ||||
| 		if (mtime == 0) return null; | ||||
| 		 | ||||
| 		long mtime = file.lastModified(); | ||||
| 		JsonElement raw = loadFileJson(file); | ||||
| 		if (raw == null) return null; | ||||
| 		 | ||||
| 		return new SimpleEntry<JsonElement, Long>(raw, mtime); | ||||
| 	} | ||||
| @ -153,15 +167,15 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	@Override | ||||
| 	public Map<String, Entry<JsonElement, Long>> loadAll(Coll<?> coll) | ||||
| 	{ | ||||
| 		// Declare Ret | ||||
| 		// Create Ret | ||||
| 		Map<String, Entry<JsonElement, Long>> ret = null; | ||||
| 		 | ||||
| 		// Get collection directory | ||||
| 		File collDir = getCollDir(coll); | ||||
| 		if ( ! collDir.isDirectory()) return ret; | ||||
| 		// Get Directory | ||||
| 		File directory = getDirectory(coll); | ||||
| 		if ( ! directory.isDirectory()) return ret; | ||||
| 		 | ||||
| 		// Find All | ||||
| 		File[] files = collDir.listFiles(JsonFileFilter.get()); | ||||
| 		File[] files = directory.listFiles(JsonFileFilter.get()); | ||||
| 		 | ||||
| 		// Create Ret | ||||
| 		ret = new LinkedHashMap<String, Entry<JsonElement, Long>>(files.length); | ||||
| @ -174,6 +188,9 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 			 | ||||
| 			// Get Entry | ||||
| 			Entry<JsonElement, Long> entry = loadFile(file); | ||||
| 			// NOTE: The entry can be a failed one with null and 0. | ||||
| 			// NOTE: We add it anyways since it's an informative failure. | ||||
| 			// NOTE: This is supported by our defined specification. | ||||
| 			 | ||||
| 			// Add | ||||
| 			ret.put(id, entry); | ||||
| @ -184,11 +201,11 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public Long save(Coll<?> coll, String id, JsonElement data) | ||||
| 	public long save(Coll<?> coll, String id, JsonElement data) | ||||
| 	{ | ||||
| 		File file = fileFromId(coll, id); | ||||
| 		String content = coll.getGson().toJson(data); | ||||
| 		if (DiscUtil.writeCatch(file, content) == false) return null; | ||||
| 		if (DiscUtil.writeCatch(file, content) == false) return 0; | ||||
| 		return file.lastModified(); | ||||
| 	} | ||||
| 
 | ||||
| @ -203,7 +220,7 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	// UTIL | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	public static File getCollDir(Coll<?> coll) | ||||
| 	public static File getDirectory(Coll<?> coll) | ||||
| 	{ | ||||
| 		return (File) coll.getCollDriverObject(); | ||||
| 	} | ||||
| @ -217,7 +234,7 @@ public class DriverFlatfile extends DriverAbstract | ||||
| 	 | ||||
| 	public static File fileFromId(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		File collDir = getCollDir(coll); | ||||
| 		File collDir = getDirectory(coll); | ||||
| 		File idFile = new File(collDir, id + DOTJSON); | ||||
| 		return idFile; | ||||
| 	} | ||||
|  | ||||
| @ -51,6 +51,23 @@ public class DriverMongo extends DriverAbstract | ||||
| 		DB db = this.getDbInner(uri); | ||||
| 		return new DbMongo(this, db); | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public boolean dropDb(Db db) | ||||
| 	{ | ||||
| 		if ( ! (db instanceof DbMongo)) throw new IllegalArgumentException("db"); | ||||
| 		DbMongo dbMongo = (DbMongo)db; | ||||
| 		 | ||||
| 		try | ||||
| 		{ | ||||
| 			dbMongo.db.dropDatabase(); | ||||
| 			return true; | ||||
| 		} | ||||
| 		catch (Exception e) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public Set<String> getCollnames(Db db) | ||||
| @ -81,15 +98,17 @@ public class DriverMongo extends DriverAbstract | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Long getMtime(Coll<?> coll, String id) | ||||
| 	public long getMtime(Coll<?> coll, String id) | ||||
| 	{ | ||||
| 		DBCollection dbcoll = fixColl(coll); | ||||
| 		 | ||||
| 		BasicDBObject found = (BasicDBObject)dbcoll.findOne(new BasicDBObject(ID_FIELD, id), dboKeysMtime); | ||||
| 		if (found == null) return null; | ||||
| 		if (found == null) return 0; | ||||
| 		 | ||||
| 		// In case there is no _mtime set we assume 0. Probably a manual database addition by the server owner. | ||||
| 		long mtime = found.getLong(MTIME_FIELD, 0); | ||||
| 		// In case there is no _mtime set we assume 1337. | ||||
| 		// NOTE: We can not use 0 since that one is reserved for errors. | ||||
| 		// Probably a manual database addition by the server owner. | ||||
| 		long mtime = found.getLong(MTIME_FIELD, 1337L); | ||||
| 		 | ||||
| 		return mtime; | ||||
| 	} | ||||
| @ -105,7 +124,7 @@ public class DriverMongo extends DriverAbstract | ||||
| 		try | ||||
| 		{ | ||||
| 			ret = new ArrayList<String>(cursor.count()); | ||||
| 			while(cursor.hasNext()) | ||||
| 			while (cursor.hasNext()) | ||||
| 			{ | ||||
| 				Object remoteId = cursor.next().get(ID_FIELD); | ||||
| 				ret.add(remoteId.toString()); | ||||
| @ -130,13 +149,15 @@ public class DriverMongo extends DriverAbstract | ||||
| 		try | ||||
| 		{ | ||||
| 			ret = new HashMap<String, Long>(cursor.count()); | ||||
| 			while(cursor.hasNext()) | ||||
| 			while (cursor.hasNext()) | ||||
| 			{ | ||||
| 				BasicDBObject raw = (BasicDBObject)cursor.next(); | ||||
| 				Object remoteId = raw.get(ID_FIELD); | ||||
| 				 | ||||
| 				// In case there is no _mtime set we assume 0. Probably a manual database addition by the server owner. | ||||
| 				long mtime = raw.getLong(MTIME_FIELD, 0); | ||||
| 				// In case there is no _mtime set we assume 1337. | ||||
| 				// NOTE: We can not use 0 since that one is reserved for errors. | ||||
| 				// Probably a manual database addition by the server owner. | ||||
| 				long mtime = raw.getLong(MTIME_FIELD, 1337L); | ||||
| 				 | ||||
| 				ret.put(remoteId.toString(), mtime); | ||||
| 			} | ||||
| @ -159,13 +180,15 @@ public class DriverMongo extends DriverAbstract | ||||
| 	 | ||||
| 	public Entry<JsonElement, Long> loadRaw(BasicDBObject raw) | ||||
| 	{ | ||||
| 		if (raw == null) return null; | ||||
| 		if (raw == null) return new SimpleEntry<JsonElement, Long>(null, 0L); | ||||
| 		 | ||||
| 		// Throw away the id field | ||||
| 		raw.removeField(ID_FIELD); | ||||
| 		 | ||||
| 		// In case there is no _mtime set we assume 0. Probably a manual database addition by the server owner.  | ||||
| 		Long mtime = 0L; | ||||
| 		// In case there is no _mtime set we assume 1337. | ||||
| 		// NOTE: We can not use 0 since that one is reserved for errors. | ||||
| 		// Probably a manual database addition by the server owner. | ||||
| 		long mtime = 1337L; | ||||
| 		Object mtimeObject = raw.removeField(MTIME_FIELD); | ||||
| 		if (mtimeObject != null) | ||||
| 		{ | ||||
| @ -207,9 +230,9 @@ public class DriverMongo extends DriverAbstract | ||||
| 				 | ||||
| 				// Get Entry | ||||
| 				Entry<JsonElement, Long> entry = loadRaw(raw); | ||||
| 				//if (entry == null) continue; | ||||
| 				// Actually allow adding null entries! | ||||
| 				// they are informative failures! | ||||
| 				// NOTE: The entry can be a failed one with null and 0. | ||||
| 				// NOTE: We add it anyways since it's an informative failure. | ||||
| 				// NOTE: This is supported by our defined specification. | ||||
| 				 | ||||
| 				// Add | ||||
| 				ret.put(id, entry); | ||||
| @ -226,13 +249,13 @@ public class DriverMongo extends DriverAbstract | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public Long save(Coll<?> coll, String id, JsonElement data) | ||||
| 	public long save(Coll<?> coll, String id, JsonElement data) | ||||
| 	{		 | ||||
| 		DBCollection dbcoll = fixColl(coll); | ||||
| 		 | ||||
| 		BasicDBObject dbo = new BasicDBObject(); | ||||
| 		 | ||||
| 		Long mtime = System.currentTimeMillis(); | ||||
| 		long mtime = System.currentTimeMillis(); | ||||
| 		dbo.put(ID_FIELD, id); | ||||
| 		dbo.put(MTIME_FIELD, mtime); | ||||
| 		 | ||||
|  | ||||
| @ -109,15 +109,15 @@ public abstract class Entity<E extends Entity<E>> implements Comparable<E> | ||||
| 		Coll<E> coll = this.getColl(); | ||||
| 		if (coll == null) return; | ||||
| 		 | ||||
| 		if (!coll.inited()) return; | ||||
| 		if ( ! coll.inited()) return; | ||||
| 		 | ||||
| 		coll.changedIds.add(id); | ||||
| 		coll.identifiedModifications.put(id, Modification.UNKNOWN); | ||||
| 	} | ||||
| 	 | ||||
| 	public ModificationState sync() | ||||
| 	public Modification sync() | ||||
| 	{ | ||||
| 		String id = this.getId(); | ||||
| 		if (id == null) return ModificationState.UNKNOWN; | ||||
| 		if (id == null) return Modification.UNKNOWN; | ||||
| 		return this.getColl().syncId(id); | ||||
| 	} | ||||
| 	 | ||||
| @ -134,7 +134,7 @@ public abstract class Entity<E extends Entity<E>> implements Comparable<E> | ||||
| 		String id = this.getId(); | ||||
| 		if (id == null) return; | ||||
| 		 | ||||
| 		this.getColl().loadFromRemote(id, null, false); | ||||
| 		this.getColl().loadFromRemote(id, null); | ||||
| 	} | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
|  | ||||
| @ -43,7 +43,7 @@ public class ExamineThread extends Thread | ||||
| 				long before = System.currentTimeMillis(); | ||||
| 				for (Coll<?> coll : Coll.getInstances()) | ||||
| 				{ | ||||
| 					coll.findSuspects(); | ||||
| 					coll.identifyModifications(); | ||||
| 				} | ||||
| 				long after = System.currentTimeMillis(); | ||||
| 				long duration = after-before; | ||||
|  | ||||
| @ -15,7 +15,7 @@ public class JsonFileFilter implements FileFilter | ||||
| 	// INSTANCE & CONSTRUCT | ||||
| 	// -------------------------------------------- // | ||||
| 	 | ||||
| 	private static JsonFileFilter i = new JsonFileFilter(); | ||||
| 	private static final JsonFileFilter i = new JsonFileFilter(); | ||||
| 	public static JsonFileFilter get() { return i; } | ||||
| 	 | ||||
| 	// -------------------------------------------- // | ||||
|  | ||||
| @ -18,8 +18,8 @@ public class MStore | ||||
| 	private static Map<String, Driver> drivers = new HashMap<String, Driver>(); | ||||
| 	public static boolean registerDriver(Driver driver) | ||||
| 	{ | ||||
| 		if (drivers.containsKey(driver.getName())) return false; | ||||
| 		drivers.put(driver.getName(), driver); | ||||
| 		if (drivers.containsKey(driver.getDriverName())) return false; | ||||
| 		drivers.put(driver.getDriverName(), driver); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package com.massivecraft.massivecore.store; | ||||
| 
 | ||||
| public enum ModificationState | ||||
| public enum Modification | ||||
| { | ||||
| 	LOCAL_ALTER (true, true), | ||||
| 	LOCAL_ATTACH (true, true), | ||||
| @ -19,7 +19,7 @@ public enum ModificationState | ||||
| 	public boolean isLocal() { return this.local; } | ||||
| 	public boolean isRemote() { return this.local == false; } | ||||
| 	 | ||||
| 	private ModificationState(boolean modified, boolean local) | ||||
| 	private Modification(boolean modified, boolean local) | ||||
| 	{ | ||||
| 		this.modified = modified; | ||||
| 		this.local = local; | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user