org.jpos.space.HzlSpace.java Source code

Java tutorial

Introduction

Here is the source code for org.jpos.space.HzlSpace.java

Source

/*
 * Hazelcast Space for JPOS
 * Copyright (C) 2012 Eric Reuthe/ Wired-Mind Labs, LLC
 *
 * Code based on Alejandro Revilla's
 * TSpace implementation
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

package org.jpos.space;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Set;
import java.util.LinkedList;
import java.util.*;
import java.io.Serializable;

import com.hazelcast.client.ClientConfig;
import com.hazelcast.client.HazelcastClient;
import com.hazelcast.config.Config;
import com.hazelcast.config.GroupConfig;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.nio.Serializer.DataSerializer;
import com.hazelcast.core.*;

import org.apache.commons.collections.map.SingletonMap;
import org.jpos.util.Loggeable;
import org.jpos.core.Configuration;

import java.util.concurrent.*;

/**
 * HzlSpace implementation for JPOS
 *
 * @author Eric Reuthe
 * @version $Revision$ $Date$
 */

public class HzlSpace<K, V> implements LocalSpace<K, V>, Loggeable, HazelcastInstanceAware {
    private volatile HazelcastInstance instance = null;
    private String hzlConfigFile;
    private String spaceName;
    private DataSerializer serializer = new DataSerializer();
    protected Configuration cfg;
    protected Config hzlConfig;
    protected TSpace<K, V> liveObjectSpace = new TSpace<K, V>();
    protected IMap<Object, LinkedList<Object>> entries;
    protected HzlSpace sl; // space listeners
    protected IList<Set> expirables;
    protected ScheduledExecutorService cleanupScheduler;
    public static final long GCDELAY = 60 * 1000;
    private static final long GCLONG = GCDELAY * 5;
    private long lastLongGC = System.currentTimeMillis();

    public HzlSpace(String spaceName) {
        super();
        createSpace(spaceName, "");
    }

    public HzlSpace(String spaceName, String configFile) {
        super();
        createSpace(spaceName, configFile);
    }

    public HzlSpace(Configuration cfg) {
        super();
        this.cfg = cfg;
        this.spaceName = cfg.get("space-name", "hzl:DefaultSpace");
        createSpace(spaceName, "");
    }

    public HzlSpace() {
        super();
        createSpace("hzl:DefaultSpace", "");
    }

