001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.tide.util;
023
024import java.lang.reflect.Method;
025import java.util.ArrayList;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031import org.granite.logging.Logger;
032import org.granite.tide.invocation.ContextEvent;
033
034/**
035 * @author Franck WOLFF
036 */
037public abstract class AbstractContext extends HashMap<String, Object> {
038
039        private static final long serialVersionUID = 1L;
040        private static final Logger log = Logger.getLogger(AbstractContext.class);
041        private static final ThreadLocal<AbstractContext> contexts = new ThreadLocal<AbstractContext>();
042        
043        private final Map<String, Set<Method>> observers;
044        private final List<ContextEvent> remoteEvents;
045        
046        protected AbstractContext(Map<String, Set<Method>> observers) {
047                synchronized (contexts) {
048                        if (contexts.get() != null)
049                                throw new IllegalStateException("Context already created");
050                        
051                        this.observers = observers;
052                        this.remoteEvents = new ArrayList<ContextEvent>();
053                        
054                        contexts.set(this);
055                }
056        }
057        
058        public List<ContextEvent> getRemoteEvents() {
059                return remoteEvents;
060        }
061        
062        protected abstract Set<String> getRemoteObservers();
063        protected abstract Object callMethod(Method method, Object... args) throws Exception;
064        
065        public static AbstractContext instance() {
066                return contexts.get();
067        }
068        
069        public static void raiseEvent(String name, Object... args) {
070                AbstractContext instance = instance();
071                Map<String, Set<Method>> observers = instance.observers;
072                if (observers.containsKey(name)) {
073                        for (Method method : observers.get(name)) {
074                                try {
075                                        instance.callMethod(method, args);
076                                }
077                                catch (Exception e) {
078                                        log.error(e, "Could not call method: %s", method);
079                                }
080                        }
081                }
082                Set<String> remoteObservers = instance.getRemoteObservers();
083                if (remoteObservers.contains(name))
084                        instance.remoteEvents.add(new ContextEvent(name, args));
085        }
086        
087        public static void remove() {
088                AbstractContext context = contexts.get();
089                if (context != null) {
090                        try {
091                                context.clear();
092                                context.remoteEvents.clear();
093                        }
094                        finally {
095                                contexts.remove();
096                        }
097                }
098        }
099}