net.anotheria.anoprise.sessiondistributor.cache.SDCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ano-prise Show documentation
Show all versions of ano-prise Show documentation
Collection of utils for different enterprise class projects. Among other stuff contains
Caches, Mocking, DualCrud, MetaFactory and SessionDistributorService. Visit https://opensource.anotheria.net for details.
package net.anotheria.anoprise.sessiondistributor.cache;
import net.anotheria.anoprise.eventservice.Event;
import net.anotheria.anoprise.eventservice.EventServiceFactory;
import net.anotheria.anoprise.eventservice.EventServicePushConsumer;
import net.anotheria.anoprise.eventservice.util.QueuedEventReceiver;
import net.anotheria.anoprise.fs.FSSaveable;
import net.anotheria.anoprise.sessiondistributor.DistributedSessionAttribute;
import net.anotheria.anoprise.sessiondistributor.DistributedSessionVO;
import net.anotheria.anoprise.sessiondistributor.NoSuchDistributedSessionException;
import net.anotheria.anoprise.sessiondistributor.SessionDistributorServiceConfig;
import net.anotheria.anoprise.sessiondistributor.SessionDistributorServiceImpl;
import net.anotheria.anoprise.sessiondistributor.cache.events.SDCacheEvent;
import net.anotheria.anoprise.sessiondistributor.cache.events.SDCacheEventAnnouncer;
import net.anotheria.util.IdCodeGenerator;
import net.anotheria.util.StringUtils;
import net.anotheria.util.concurrency.IdBasedLock;
import net.anotheria.util.concurrency.IdBasedLockManager;
import net.anotheria.util.concurrency.SafeIdBasedLockManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Defines SDCache, with serialization possibility, etc.
*/
public final class SDCache implements FSSaveable {
/**
* Basic serial version UID.
*/
private static final long serialVersionUID = -1947015503980653358L;
/**
* Default id of current cache node in the cluster.
* Actually this Id will be used for NON clustered environment, as SERIALIZED cache file name.
*/
static final String DEFAULT_NODE_ID = "1000";
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(SDCache.class);
/**
* {@link IdBasedLockManager} instance.
*/
private transient IdBasedLockManager lockManager;
/**
* {@link SessionDistributorServiceConfig} configuration.
*/
private transient SessionDistributorServiceConfig config;
/**
* Event sender.
*/
private transient SDCacheEventAnnouncer announcer;
/**
* Unique id of current cache in the cluster.
* Also serves as FileName for Serialized cache - on FS!
* If nodeId won't be provided via System.property with configured name - then DEFAULT_NODE_ID will be used.
* And cluster features won't be enabled.
*/
private transient String nodeId = DEFAULT_NODE_ID;
/**
* Internal storage for session holders.
*/
private ConcurrentMap sessions;
/**
* Constructor.
*/
protected SDCache() {
sessions = new ConcurrentHashMap();
init();
}
/**
* Initialise, integration and internal stuff.
*/
private void init() {
lockManager = new SafeIdBasedLockManager();
config = SessionDistributorServiceConfig.getInstance();
//checking that clustering is enabled
if (config.isMultipleInstancesEnabled())
configureClusterIntegration();
}
/**
* Configure integration STUFF.
*/
private void configureClusterIntegration() {
LOG.info("Cluster integration configuration started");
String systemPropertyNodeId = System.getProperty(config.getNodeIdSystemPropertyName());
if (StringUtils.isEmpty(systemPropertyNodeId)) {
LOG.warn("SD - Cluster configuration FAILED. Required nodeId property[" + config.getNodeIdSystemPropertyName() + "] is absent in SystemProperties, or badly configured!" +
" Working in standAlone mode!");
return;
}
nodeId = systemPropertyNodeId;
//announcer!
announcer = new SDCacheEventAnnouncer();
//receiver configuration
SDCacheEventsConsumer cacheConsumer = new SDCacheEventsConsumer();
QueuedEventReceiver sdCacheEventReceiver = new QueuedEventReceiver("SDCacheEventReceiver",
SDCacheEventAnnouncer.EVENT_CHANNEL_NAME, cacheConsumer,
config.getSdCacheEventQueueSize(),
config.getSessionDistributorEventQueueSleepTime(), LOG);
//consumer registration
EventServiceFactory.createEventService().obtainEventChannel(SDCacheEventAnnouncer.EVENT_CHANNEL_NAME, sdCacheEventReceiver).addConsumer(cacheConsumer);
sdCacheEventReceiver.start();
LOG.info("Cluster integration stuff successfully configured! NodeId{" + nodeId + "}");
}
/**
* Returns cached sessions count.
*
* @return amount of cached sessions
*/
public int getCount() {
return sessions.size();
}
/**
* Returns all cached sessions.
*
* @return {@link java.util.List }
*/
public List getSessions() {
return new ArrayList(sessions.values());
}
/**
* Return session ids.
*
* @return ids collection
*/
public List getSessionIds() {
return new ArrayList(sessions.keySet());
}
/**
* Return true if session with selected id exists.
*
* @param sessionId id of distributed session
* @return boolean value
*/
public boolean sessionExists(String sessionId) {
return sessions.containsKey(sessionId);
}
/**
* Create session.
*
* @param possibleSessionId possible id of session.
* @return session id
*/
public String createSession(String possibleSessionId) {
DistributedSessionVO toCreate = new DistributedSessionVO(possibleSessionId);
if (!sessionExists(possibleSessionId)) {
sessions.put(possibleSessionId, toCreate);
//send event! CREATE
announceSave(toCreate);
return possibleSessionId;
}
DistributedSessionVO old;
do {
toCreate.setName(IdCodeGenerator.generateCode(SessionDistributorServiceImpl.SESSION_ID_LENGTH));
old = sessions.putIfAbsent(toCreate.getName(), toCreate);
} while (old != null);
//send event! CREATE
announceSave(toCreate);
return toCreate.getName();
}
/**
* Remove session with selected id.
*
* @param sessionId id of session
* @throws net.anotheria.anoprise.sessiondistributor.NoSuchDistributedSessionException
* if session does not exists
*/
public void removeSession(String sessionId) throws NoSuchDistributedSessionException {
IdBasedLock lock = lockManager.obtainLock(sessionId);
lock.lock();
try {
DistributedSessionVO session = getSession(sessionId);
sessions.remove(sessionId);
//Announce delete
announceDelete(session);
} finally {
lock.unlock();
}
}
/**
* Returns distributed session if such exists.
*
* @param sessionId session id
* @return {@link DistributedSessionVO} if such exists
* @throws NoSuchDistributedSessionException
* if session does not exists
*/
public DistributedSessionVO getSession(String sessionId) throws NoSuchDistributedSessionException {
if (!sessionExists(sessionId) || sessions.get(sessionId) == null)
throw new NoSuchDistributedSessionException(sessionId);
return sessions.get(sessionId);
}
/**
* Update userId property for session with selected id.
*
* @param sessionId id of the session
* @param userId user id
* @throws NoSuchDistributedSessionException
* if no such session exists
*/
public void updateSessionUserId(String sessionId, String userId) throws NoSuchDistributedSessionException {
IdBasedLock lock = lockManager.obtainLock(sessionId);
lock.lock();
try {
DistributedSessionVO session = getSession(sessionId);
session.setUserId(userId);
session.setLastChangeTime(System.currentTimeMillis());
//update event
announceSave(session);
} finally {
lock.unlock();
}
}
/**
* Update editorId property for session with selected id.
*
* @param sessionId id of the session
* @param editorId user id
* @throws NoSuchDistributedSessionException
* if no such session exists
*/
public void updateSessionEditorId(String sessionId, String editorId) throws NoSuchDistributedSessionException {
IdBasedLock lock = lockManager.obtainLock(sessionId);
lock.lock();
try {
DistributedSessionVO session = getSession(sessionId);
session.setEditorId(editorId);
session.setLastChangeTime(System.currentTimeMillis());
//update event
announceSave(session);
} finally {
lock.unlock();
}
}
/**
* Add attribute to session with selected id.
*
* @param sessionId id of the session
* @param attribute {@link net.anotheria.anoprise.sessiondistributor.DistributedSessionAttribute}
* @throws NoSuchDistributedSessionException
* if no such session exists
*/
public void addAttribute(String sessionId, DistributedSessionAttribute attribute) throws NoSuchDistributedSessionException {
IdBasedLock lock = lockManager.obtainLock(sessionId);
lock.lock();
try {
DistributedSessionVO session = getSession(sessionId);
session.addDistributedAttribute(attribute);
session.setLastChangeTime(System.currentTimeMillis());
//update event
announceSave(session);
} finally {
lock.unlock();
}
}
/**
* Remove attribute to session with selected id.
*
* @param sessionId id of the session
* @param attribute session attribute name
* @throws NoSuchDistributedSessionException
* if no such session exists
*/
public void removeAttribute(String sessionId, String attribute) throws NoSuchDistributedSessionException {
IdBasedLock lock = lockManager.obtainLock(sessionId);
lock.lock();
try {
DistributedSessionVO session = getSession(sessionId);
session.removeDistributedAttribute(attribute);
session.setLastChangeTime(System.currentTimeMillis());
//update event ! should send Update announce even if no Remove of attribute was performed! cause at least UseTime was updated!!!:)
announceSave(session);
} finally {
lock.unlock();
}
}
/**
* Updates session call time
*
* @param sessionId id of the session
* @throws NoSuchDistributedSessionException
* if no such session exists
*/
public void updateCallTime(String sessionId) throws NoSuchDistributedSessionException {
IdBasedLock lock = lockManager.obtainLock(sessionId);
lock.lock();
try {
DistributedSessionVO session = getSession(sessionId);
session.setLastChangeTime(System.currentTimeMillis());
//update event
announceSave(session);
} finally {
lock.unlock();
}
}
@Override
public String getOwnerId() {
return nodeId;
}
@Override
public String toString() {
return "SDCache{" +
"sessions-size=" + sessions.size() +
", ownerId='" + getOwnerId() + '\'' +
'}';
}
/**
* Serialization method.
*
* @param oos - {@link java.io.ObjectOutputStream}
* @throws java.io.IOException on serialization errors
*/
private void writeObject(ObjectOutputStream oos) throws IOException {
ObjectOutputStream.PutField fields = oos.putFields();
fields.put("sessions", sessions);
oos.writeFields();
}
/**
* DeSerialization method.
*
* @param ois - {@link java.io.ObjectInputStream}
* @throws ClassNotFoundException on DeSerialization errors
* @throws java.io.IOException on DeSerialization errors
*/
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ObjectInputStream.GetField fields = ois.readFields();
sessions = (ConcurrentMap) fields.get("sessions", new ConcurrentHashMap());
LOG.info("Reading persisted Sessions - started");
//prolong life time! after reading from file!
long time = System.currentTimeMillis();
for (DistributedSessionVO session : sessions.values())
session.setLastChangeTime(time);
//execute initialization!
init();
LOG.info("Reading persisted Sessions - completed! " + sessions.size() + "Read successfully, LastChangeTime updated!");
}
/**
* Announce save event to cluster.
*
* @param session {@link DistributedSessionVO}
*/
private void announceSave(DistributedSessionVO session) {
if (!config.isMultipleInstancesEnabled())
return;
if (announcer == null)
return;
announcer.sessionSave(nodeId, session);
}
/**
* Announce delete to cluster.
*
* @param session {@link DistributedSessionVO}
*/
private void announceDelete(DistributedSessionVO session) {
if (!config.isMultipleInstancesEnabled())
return;
if (announcer == null)
return;
announcer.sessionDelete(nodeId, session);
}
/**
* Remove session. Method is used from Consumer!
*
* @param incomingNodeId node id
* @param session {@link DistributedSessionVO to remove}
*/
private void remove(String incomingNodeId, DistributedSessionVO session) {
boolean isDebug = LOG.isDebugEnabled();
if (isDebug)
LOG.debug("Called remove(" + incomingNodeId + "," + session + ")");
//should not handle! This is the event from current SDCache!
if (nodeId.equals(incomingNodeId))
return;
IdBasedLock lock = lockManager.obtainLock(session.getName());
lock.lock();
try {
try {
DistributedSessionVO onCurrentNode = getSession(session.getName());
// remove if it's possible!!!
if (onCurrentNode.getLastChangeTime() <= session.getLastChangeTime()) {
sessions.remove(session.getName());
if (isDebug)
LOG.debug("Session[" + session.getName() + "] removed by delete event, on node[" + nodeId + "] - event comes from remote node[" + incomingNodeId + "] ");
}
if (isDebug)
LOG.debug("Session[" + session.getName() + "] can't be removed by delete event, on node[" + nodeId + "], cause It is still in use!!!!! - " +
"event comes by remote node[" + incomingNodeId + "] ");
} catch (NoSuchDistributedSessionException e) {
// Session does not exists on current node!!
LOG.warn("NoSuchDistributedSessionException - occurred on delete try of session with name [" + session.getName() + "] on node : [" + nodeId + "]," +
" - deletion event comes from remote_node:[" + incomingNodeId + "]");
}
} finally {
lock.unlock();
}
}
/**
* Save session. Method is used from Consumer!
*
* @param incomingNodeId node id
* @param session {@link DistributedSessionVO to remove}
*/
private void save(String incomingNodeId, DistributedSessionVO session) {
boolean isDebug = LOG.isDebugEnabled();
if (isDebug)
LOG.debug("Called save(" + incomingNodeId + "," + session + ")");
//should not handle! This is the event from current SDCache!
if (nodeId.equals(incomingNodeId))
return;
IdBasedLock lock = lockManager.obtainLock(session.getName());
lock.lock();
try {
try {
DistributedSessionVO onCurrentNode = getSession(session.getName());
// update if it's possible!!!
if (onCurrentNode.getLastChangeTime() < session.getLastChangeTime()) {
sessions.put(session.getName(), session);
if (isDebug)
LOG.debug("Session[" + session.getName() + "] updated on node[" + nodeId + "] by event from remote_node[" + incomingNodeId + "]");
}
} catch (NoSuchDistributedSessionException e) {
sessions.put(session.getName(), session);
if (isDebug)
LOG.debug("Session [" + session.getName() + "] created on node[" + nodeId + "], by event from remote_node[" + incomingNodeId + "]");
}
} finally {
lock.unlock();
}
}
/**
* Events consumer - for SDCache.
*/
private class SDCacheEventsConsumer implements EventServicePushConsumer {
/**
* {@link Logger} instance.
*/
private final Logger log = LoggerFactory.getLogger(SDCacheEventsConsumer.class);
@Override
public void push(Event incomingEvent) {
if (log.isDebugEnabled())
log.debug("SDCacheEvent : " + incomingEvent);
if (incomingEvent == null || incomingEvent.getData() == null || !(incomingEvent.getData() instanceof SDCacheEvent))
return;
SDCacheEvent cacheEvent = SDCacheEvent.class.cast(incomingEvent.getData());
switch (cacheEvent.getOperation()) {
case CACHE_SESSION_SAVE:
save(cacheEvent.getClusterNodeId(), cacheEvent.getSession());
break;
case CACHE_SESSION_REMOVE:
remove(cacheEvent.getClusterNodeId(), cacheEvent.getSession());
break;
default:
log.warn(cacheEvent.getOperation().name() + " NOT supported in current implementation");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy