Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jpos.space.TSpace Maven / Gradle / Ivy
/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2023 jPOS Software SRL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package org.jpos.space;
import org.jpos.util.Loggeable;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* TSpace implementation
* @author Alejandro Revilla
* @version $Revision$ $Date$
* @since !.4.9
*/
@SuppressWarnings("unchecked")
public class TSpace implements LocalSpace, Loggeable, Runnable {
protected Map entries;
protected TSpace sl; // space listeners
public static final long GCDELAY = 5*1000;
private static final long GCLONG = 60*1000;
private static final long NRD_RESOLUTION = 500L;
private static final int MAX_ENTRIES_IN_DUMP = 1000;
private static final long ONE_MILLION = 1_000_000L; // multiplier millis --> nanos
private final Set[] expirables;
private long lastLongGC = System.nanoTime();
public TSpace () {
super();
entries = new HashMap ();
expirables = new Set[] { new HashSet(), new HashSet() };
SpaceFactory.getGCExecutor().scheduleAtFixedRate(this, GCDELAY, GCDELAY, TimeUnit.MILLISECONDS);
}
@Override
public void out (K key, V value) {
if (key == null || value == null)
throw new NullPointerException ("key=" + key + ", value=" + value);
synchronized(this) {
List l = getList(key);
l.add (value);
if (l.size() == 1)
this.notifyAll ();
}
if (sl != null)
notifyListeners(key, value);
}
@Override
public void out (K key, V value, long timeout) {
if (key == null || value == null)
throw new NullPointerException ("key=" + key + ", value=" + value);
Object v = value;
if (timeout > 0) {
v = new Expirable (value, System.nanoTime() + (timeout * ONE_MILLION));
}
synchronized (this) {
List l = getList(key);
l.add(v);
if (l.size() == 1)
this.notifyAll ();
if (timeout > 0) {
registerExpirable(key, timeout);
}
}
if (sl != null)
notifyListeners(key, value);
}
@Override
public synchronized V rdp (Object key) {
if (key instanceof Template)
return (V) getObject ((Template) key, false);
return (V) getHead (key, false);
}
@Override
public synchronized V inp (Object key) {
if (key instanceof Template)
return (V) getObject ((Template) key, true);
return (V) getHead (key, true);
}
@Override
public synchronized V in (Object key) {
Object obj;
while ((obj = inp (key)) == null) {
try {
this.wait ();
} catch (InterruptedException e) { }
}
return (V) obj;
}
@Override
public synchronized V in (Object key, long timeout) {
V obj;
long now = System.nanoTime();
long to = now + timeout * ONE_MILLION;
long waitFor;
while ( (obj = inp (key)) == null &&
(waitFor = (to - System.nanoTime())) >= 0 )
{
try {
this.wait(Math.max(waitFor / ONE_MILLION, 1L));
} catch (InterruptedException e) { }
}
return obj;
}
@Override
public synchronized V rd (Object key) {
Object obj;
while ((obj = rdp (key)) == null) {
try {
this.wait ();
} catch (InterruptedException e) { }
}
return (V) obj;
}
@Override
public synchronized V rd (Object key, long timeout) {
V obj;
long now = System.nanoTime();
long to = now + (timeout * ONE_MILLION);
long waitFor;
while ( (obj = rdp (key)) == null &&
(waitFor = (to - System.nanoTime())) >= 0 )
{
try {
this.wait(Math.max(waitFor / ONE_MILLION, 1L));
} catch (InterruptedException e) { }
}
return obj;
}
@Override
public synchronized void nrd (Object key) {
while (rdp (key) != null) {
try {
this.wait (NRD_RESOLUTION);
} catch (InterruptedException ignored) { }
}
}
@Override
public synchronized V nrd (Object key, long timeout) {
V obj;
long now = System.nanoTime();
long to = now + (timeout * ONE_MILLION);
long waitFor;
while ( (obj = rdp (key)) != null &&
(waitFor = (to - System.nanoTime())) >= 0 )
{
try {
this.wait(Math.min(NRD_RESOLUTION,
Math.max(waitFor / ONE_MILLION, 1L)));
} catch (InterruptedException ignored) { }
}
return obj;
}
@Override
public void run () {
try {
gc();
} catch (Exception e) {
e.printStackTrace(); // this should never happen
}
}
public void gc () {
gc(0);
if (System.nanoTime() - lastLongGC > GCLONG) {
gc(1);
lastLongGC = System.nanoTime();
}
}
private void gc (int generation) {
Set exps;
synchronized (this) {
exps = expirables[generation];
expirables[generation] = new HashSet();
}
for (K k : exps) {
if (rdp(k) != null) {
synchronized (this) {
expirables[generation].add(k);
}
}
Thread.yield ();
}
if (sl != null) {
synchronized (this) {
if (sl != null && sl.isEmpty())
sl = null;
}
}
}
@Override
public synchronized int size (Object key) {
int size = 0;
List l = (List) entries.get (key);
if (l != null)
size = l.size();
return size;
}
@Override
public synchronized void addListener (Object key, SpaceListener listener) {
getSL().out (key, listener);
}
@Override
public synchronized void addListener
(Object key, SpaceListener listener, long timeout)
{
getSL().out (key, listener, timeout);
}
@Override
public synchronized void removeListener
(Object key, SpaceListener listener)
{
if (sl != null) {
sl.inp (new ObjectTemplate (key, listener));
}
}
public boolean isEmpty() {
return entries.isEmpty();
}
@Override
public synchronized Set getKeySet() {
return new HashSet(entries.keySet());
}
public String getKeysAsString () {
StringBuilder sb = new StringBuilder();
Object[] keys;
synchronized (this) {
keys = entries.keySet().toArray();
}
for (int i=0; i 0)
sb.append (' ');
sb.append (keys[i]);
}
return sb.toString();
}
@Override
public void dump(PrintStream p, String indent) {
Object[] keys;
int size = entries.size();
if (size > MAX_ENTRIES_IN_DUMP * 100) {
p.printf ("%sWARNING - space too big, size=%d%n", indent, size);
return;
}
synchronized (this) {
keys = entries.keySet().toArray();
}
int i=0;
for (Object key : keys) {
p.printf("%s%s %n", indent, size(key), key);
if (i++ > MAX_ENTRIES_IN_DUMP) {
p.printf ("%s...%n", indent);
p.printf ("%s...%n", indent);
break;
}
}
p.printf("%s key-count: %d%n", indent, keys.length);
int exp0, exp1;
synchronized (this) {
exp0 = expirables[0].size();
exp1 = expirables[1].size();
}
p.printf("%s gcinfo: %d,%d%n", indent, exp0, exp1);
}
public void notifyListeners (Object key, Object value) {
Object[] listeners = null;
synchronized (this) {
if (sl == null)
return;
List l = (List) sl.entries.get (key);
if (l != null)
listeners = l.toArray();
}
if (listeners != null) {
for (Object listener : listeners) {
Object o = listener;
if (o instanceof Expirable)
o = ((Expirable) o).getValue();
if (o instanceof SpaceListener)
((SpaceListener) o).notify(key, value);
}
}
}
@Override
public void push (K key, V value) {
if (key == null || value == null)
throw new NullPointerException ("key=" + key + ", value=" + value);
synchronized(this) {
List l = getList(key);
boolean wasEmpty = l.isEmpty();
l.add (0, value);
if (wasEmpty)
this.notifyAll ();
}
if (sl != null)
notifyListeners(key, value);
}
@Override
public void push (K key, V value, long timeout) {
if (key == null || value == null)
throw new NullPointerException ("key=" + key + ", value=" + value);
Object v = value;
if (timeout > 0) {
v = new Expirable (value, System.nanoTime() + (timeout * ONE_MILLION));
}
synchronized (this) {
List l = getList(key);
boolean wasEmpty = l.isEmpty();
l.add (0, v);
if (wasEmpty)
this.notifyAll ();
if (timeout > 0) {
registerExpirable(key, timeout);
}
}
if (sl != null)
notifyListeners(key, value);
}
@Override
public void put (K key, V value) {
if (key == null || value == null)
throw new NullPointerException ("key=" + key + ", value=" + value);
synchronized (this) {
List l = new LinkedList();
l.add (value);
entries.put (key, l);
this.notifyAll ();
}
if (sl != null)
notifyListeners(key, value);
}
@Override
public void put (K key, V value, long timeout) {
if (key == null || value == null)
throw new NullPointerException ("key=" + key + ", value=" + value);
Object v = value;
if (timeout > 0) {
v = new Expirable (value, System.nanoTime() + (timeout * ONE_MILLION));
}
synchronized (this) {
List l = new LinkedList();
l.add (v);
entries.put (key, l);
this.notifyAll ();
if (timeout > 0) {
registerExpirable(key, timeout);
}
}
if (sl != null)
notifyListeners(key, value);
}
@Override
public boolean existAny (K[] keys) {
for (K key : keys) {
if (rdp(key) != null)
return true;
}
return false;
}
@Override
public boolean existAny (K[] keys, long timeout) {
long now = System.nanoTime();
long to = now + (timeout * ONE_MILLION);
long waitFor;
while ((waitFor = (to - System.nanoTime())) >= 0) {
if (existAny (keys))
return true;
synchronized (this) {
try {
this.wait(Math.max(waitFor / ONE_MILLION, 1L));
} catch (InterruptedException e) { }
}
}
return false;
}
/**
* unstandard method (required for space replication) - use with care
* @return underlying entry map
*/
public Map getEntries () {
return entries;
}
/**
* unstandard method (required for space replication) - use with care
* @param entries underlying entry map
*/
public void setEntries (Map entries) {
this.entries = entries;
}
private List getList (Object key) {
List l = (List) entries.get (key);
if (l == null)
entries.put (key, l = new LinkedList());
return l;
}
private Object getHead (Object key, boolean remove) {
Object obj = null;
List l = (List) entries.get (key);
boolean wasExpirable = false;
while (obj == null && l != null && l.size() > 0) {
obj = l.get(0);
if (obj instanceof Expirable) {
obj = ((Expirable) obj).getValue();
wasExpirable = true;
}
if (obj == null) {
l.remove (0);
if (l.isEmpty()) {
entries.remove (key);
}
}
}
if (l != null) {
if (remove && obj != null)
l.remove (0);
if (l.isEmpty()) {
entries.remove (key);
if (wasExpirable)
unregisterExpirable(key);
}
}
return obj;
}
private Object getObject (Template tmpl, boolean remove) {
Object obj = null;
Object key = tmpl.getKey();
List l = (List) entries.get (key);
if (l != null) {
Iterator iter = l.iterator();
boolean wasExpirable = false;
while (iter.hasNext()) {
obj = iter.next();
if (obj instanceof Expirable) {
obj = ((Expirable) obj).getValue();
if (obj == null) {
iter.remove();
wasExpirable = true;
continue;
}
}
if (tmpl.equals (obj)) {
if (remove)
iter.remove();
break;
} else
obj = null;
}
if (l.isEmpty()) {
entries.remove (key);
if (wasExpirable)
unregisterExpirable(key);
}
}
return obj;
}
private TSpace getSL() {
synchronized (this) {
if (sl == null)
sl = new TSpace();
}
return sl;
}
private void registerExpirable(K k, long t) {
expirables[t > GCLONG ? 1 : 0].add(k);
}
private void unregisterExpirable(Object k) {
for (Set s : expirables)
s.remove(k);
}
static class Expirable implements Comparable, Serializable {
private static final long serialVersionUID = 0xA7F22BF5;
Object value;
/**
* When to expire, in the future, as given by monotonic System.nanoTime().
* IMPORTANT: always use a nanosec offset from System.nanoTime()!
*/
long expires;
Expirable (Object value, long expires) {
super();
this.value = value;
this.expires = expires;
}
boolean isExpired () {
return (System.nanoTime() - expires) > 0;
}
@Override
public String toString() {
return getClass().getName()
+ "@" + Integer.toHexString(hashCode())
+ ",value=" + value.toString()
+ ",expired=" + isExpired ();
}
Object getValue() {
return isExpired() ? null : value;
}
@Override
public int compareTo (Object other) {
long diff = this.expires - ((Expirable)other).expires;
return diff > 0 ? 1 :
diff < 0 ? -1 :
0;
}
}
}