package com.netifera.platform.internal.model; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import com.db4o.ObjectContainer; import com.db4o.ext.DatabaseClosedException; import com.netifera.platform.api.events.EventListenerManager; import com.netifera.platform.api.events.IEventHandler; import com.netifera.platform.api.log.ILogger; import com.netifera.platform.api.model.IEntity; import com.netifera.platform.api.model.ISpace; import com.netifera.platform.api.model.IWorkspace; import com.netifera.platform.api.model.SpaceNameChangeEvent; import com.netifera.platform.api.model.SpaceTaskChangeEvent; import com.netifera.platform.api.probe.IProbe; import com.netifera.platform.api.tasks.ITaskRecord; import com.netifera.platform.api.tasks.ITaskStatus; import com.netifera.platform.model.ProbeEntity; public class Space implements ISpace { private final static int BACKGROUND_COMMIT_INTERVAL = 5000; /* Unique ID value for this space */ private final long id; /* Name for this space to display in the user interface */ private String name; /* Every space has a root entity which is a realm creating entity. Generally this is a probe entity */ private final IEntity rootEntity; /* Every space is permanently associated with a single Probe entity */ private final ProbeEntity probeEntity; /* The list of entities that are contained in this space. */ private final List<IEntity> spaceEntities; /* The list of tasks which have been executed in this space */ private final List<ITaskRecord> spaceTasks; private final SpaceManager manager; private transient boolean isOpened; /* Optimization to quickly test if an entity is present in this view */ private transient Set<IEntity> entitySet; private transient EventListenerManager spaceChangeListeners; private transient EventListenerManager taskChangeListeners; private transient Thread commitThread; private transient boolean entitiesDirty; private transient boolean tasksDirty; private transient ObjectContainer database; private transient ILogger logger; /* Create a new space */ Space(long id, IProbe probe, String name, IEntity root, SpaceManager manager) { this.id = id; this.probeEntity = (ProbeEntity) probe.getEntity(); this.name = name; this.rootEntity = root; this.spaceEntities = Collections.synchronizedList(new ArrayList<IEntity>()); this.spaceTasks = Collections.synchronizedList(new ArrayList<ITaskRecord>()); this.entitySet = Collections.synchronizedSet(new HashSet<IEntity>()); this.manager = manager; this.database = manager.getDatabase(); this.logger = manager.getLogger().getManager().getLogger("Space : " + name); startCommitThread(); } public void objectOnActivate(ObjectContainer container) { this.database = container; buildEntitySet(); startCommitThread(); } public void open() { isOpened = true; manager.openSpace(this); } public void close() { isOpened = false; manager.closeSpace(this); } public boolean isOpened() { return isOpened; } ObjectContainer getDatabase() { return database; } public List<IEntity> getEntities() { return Collections.unmodifiableList(spaceEntities); } public synchronized void addEntity(IEntity entity) { if(!entitySet.contains(entity)) { entitySet.add(entity); spaceEntities.add(entity); entitiesDirty = true; getEventManager().fireEvent(SpaceContentChangeEvent.createCreationEvent(entity)); manager.notifySpaceChange(this); } } public void updateEntity(IEntity entity) { if(entitySet.contains(entity)) { getEventManager().fireEvent(SpaceContentChangeEvent.createUpdateEvent(entity)); } } public synchronized void removeEntity(IEntity entity) { if(entitySet.contains(entity)) { entitySet.remove(entity); spaceEntities.remove(entity); entitiesDirty = true; getEventManager().fireEvent(SpaceContentChangeEvent.createRemovalEvent(entity)); manager.notifySpaceChange(this); } } public Set<String> getTags() { Set<String> tags = new HashSet<String>(); for (IEntity entity: entitySet) { tags.addAll(entity.getTags()); } return tags; } public synchronized void addTaskRecord(ITaskStatus status) { final TaskRecord record = new TaskRecord(status, this); if(spaceTasks.contains(record)) { return; } spaceTasks.add(record); database.store(record); manager.addTaskToSpace(status.getTaskId(), this); tasksDirty = true; getTaskEventManager().fireEvent(SpaceTaskChangeEvent.createCreationEvent(record)); } public void updateTaskRecord(ITaskRecord record) { getTaskEventManager().fireEvent(SpaceTaskChangeEvent.createUpdateEvent(record)); } public List<ITaskRecord> getTasks() { return Collections.unmodifiableList(spaceTasks); } private void buildEntitySet() { entitySet = new HashSet<IEntity>(); for(IEntity entity : spaceEntities) { entitySet.add(entity); } } public IWorkspace getWorkspace() { return rootEntity.getWorkspace(); } public IEntity getRootEntity() { return rootEntity; } public long getId() { return id; } public long getProbeId() { return probeEntity.getProbeId(); } public String getName() { return name; } public void setName(String name) { this.name = name; database.store(this); manager.notifySpaceChange(this); getEventManager().fireEvent(new SpaceNameChangeEvent(name)); } public int entityCount() { return spaceEntities.size(); } public void addChangeListenerAndPopulate(IEventHandler handler) { getEventManager().addListener(handler); synchronized(spaceEntities) { for(IEntity entity : spaceEntities) { handler.handleEvent(SpaceContentChangeEvent.createCreationEvent(entity)); } } } public void addChangeListener(IEventHandler handler) { getEventManager().addListener(handler); } public void removeChangeListener(IEventHandler handler) { getEventManager().removeListener(handler); } private EventListenerManager getEventManager() { if(spaceChangeListeners == null) { spaceChangeListeners = new EventListenerManager(); } return spaceChangeListeners; } public void addTaskChangeListenerAndPopulate(IEventHandler handler) { getTaskEventManager().addListener(handler); for(ITaskRecord task : spaceTasks) { handler.handleEvent(SpaceTaskChangeEvent.createCreationEvent(task)); } } public void addTaskChangeListener(IEventHandler handler) { getTaskEventManager().addListener(handler); } public void removeTaskChangeListener(IEventHandler handler) { getTaskEventManager().removeListener(handler); } private EventListenerManager getTaskEventManager() { if(taskChangeListeners == null) { taskChangeListeners = new EventListenerManager(); } return taskChangeListeners; } public String toString() { return "Space (" + name + ") " + (isOpened ? "[opened]" : "[closed]"); } public boolean equals(Object other) { if(!(other instanceof Space)) { return false; } return ((Space)other).getId() == id; } public int hashCode() { return (int) id; } private void startCommitThread() { commitThread = new Thread(new Runnable() { public void run() { while(true) { try { Thread.sleep(BACKGROUND_COMMIT_INTERVAL); if(database.ext().isClosed()) return; runCommit(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } catch (DatabaseClosedException e) { return; } } } }); commitThread.setDaemon(true); commitThread.setName("Background Commit thread for space [" + name + "]"); commitThread.start(); } private synchronized void runCommit() { if(tasksDirty) { commitTasks(); tasksDirty = false; } if(entitiesDirty) { commitEntities(); entitiesDirty = false; } } private void commitEntities() { synchronized(spaceEntities) { database.store(spaceEntities); } } private void commitTasks() { synchronized (spaceTasks) { database.store(spaceTasks); } } }