    private void createSpace(String spaceName, String configFile) {
        if (configFile != null && !configFile.isEmpty()) {
            this.hzlConfigFile = configFile;
        } else {
            if (this.cfg != null) {
                this.hzlConfigFile = cfg.get("hzlConfigFile", "");
            }
        }

        if (this.hzlConfigFile != null && !this.hzlConfigFile.isEmpty()) {
            try {
                this.hzlConfig = new XmlConfigBuilder(this.hzlConfigFile).build();
                this.hzlConfig.setLiteMember(cfg.getBoolean("liteMember", false));
            } catch (FileNotFoundException e) {
                throw new SpaceError(e);
            }
        }

        try {
            if (this.cfg != null && this.hzlConfig != null) {
                this.instance = Hazelcast.init(this.hzlConfig);
            } else {
                this.instance = Hazelcast.getDefaultInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.entries = this.instance.getMap(spaceName);
        this.expirables = this.instance.getList(spaceName + "-expirables");

        this.expirables.add(new HashSet<K>());
        this.expirables.add(new HashSet<K>());
        this.cleanupScheduler = Executors.newScheduledThreadPool(1);
    }

    public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
        this.instance = hazelcastInstance;
    }

    public void out(K key, V value) {
        if (key == null || value == null)
            throw new NullPointerException("key=" + key + ", value=" + value);
        synchronized (this) {
            if (isLiveObject(value)) {
                liveObjectSpace.out(key, value);
                Map<String, K> k = new SingletonMap("localkey", key);
                value = (V) k;
            }
            LinkedList<Object> l = entries.get(key);
            if (l == null)
                l = new LinkedList<Object>();
            l.add(value);
            entries.put(key, l);
            this.notifyAll();
            if (sl != null)
                notifyListeners(key, value);
        }
    }

    public void out(K key, V value, long timeout) {
        if (key == null || value == null)
            throw new NullPointerException("key=" + key + ", value=" + value);

        synchronized (this) {
            if (isLiveObject(value)) {
                liveObjectSpace.out(key, value);
                Map<String, K> k = new SingletonMap("localkey", key);
                value = (V) k;
            }
            Object v = value;
            if (timeout > 0) {
                v = new Expirable(value, System.currentTimeMillis() + timeout);
            }
            LinkedList<Object> l = entries.get(key);
            if (l == null)
                l = new LinkedList<Object>();
            l.add(v);
            entries.put(key, l);
            this.notifyAll();
            if (sl != null)
                notifyListeners(key, value);
            if (timeout > 0) {
                registerExpirable(key, timeout);
            }
        }
    }

    public synchronized V rdp(Object key) {
        Object v = null;
        if (key instanceof Template)
            v = getObject((Template) key, false);
        else
            v = getHead(key, false);

        if (v instanceof SingletonMap)
            v = liveObjectSpace.rdp(key);

        return (V) v;
    }

    public synchronized V inp(Object key) {
        Object v = null;
        if (key instanceof Template) {
            v = getObject((Template) key, true);
        } else {
            v = getHead(key, true);
        }

        if (v instanceof SingletonMap) {
            v = liveObjectSpace.inp(key);
        }

        return (V) v;
    }

    public synchronized V in(Object key) {
        Object obj;
        while ((obj = inp(key)) == null) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                ///
            }
        }
        return (V) obj;
    }

    public synchronized V in(Object key, long timeout) {
        Object obj;
        long now = System.currentTimeMillis();
        long end = now + timeout;
        while ((obj = inp(key)) == null && ((now = System.currentTimeMillis()) < end)) {
            try {
                this.wait(end - now);
            } catch (InterruptedException e) {
                ///
            }
        }
        return (V) obj;
    }

    public synchronized V rd(Object key) {
        Object obj;
        while ((obj = rdp(key)) == null) {
            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        }
        return (V) obj;
    }

    public synchronized V rd(Object key, long timeout) {
        Object obj;
        long now = System.currentTimeMillis();
        long end = now + timeout;
        while ((obj = rdp(key)) == null && ((now = System.currentTimeMillis()) < end)) {
            try {
                this.wait(end - now);
            } catch (InterruptedException e) {
            }
        }
        return (V) obj;
    }

    public synchronized int size(Object key) {
        int size = 0;
        LinkedList<Object> l = entries.get(key);
        if (l != null)
            size = l.size();
        return size;
    }

    public synchronized void addListener(Object key, SpaceListener listener) {
        getSL().out(key, listener);
    }

    public synchronized void addListener(Object key, SpaceListener listener, long timeout) {
        getSL().out(key, listener, timeout);
    }

    public synchronized void removeListener(Object key, SpaceListener listener) {
        if (sl != null) {
            sl.inp(new ObjectTemplate(key, listener));
        }
    }

    public boolean isEmpty() {
        return entries.isEmpty();
    }

    public Set getKeySet() {
        return entries.keySet();
    }

    public String getKeysAsString() {
        StringBuffer sb = new StringBuffer();
        Object[] keys;

        synchronized (this) {
            keys = this.entries.keySet().toArray();
        }

        for (int i = 0; i < keys.length; i++) {
            if (i > 0)
                sb.append(' ');
            sb.append(keys[i]);
        }
        return sb.toString();
    }

    public void dump(PrintStream p, String indent) {
        Object[] keys;
        synchronized (this) {
            keys = this.entries.keySet().toArray();
        }

        for (int i = 0; i < keys.length; i++) {
            p.printf("%s<key count='%d'>%s</key>\n", indent, size(keys[i]), keys[i]);
        }
        p.println(indent + "<keycount>" + (keys.length) + "</keycount>");
        long exp0, exp1;
        synchronized (this) {
            exp0 = this.expirables.get(0).size();
            exp1 = this.expirables.get(1).size();
        }
        p.println(String.format("%s<gcinfo>%d,%d</gcinfo>\n", indent, exp0, exp1));
    }

    public void notifyListeners(Object key, Object value) {
        Object[] listeners = null;

        synchronized (this) {
            if (sl == null)
                return;
            LinkedList<Object> l = (LinkedList<Object>) sl.entries.get(key);
            if (l != null)
                listeners = l.toArray();
        }
        if (listeners != null) {
            for (int i = 0; i < listeners.length; i++) {
                Object o = listeners[i];
                if (o instanceof Expirable)
                    o = ((Expirable) o).getValue();
                if (o instanceof SpaceListener)
                    ((SpaceListener) o).notify(key, value);
            }
        }
    }

    public void push(K key, V value) {
        if (key == null || value == null)
            throw new NullPointerException("key=" + key + ", value=" + value);
        synchronized (this) {
            if (isLiveObject(value)) {
                liveObjectSpace.push(key, value);
                Map<String, K> k = new SingletonMap("localkey", key);
                value = (V) k;
            }
            LinkedList<Object> l = entries.get(key);
            if (l == null)
                l = new LinkedList<Object>();
            l.add(value);
            entries.put(0, l);
            this.notifyAll();
            if (sl != null)
                notifyListeners(key, value);
        }
    }

    public void push(K key, V value, long timeout) {
        if (key == null || value == null)
            throw new NullPointerException("key=" + key + ", value=" + value);

        synchronized (this) {
            if (isLiveObject(value)) {
                liveObjectSpace.push(key, value);
                Map<String, K> k = new SingletonMap("localkey", key);
                value = (V) k;
            }
            Object v = value;
            if (timeout > 0) {
                v = new Expirable(value, System.currentTimeMillis() + timeout);
            }
            LinkedList<Object> l = entries.get(key);
            if (l == null)
                l = new LinkedList<Object>();
            l.add(v);
            entries.put(key, l);
            this.notifyAll();
            if (sl != null)
                notifyListeners(key, value);
            if (timeout > 0) {
                registerExpirable(key, timeout);
            }
        }
    }

    public synchronized void put(K key, V value) {
        if (key == null || value == null)
            throw new NullPointerException("key=" + key + ", value=" + value);

        if (isLiveObject(value)) {
            liveObjectSpace.put(key, value);
            Map<String, K> k = new SingletonMap("localkey", key);
            value = (V) k;
        }
        LinkedList<Object> l = new LinkedList<Object>();
        l.add(value);
        entries.put((Object) key, l);
        this.notifyAll();
        if (sl != null)
            notifyListeners(key, value);
    }

    public void put(K key, V value, long timeout) {
        if (key == null || value == null)
            throw new NullPointerException("key=" + key + ", value=" + value);

        synchronized (this) {
            if (isLiveObject(value)) {
                liveObjectSpace.put(key, value);
                Map<String, K> k = new SingletonMap("localkey", key);
                value = (V) k;
            }
            Object v = value;
            if (timeout > 0) {
                v = new Expirable(value, System.currentTimeMillis() + timeout);
            }
            LinkedList<Object> l = new LinkedList<Object>();
            l.add(v);
            entries.put(key, l);
            this.notifyAll();
            if (sl != null)
                notifyListeners(key, value);
            if (timeout > 0) {
                registerExpirable(key, timeout);
            }
        }
    }

    public boolean existAny(K[] keys) {
        for (int i = 0; i < keys.length; i++) {
            if (rdp(keys[i]) != null)
                return true;
        }
        return false;
    }

    public boolean existAny(K[] keys, long timeout) {
        long now = System.currentTimeMillis();
        long end = now + timeout;
        while (((now = System.currentTimeMillis()) < end)) {
            if (existAny(keys))
                return true;
            synchronized (this) {
                try {
                    wait(end - now);
                } catch (InterruptedException e) {
                }
            }
        }
        return false;
    }

    public Map getEntries() {
        return entries;
    }

    public void setEntries(IMap entries) {
        this.entries = entries;
    }

    public boolean isLiveObject(Object obj) {
        if (obj instanceof Serializable || this.serializer.isSuitable((obj))) {
            return false;
        }

        return true;
    }

    private Object getHead(Object key, boolean remove) {
        Object obj = null;
        LinkedList<Object> l = this.entries.get(key);
        boolean wasExpirable = false;
        while (obj == null && l != null && l.size() > 0) {
            obj = l.get(0);
            if (obj == null) {
                l.remove(0);
                if (l.size() == 0) {
                    entries.remove(key);
                }
            }
        }
        if (obj != null && remove) {
            l.remove(0);
            if (l.size() == 0) {
                this.entries.remove(key);
                if (wasExpirable)
                    unregisterExpirable(key);
            }
        }
        this.entries.replace(key, l);

        return obj;
    }

    private Object getObject(Template tmpl, boolean remove) {
        Object obj = null;
        LinkedList<Object> l = this.entries.get(tmpl.getKey());
        if (l == null)
            return obj;

        Iterator iter = l.iterator();
        while (iter.hasNext()) {
            obj = iter.next();
            if (tmpl.equals(obj)) {
                if (remove)
                    iter.remove();
                break;
            } else
                obj = null;
        }
        entries.replace(tmpl.getKey(), l);

        return obj;
    }

    protected class ExpirationService implements Serializable, Runnable {

        public ExpirationService() {
            super();
            cleanupScheduler.scheduleAtFixedRate(this, GCDELAY, GCDELAY, TimeUnit.MILLISECONDS);
        }

        public void run() {
            try {
                gc();
            } catch (Exception e) {
                e.printStackTrace(); // this should never happen
            }
        }

        public void gc() {
            gc(0);
            if (System.currentTimeMillis() - lastLongGC > GCLONG) {
                gc(1);
                lastLongGC = System.currentTimeMillis();
            }
        }

        private void gc(int generation) {
            Set<K> exps = expirables.get(generation);
            synchronized (this) {
                expirables.set(generation, new HashSet<K>());
            }

            for (K k : exps) {
                if (rdp(k) != null) {
                    unregisterExpirable(k);
                    synchronized (this) {
                        expirables.get(generation).add(k);
                    }
                } else {
                    entries.remove(k);
                }
                Thread.yield();
            }
            if (sl != null) {
                synchronized (this) {
                    if (sl != null && sl.isEmpty())
                        sl = null;
                }
            }
        }
    }

    private HzlSpace getSL() {
        synchronized (this) {
            if (sl == null)
                sl = new HzlSpace();
        }
        return sl;
    }

    private void registerExpirable(K k, long t) {
        expirables.get(t > GCLONG ? 1 : 0).add(k);
    }

    private void unregisterExpirable(Object k) {
        for (Set<K> s : expirables)
            s.remove(k);
    }

    static class Expirable implements Comparable, Serializable {
        Object value;
        long expires;

        public Expirable(Object value, long expires) {
            super();
            this.value = value;
            this.expires = expires;
        }

        public boolean isExpired() {
            return expires < System.currentTimeMillis();
        }

        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode()) + ",value=" + value.toString()
                    + ",expired=" + isExpired();
        }

        public Object getValue() {
            return isExpired() ? null : value;
        }

        public int compareTo(Object obj) {
            Expirable other = (Expirable) obj;
            long otherExpires = other.expires;
            if (otherExpires == expires)
                return 0;
            else if (expires < otherExpires)
                return -1;
            else
                return 1;
        }
    }

    public class Fibonacci implements Callable<Long>, Serializable {
        int input = 0;

        public Fibonacci() {
        }

        public Fibonacci(int input) {
            this.input = input;
        }

        public Long call() {
            return calculate(input);
        }

        private long calculate(int n) {
            if (Thread.currentThread().isInterrupted())
                return 0;
            if (n <= 1)
                return n;
            else
                return calculate(n - 1) + calculate(n - 2);
        }
    }
}