001package gu.sql2java;
002
003import java.util.LinkedHashSet;
004import java.util.LinkedList;
005import gu.sql2java.exception.RuntimeDaoException;
006
007import static com.google.common.base.Preconditions.*;
008import static gu.sql2java.SimpleLog.*;
009
010/** 
011 * container for multiple listener management
012 * @author guyadong 
013 */
014public class ListenerContainer <B> implements TableListener<B> {
015        private final LinkedHashSet<TableListener<B>> listeners = new LinkedHashSet<TableListener<B>>(16);
016
017        static final TransactionListenerImpl TRANSACTION_LISTENER = new TransactionListenerImpl();
018        public ListenerContainer() {
019        }
020
021        @Override
022        public void beforeInsert(B bean)throws RuntimeDaoException{
023                synchronized (listeners) {
024                        for(TableListener<B> listener:listeners){
025                                try{
026                                        listener.beforeInsert(bean);
027                                }catch(Exception e){
028                                        log("beforeInsert listener %s error:%s",listener.getClass().getName(),e.getMessage());
029                                }
030                        }
031                }
032        }
033
034        @Override
035        public void afterInsert(final B bean)throws RuntimeDaoException{
036                synchronized (listeners) {
037                        for(final TableListener<B> listener:listeners){
038                                TRANSACTION_LISTENER.runCommitTask(new Runnable() {
039
040                                        @Override
041                                        public void run() {
042                                                try{
043                                                        listener.afterInsert(bean);
044                                                }catch(Exception e){
045                                                        log("afterInsert listener %s error:%s",listener.getClass().getName(),e.getMessage());
046                                                        //log(e);
047                                                }
048                                        }
049                                });
050                        }
051                }
052        }
053
054        @Override
055        public void beforeUpdate(B bean)throws RuntimeDaoException{
056                synchronized (listeners) {
057                        for(TableListener<B> listener:listeners){
058                                try{
059                                        listener.beforeUpdate(bean);
060                                }catch(Exception e){
061                                        log("beforeUpdate listener %s error:%s",listener.getClass().getName(),e.getMessage());
062                                }
063                        }
064                }
065        }
066
067        @Override
068        public void afterUpdate(final B bean)throws RuntimeDaoException{
069                synchronized (listeners) {
070                        for(final TableListener<B> listener:listeners){
071                                TRANSACTION_LISTENER.runCommitTask(new Runnable() {
072
073                                        @Override
074                                        public void run() {
075                                                try{
076                                                        listener.afterUpdate(bean);
077                                                }catch(Exception e){
078                                                        log("afterUpdate listener %s error:%s",listener.getClass().getName(),e.getMessage());
079                                                        //log(e);
080                                                }
081                                        }
082                                });
083                        }
084                }
085        }
086
087        @Override
088        public void beforeDelete(B bean)throws RuntimeDaoException{
089                synchronized (listeners) {
090                        for(TableListener<B> listener:listeners){
091                                try{
092                                        listener.beforeDelete(bean);
093                                }catch(Exception e){
094                                        log("beforeDelete listener %s error:%s",listener.getClass().getName(),e.getMessage());
095                                }
096                        }
097                }
098        }
099
100        @Override
101        public void afterDelete(final B bean)throws RuntimeDaoException{
102                synchronized (listeners) {
103                        for(final TableListener<B> listener:listeners){
104                                TRANSACTION_LISTENER.runCommitTask(new Runnable() {
105
106                                        @Override
107                                        public void run() {
108                                                try{
109                                                        listener.afterDelete(bean);
110                                                }catch(Exception e){
111                                                        log("afterDelete listener %s error:%s",listener.getClass().getName(),e.getMessage());
112                                                }
113                                        }
114                                });
115
116                        }
117                }
118        }
119
120        @Override
121        public void done()throws RuntimeDaoException{
122                synchronized (listeners) {
123                        for(final TableListener<B> listener:listeners){
124                                TRANSACTION_LISTENER.runDoneTask(new Runnable() {
125
126                                        @Override
127                                        public void run() {
128                                                try{
129                                                        listener.done();
130                                                }catch(Exception e){
131                                                        log("done listener %s error:%s",listener.getClass().getName(),e.getMessage());
132                                                }
133                                        }
134                                });
135
136                        }
137                }
138        }
139        /**
140         * determine if the container is empty.
141         * @return 
142         */
143        public boolean isEmpty() {
144                return listeners.isEmpty();
145        }
146        /**
147         * determine if the {@code listener} be added.
148         * @param listener
149         * @return {@code true} if {@code listener} exists in container
150         */
151        public boolean contains(TableListener<B> listener) {
152                synchronized (listeners) {
153                        return listeners.contains(listener);
154                }
155        }
156        /**
157         * add {@code listener} into container
158         * @return {@code true} if add successfully.
159         */
160        public boolean add(TableListener<B> listener) {
161                synchronized (listeners) {
162                        return null == listener ? false : listeners.add(listener);
163                }
164        }
165        /**
166         * remove {@code listener} from container
167         * @param listener instance that will be removed.
168         * @return {@code true} if remove successfully.
169         */
170        public boolean remove(TableListener<B> listener) {
171                synchronized (listeners) {
172                        return null == listener? false : listeners.remove(listener);
173                }
174        }
175        /** remove all listeners in container */
176        public void clear() {
177                synchronized (listeners) {
178                        listeners.clear();
179                }
180        }
181        private static class TransactionListenerImpl implements TransactionListener{
182                private static final InheritableThreadLocal<LinkedList<Runnable>> commitTasks = new InheritableThreadLocal<>();
183                private static final InheritableThreadLocal<LinkedList<Runnable>> doneTasks = new InheritableThreadLocal<>();
184                @Override
185                public void onBegin() {
186                        commitTasks.set(new LinkedList<Runnable>());
187                        doneTasks.set(new LinkedList<Runnable>());
188                }
189                @Override
190                public void onCommit() {
191                        // run all commit tasks
192                        LinkedList<Runnable> tasks = commitTasks.get();
193                        checkState(null != tasks,"'onBegin' must be called firstly");
194                        for (Runnable task : tasks) {
195                                task.run();
196                        }
197                }
198                @Override
199                public void onEnd() {
200                        // run all done tasks
201                        LinkedList<Runnable> tasks = doneTasks.get();
202                        checkState(null != tasks,"'onBegin' must be called firstly");
203                        for (Runnable task : tasks) {
204                                task.run();
205                        }
206                        commitTasks.remove();
207                        doneTasks.remove();
208                }
209                void runCommitTask(Runnable task){
210                        LinkedList<Runnable> tasks = commitTasks.get();
211                        if(tasks != null){
212                                tasks.add(task);
213                        }else {
214                                task.run();
215                        }
216                }
217                void runDoneTask(Runnable task){
218                        LinkedList<Runnable> tasks = doneTasks.get();
219                        if(tasks != null){
220                                tasks.add(task);
221                        }else {
222                                task.run();
223                        }
224                }
225        }
226}