001package com.hammurapi.common.concurrent;
002
003import java.util.Arrays;
004import java.util.HashSet;
005import java.util.Map;
006import java.util.Set;
007import java.util.concurrent.Callable;
008import java.util.concurrent.ExecutorService;
009import java.util.concurrent.Future;
010import java.util.concurrent.ScheduledExecutorService;
011import java.util.concurrent.TimeUnit;
012import java.util.concurrent.locks.ReadWriteLock;
013
014import com.hammurapi.common.Context;
015import com.hammurapi.convert.Converter;
016import com.hammurapi.convert.ConvertingService;
017
018/**
019 * 
020 * @author Pavel Vlasov
021 *
022 * @param <KP> Key path
023 * @param <KE> Key path element
024 */
025public abstract class AbstractPropertySet<KP,KE> implements PropertySet<KP>, ReadWriteLock {
026        
027        protected Converter converter;
028        protected ExecutorService executorService;
029        protected PropertySet<KP>[] shadows;
030//      protected ClassLoader classLoader;
031        protected Context context;
032        
033        private PropertySet<KP> shadowPropertySet = new PropertySet<KP>() {
034
035                @Override
036                public <T> T getAdapter(Class<T> type, Context context) {
037                        throw new UnsupportedOperationException(); // Maybe implement.
038                }
039
040                @Override
041                public Set<KP> keySet() {
042                        HashSet<KP> ret = new HashSet<KP>();
043                        
044                        for (PropertySet<KP> ch: shadows) {
045                                if (ch!=null) {
046                                        ret.addAll(ch.keySet());
047                                }
048                        }
049                        return ret;
050                }
051
052                @Override
053                public PropertySet<KP> subset(KP prefix) {
054                        return new AbstractSubSet<KP>(this, prefix) {
055
056                                @Override
057                                protected KP buildPath(KP prefix, KP key) {
058                                        return AbstractPropertySet.this.buildPath(prefix, key);
059                                }
060                                
061                        };
062                }
063
064                @Override
065                public void remove(KP key) {
066                        throw new UnsupportedOperationException();
067                }
068
069                @Override
070                public void clear() {
071                        throw new UnsupportedOperationException();
072                }
073
074                @Override
075                public void mount(KP prefix, PropertySet<KP> source) {
076                        throw new UnsupportedOperationException();
077                }
078
079                @Override
080                public void unmount(KP prefix) {
081                        throw new UnsupportedOperationException();
082                }
083
084                @Override
085                public void setAll(PropertySet<KP> source) {
086                        throw new UnsupportedOperationException();
087                }
088
089                @Override
090                public Object get(KP key) {
091                        for (PropertySet<KP> ch: shadows) {
092                                if (ch!=null) {
093                                        Object ret = ch.get(key);
094                                        if (ret!=null) {
095                                                return ret;
096                                        }
097                                }
098                        }
099                        return null;
100                }
101
102                @Override
103                public Object get(KP key, Object defaultValue) {
104                        for (PropertySet<KP> ch: shadows) {
105                                if (ch!=null) {
106                                        Object ret = ch.get(key);
107                                        if (ret!=null) {
108                                                return ret;
109                                        }
110                                }
111                        }
112                        return defaultValue;
113                }
114
115                @Override
116                public <T> T get(KP key, Class<T> type) {
117                        for (PropertySet<KP> ch: shadows) {
118                                if (ch!=null) {
119                                        T ret = ch.get(key, type);
120                                        if (ret!=null) {
121                                                return ret;
122                                        }
123                                }
124                        }
125                        return null;
126                }
127
128                @Override
129                public <T> T get(KP key, Class<T> type, T defaultValue) {
130                        for (PropertySet<KP> ch: shadows) {
131                                if (ch!=null) {
132                                        T ret = ch.get(key, type);
133                                        if (ret!=null) {
134                                                return ret;
135                                        }
136                                }
137                        }
138                        return defaultValue;
139                }
140
141                @Override
142                public Object get(KP key, long timeout, TimeUnit unit) {
143                        for (PropertySet<KP> ch: shadows) {
144                                if (ch!=null) {
145                                        Object ret = ch.get(key, timeout, unit);
146                                        if (ret!=null) {
147                                                return ret;
148                                        }
149                                }
150                        }
151                        return null;
152                }
153
154                @Override
155                public Object get(KP key, Object defaultValue, long timeout, TimeUnit unit) {
156                        for (PropertySet<KP> ch: shadows) {
157                                if (ch!=null) {
158                                        Object ret = ch.get(key, timeout, unit);
159                                        if (ret!=null) {
160                                                return ret;
161                                        }
162                                }
163                        }
164                        return defaultValue;
165                }
166
167                @Override
168                public <T> T get(KP key, Class<T> type, long timeout, TimeUnit unit) {
169                        for (PropertySet<KP> ch: shadows) {
170                                if (ch!=null) {
171                                        T ret = ch.get(key, type, timeout, unit);
172                                        if (ret!=null) {
173                                                return ret;
174                                        }
175                                }
176                        }
177                        return null;
178                }
179
180                @Override
181                public <T> T get(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
182                        for (PropertySet<KP> ch: shadows) {
183                                if (ch!=null) {
184                                        T ret = ch.get(key, type, timeout, unit);
185                                        if (ret!=null) {
186                                                return ret;
187                                        }
188                                }
189                        }
190                        return defaultValue;
191                }
192
193                @Override
194                public void set(KP key, Object value) {
195                        throw new UnsupportedOperationException();
196                        
197                }
198
199                @Override
200                public void setInvocable(KP key, Invocable<PropertySet<KP>, ?, KP, ?> invocable) {
201                        throw new UnsupportedOperationException();
202                }
203
204                @Override
205                public void cook(KP key, Callable<?> callable) {
206                        throw new UnsupportedOperationException();
207                }
208
209                @Override
210                public void cook(KP key, Callable<?> callable, long delay, TimeUnit timeUnit) {
211                        throw new UnsupportedOperationException();
212                }
213
214                @Override
215                public void cooking(KP key, Future<?> future) {
216                        throw new UnsupportedOperationException();
217                }
218
219                @Override
220                public void lazy(KP key, Callable<?> callable) {
221                        throw new UnsupportedOperationException();
222                }
223
224                @Override
225                public void cancel(KP key) {
226                        throw new UnsupportedOperationException();
227                }
228
229                @Override
230                public void cancelAll() {
231                        throw new UnsupportedOperationException();
232                }
233
234                @Override
235                public Object invoke(KP key, Object... args) {
236                        throw new UnsupportedOperationException();
237                }
238
239                @Override
240                public Object shadowInvoke(KP key, Object... args) {
241                        throw new UnsupportedOperationException();
242                }
243
244                @Override
245                public Object shadowGet(KP key) {
246                        throw new UnsupportedOperationException();
247                }
248
249                @Override
250                public Object shadowGet(KP key, Object defaultValue) {
251                        throw new UnsupportedOperationException();
252                }
253
254                @Override
255                public <T> T shadowGet(KP key, Class<T> type) {
256                        throw new UnsupportedOperationException();
257                }
258
259                @Override
260                public <T> T shadowGet(KP key, Class<T> type, T defaultValue) {
261                        throw new UnsupportedOperationException();
262                }
263                
264
265                @Override
266                public Object shadowGet(KP key, long timeout, TimeUnit unit) {
267                        throw new UnsupportedOperationException();
268                }
269
270                @Override
271                public Object shadowGet(KP key, Object defaultValue, long timeout, TimeUnit unit) {
272                        throw new UnsupportedOperationException();
273                }
274
275                @Override
276                public <T> T shadowGet(KP key, Class<T> type, long timeout, TimeUnit unit) {
277                        throw new UnsupportedOperationException();
278                }
279
280                @Override
281                public <T> T shadowGet(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
282                        throw new UnsupportedOperationException();
283                }
284
285                @Override
286                public PropertySet<KP> createVersion() {
287                        throw new UnsupportedOperationException();
288                }
289                
290        };
291        
292        /**
293         * @return Store for values.
294         */
295        protected abstract Map<KE, Object> getValueStore();
296        
297        /**
298         * 
299         * @return
300         */
301        protected abstract Map<KE, AbstractPropertySet<KP,KE>> getSubSetStore();
302        
303        /**
304         * Creates a property set which shares lock and chain with this one.
305         * @return
306         */
307        protected abstract AbstractPropertySet<KP,KE> newInstance(KE key);
308        
309        protected abstract Map<KE, PropertySet<KP>> getMounts();
310        
311        protected AbstractPropertySet(
312                        ExecutorService executorService, 
313                        Converter converter,
314//                      ClassLoader classLoader,
315                        Context context,
316                        PropertySet<KP>... shadows) {
317                this.executorService = executorService;
318                this.converter = converter==null ? ConvertingService.CONVERTER : converter;
319//              this.classLoader = classLoader==null ? getClass().getClassLoader() : classLoader;
320                this.context = context;
321                this.shadows = shadows;         
322        }
323        
324        protected abstract KP buildPath(int startIdx, Object... elements);
325
326        protected KP buildPath(Object... elements) {
327                return buildPath(0, elements);
328        }
329        
330        protected abstract KP buildPath(KP prefix, Object... elements);
331        
332        protected abstract KP buildPath(KE prefix, KP suffix);
333        
334        protected abstract KE[] tokenize(KP path);
335        
336        /**
337         * Versions object for forward-only change propagation purposes.
338         * @param <T>
339         * @param source
340         * @return
341         */
342        protected abstract <T> T version(T source);
343        
344        @Override
345        public Set<KP> keySet() {
346                readLock().lock();
347                HashSet<KP> ret = new HashSet<KP>();
348                try {
349                        for (KE key: getValueStore().keySet()) {
350                                ret.add(buildPath(key));
351                        }
352                        
353                        for (Map.Entry<KE, PropertySet<KP>> entry: getMounts().entrySet()) {
354                                for (KP path: entry.getValue().keySet()) {
355                                        ret.add(buildPath(entry.getKey(), path));
356                                }
357                        }
358                        
359                        for (Map.Entry<KE, AbstractPropertySet<KP, KE>> entry: getSubSetStore().entrySet()) {
360                                if (!getMounts().containsKey(entry.getKey())) { // If not shadowed
361                                        for (KP path: entry.getValue().keySet()) {
362                                                ret.add(buildPath(entry.getKey(), path));
363                                        }
364                                }
365                        }                       
366                } finally {
367                        readLock().unlock();
368                }
369                
370                ret.addAll(shadowPropertySet.keySet());
371                return ret;
372        }
373
374        @Override
375        public PropertySet<KP> subset(KP prefix) {
376                writeLock().lock();
377                try {
378                        return subset(tokenize(prefix),0,true);
379                } finally {
380                        writeLock().unlock();
381                }
382        }
383
384        protected PropertySet<KP> subset(KE[] prefix, int idx, boolean create) {
385                PropertySet<KP> mount = getMounts().get(prefix[idx]);
386                if (mount!=null) {
387                        if (prefix.length==idx+1) {
388                                return mount;
389                        }
390                        
391                        return mount.subset(buildPath(idx+1, prefix));
392                }
393                
394                AbstractPropertySet<KP, KE> ret = getSubSetStore().get(prefix[idx]);
395                if (ret==null) {
396                        if (!create) {
397                                return null;
398                        }
399                        ret = newInstance(prefix[idx]);
400                        getSubSetStore().put(prefix[idx], ret);
401                }
402                
403                if (prefix.length==idx+1) {
404                        return ret;
405                }
406                
407                return ret.subset(prefix, idx+1, create);
408        }
409        
410        @Override
411        public void remove(KP key) {
412                writeLock().lock();
413                try {
414                        remove(tokenize(key), 0);
415                } finally {
416                        writeLock().unlock();
417                }
418        }
419
420        protected void remove(KE[] path, int idx) {
421                if (path.length==idx+1) {
422                        getValueStore().remove(path[idx]);
423                } else {                        
424                        PropertySet<KP> mount = getMounts().get(path[idx]);
425                        if (mount!=null) {
426                                mount.remove(buildPath(idx+1, path));
427                        } else {                        
428                                AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
429                                if (ss!=null) {
430                                        ss.remove(path, idx+1);
431                                }
432                        }
433                }
434        }
435        
436        @Override
437        public void clear() {
438                writeLock().lock();
439                try {
440                        getValueStore().clear();
441                        for (PropertySet<KP> m: getMounts().values()) {
442                                m.clear();
443                        }
444                        for (Map.Entry<KE, AbstractPropertySet<KP, KE>> e: getSubSetStore().entrySet()) {
445                                if (!getMounts().containsKey(e.getKey())) {
446                                        e.getValue().clear();
447                                }
448                        }
449                        for (int i=0; i<shadows.length; ++i) {
450                                shadows[i]=null;
451                        }
452                } finally {
453                        writeLock().unlock();
454                }
455        }
456
457        @Override
458        public void mount(KP prefix, PropertySet<KP> source) {
459                writeLock().lock();
460                try {
461                        mount(tokenize(prefix), 0, source);
462                } finally {
463                        writeLock().unlock();
464                }
465        }
466
467        protected void mount(KE[] path, int idx, PropertySet<KP> source) {
468                if (path.length==idx+1) {
469                        getMounts().put(path[idx], source);
470                } else {                        
471                        AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
472                        if (ss==null) {
473                                ss = newInstance(path[idx]);
474                                getSubSetStore().put(path[idx], ss);
475                        }
476                        
477                        ss.mount(path, idx+1, source);
478                }
479        }
480        
481        @Override
482        public void unmount(KP prefix) {
483                writeLock().lock();
484                try {
485                        unmount(tokenize(prefix), 0);
486                } finally {
487                        writeLock().unlock();
488                }
489        }
490        
491        protected void unmount(KE[] path, int idx) {
492                if (path.length==idx+1) {
493                        getMounts().remove(path[idx]);
494                } else {                        
495                        AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
496                        if (ss!=null) {
497                                ss.unmount(path, idx+1);
498                        }
499                }
500        }
501        
502        @Override
503        public void setAll(PropertySet<KP> source) {
504                writeLock().lock();
505                try {
506                        for (KP path: source.keySet()) {
507                                set(path, source.get(path));
508                        }
509                } finally {
510                        writeLock().unlock();
511                }
512        }
513
514        @Override
515        public Object get(KP key) {
516                Object ret;
517                Object[] va = {null};
518                readLock().lock();
519                try {
520                        ret = get(tokenize(key), 0, va);
521                } finally {
522                        readLock().unlock();
523                }
524                
525                // Chained version
526                if (va[0]!=null) {
527                        set(key, va[0]);
528                }
529                
530                return ret;
531        }
532        
533        @Override
534        public Object get(KP key, long timeout, TimeUnit unit) {
535                Object ret;
536                Object[] va = {null};
537                readLock().lock();
538                try {
539                        ret = get(tokenize(key), 0, va, timeout, unit);
540                } finally {
541                        readLock().unlock();
542                }
543                
544                // Chained version
545                if (va[0]!=null) {
546                        set(key, va[0]);
547                }
548                
549                return ret;
550        }
551        
552        private static class CookEntry {
553                
554                CookEntry(Future<?> future, Callable<?> callable) {
555                        this.future = future;
556                        this.callable = callable;
557                }
558                
559                Future<?> future;
560                Callable<?> callable;
561                Object value;
562                
563                synchronized Object get() throws Exception {
564                        if (future!=null) {
565                                value = future.get();   
566                                future = null;
567                        } else if (callable!=null) {
568                                value = callable.call();
569                                callable = null;
570                        }
571                        return value;
572                }
573                
574                synchronized Object get(long timeout, TimeUnit unit) throws Exception {
575                        if (future!=null) {
576                                value = future.get(timeout, unit);      
577                                future = null;
578                        } else if (callable!=null) {
579                                value = callable.call();
580                                callable = null;
581                        }
582                        return value;
583                }
584                
585                void cancel() {
586                        if (future!=null && callable!=null && !future.isDone()) {
587                                future.cancel(false);
588                                future = null;                          
589                        }
590                }
591                
592        }
593
594        protected Object get(KE[] path, int idx, Object[] va) {
595                if (path.length==idx+1) {
596                        if (getValueStore().containsKey(path[idx])) {
597                                Object ret = getValueStore().get(path[idx]);
598                                
599                                if (ret instanceof Invocable) {
600                                        Invocable<PropertySet<KP>,?, KP, ?> i = (Invocable<PropertySet<KP>,?, KP, ?>) ret;
601                                        if (i.getParameterTypes()==null || i.getParameterTypes().length==0) {
602                                                InvocationImpl<KP> invocation = new InvocationImpl<KP>();
603                                                invocation.setPropertySet(this);
604                                                try {
605                                                        return i.invoke(this, invocation, executorService);
606                                                } catch (Exception e) {
607                                                        throw new PropertySetException(e);
608                                                }
609                                        }
610                                        return i;
611                                }
612                                
613                                if (ret instanceof CookEntry) {
614                                        try {
615                                                ret = ((CookEntry) ret).get();
616                                                va[0] = ret; // To replace cook entry with value
617                                                return ret;
618                                        } catch (Exception e) {
619                                                throw new PropertySetException(e);
620                                        }
621                                }
622                                                        
623                                return ret;
624                        }
625                } else {                
626                        PropertySet<KP> mount = getMounts().get(path[idx]);
627                        if (mount!=null) {
628                                return mount.get(buildPath(idx+1, path));
629                        }
630                        
631                        AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
632                        if (ss!=null) {
633                                Object ret = ss.get(path, idx+1, va);
634                                if (ret!=null) {
635                                        return ret;
636                                }
637                        }
638                }
639                
640                va[0] = version(shadowGet(buildPath(idx, path)));
641                return va[0];
642        }
643        
644        protected Object get(KE[] path, int idx, Object[] va, long timeout, TimeUnit unit) {
645                if (path.length==idx+1) {
646                        if (getValueStore().containsKey(path[idx])) {
647                                Object ret = getValueStore().get(path[idx]);
648                                
649                                if (ret instanceof Invocable) {
650                                        Invocable<PropertySet<KP>,?, KP, ?> i = (Invocable<PropertySet<KP>,?, KP, ?>) ret;
651                                        if (i.getParameterTypes()==null || i.getParameterTypes().length==0) {
652                                                InvocationImpl<KP> invocation = new InvocationImpl<KP>();
653                                                invocation.setPropertySet(this);
654                                                try {
655                                                        return i.invoke(this, invocation, executorService);
656                                                } catch (Exception e) {
657                                                        throw new PropertySetException(e);
658                                                }
659                                        }
660                                        return i;
661                                }
662                                
663                                if (ret instanceof CookEntry) {
664                                        try {
665                                                ret = ((CookEntry) ret).get(timeout, unit);
666                                                va[0] = ret; // To replace cook entry with value
667                                                return ret;
668                                        } catch (Exception e) {
669                                                throw new PropertySetException(e);
670                                        }
671                                }
672                                                        
673                                return ret;
674                        }
675                } else {                
676                        PropertySet<KP> mount = getMounts().get(path[idx]);
677                        if (mount!=null) {
678                                return mount.get(buildPath(idx+1, path), timeout, unit);
679                        }
680                        
681                        AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
682                        if (ss!=null) {
683                                Object ret = ss.get(path, idx+1, va, timeout, unit);
684                                if (ret!=null) {
685                                        return ret;
686                                }
687                        }
688                }
689                
690                va[0] = version(shadowGet(buildPath(idx, path), timeout, unit));
691                return va[0];
692        }
693        
694        @Override
695        public Object get(KP key, Object defaultValue) {
696                Object ret = get(key);
697                return ret == null ? defaultValue : ret;
698        }
699
700        @Override
701        public Object get(KP key, Object defaultValue, long timeout, TimeUnit unit) {
702                Object ret = get(key, timeout, unit);
703                return ret == null ? defaultValue : ret;
704        }
705
706        @Override
707        public <T> T get(KP key, Class<T> type) {
708                return get(key, type, null);
709        }
710
711        @Override
712        public <T> T get(KP key, Class<T> type, long timeout, TimeUnit unit) {
713                return get(key, type, null, timeout, unit);
714        }
715
716        @Override
717        public <T> T get(KP key, Class<T> type, T defaultValue) {
718                Object ret = get(key);
719                if (ret==null) {
720                        if (type!=null && type.isInterface()) {
721                                PropertySet<KP> toWrap = subset(tokenize(key), 0, false);
722                                if (toWrap!=null) {
723                                        return toWrap.getAdapter(type, null);
724                                }
725                        }
726                        return defaultValue;
727                }
728                if (type.isInstance(ret)) {
729                        return (T) ret;
730                }
731                T converted = converter.convert(ret, type, context);
732                if (converted==null) {
733                        throw new PropertySetException("Cannot convert "+ ret +" to "+type);
734                }
735                return converted;
736        }
737
738        @Override
739        public <T> T get(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
740                Object ret = get(key, timeout, unit);
741                if (ret==null) {
742                        if (type!=null && type.isInterface()) {
743                                PropertySet<KP> toWrap = subset(tokenize(key), 0, false);
744                                if (toWrap!=null) {
745                                        return toWrap.getAdapter(type, null);
746                                }
747                        }
748                        return defaultValue;
749                }
750                if (type.isInstance(ret)) {
751                        return (T) ret;
752                }
753                T converted = converter.convert(ret, type, context);
754                if (converted==null) {
755                        throw new PropertySetException("Cannot convert "+ ret +" to "+type);
756                }
757                return converted;
758        }
759
760        @Override
761        public void set(KP key, Object value) {
762                writeLock().lock();
763                try {
764                        set(tokenize(key), 0, value);
765                } finally {
766                        writeLock().unlock();
767                }
768        }
769
770        protected void set(KE[] path, int idx, Object value) {
771                if (path.length==idx+1) {
772                        getValueStore().put(path[idx], value);
773                } else {
774                        PropertySet<KP> mount = getMounts().get(path[idx]);
775                        if (mount!=null) {
776                                mount.set(buildPath(idx+1, path), value);
777                        } else {                                
778                                AbstractPropertySet<KP, KE> ret = getSubSetStore().get(path[idx]);
779                                if (ret==null) {
780                                        ret = newInstance(path[idx]);
781                                        getSubSetStore().put(path[idx], ret);
782                                }
783                                
784                                ret.set(path, idx+1, value);
785                        }
786                }
787        }
788        
789        @Override
790        public void setInvocable(KP key, Invocable<PropertySet<KP>, ?, KP, ?> invocable) {
791                set(key, invocable);
792        }
793
794        @Override
795        public void cook(KP key, Callable<?> callable) {
796                cook(key, callable, 0, null);
797        }
798
799        protected void cook(KE[] path, int idx, Callable<?> callable, long delay, TimeUnit timeUnit) {
800                if (path.length==idx+1) {
801                        Future<?> future = null;
802                        if (delay>0 && timeUnit!=null && executorService instanceof ScheduledExecutorService) {
803                                future = ((ScheduledExecutorService) executorService).schedule(callable, delay, timeUnit);
804                        } else if (executorService!=null) {
805                                future = executorService.submit(callable);
806                        }
807                        getValueStore().put(path[idx], new CookEntry(future, callable));
808                } else {
809                        PropertySet<KP> mount = getMounts().get(path[idx]);
810                        if (mount!=null) {
811                                mount.cook(buildPath(idx+1, path), callable, delay, timeUnit);
812                        }
813                        
814                        AbstractPropertySet<KP, KE> ret = getSubSetStore().get(path[idx]);
815                        if (ret==null) {
816                                ret = newInstance(path[idx]);
817                                getSubSetStore().put(path[idx], ret);
818                        }
819                        
820                        ret.cook(path, idx+1, callable, delay, timeUnit);
821                }
822        }
823
824        @Override
825        public void cook(KP key, Callable<?> callable, long delay, TimeUnit timeUnit) {
826                writeLock().lock();
827                try {
828                        cook(tokenize(key), 0, callable, delay, timeUnit);
829                } finally {
830                        writeLock().unlock();
831                }
832        }
833
834        @Override
835        public void cooking(KP key, Future<?> future) {
836                set(key, new CookEntry(future, null));
837        }
838
839        @Override
840        public void lazy(KP key, Callable<?> callable) {
841                set(key, new CookEntry(null, callable));
842        }
843
844        @Override
845        public void cancel(KP key) {
846                readLock().lock();
847                try {
848                        cancel(tokenize(key),0);
849                } finally {
850                        readLock().unlock();
851                }
852        }
853
854        protected void cancel(KE[] path, int idx) {
855                if (path.length==idx+1) {
856                        Object v = getValueStore().get(path[idx]);
857                        if (v instanceof CookEntry) {
858                                ((CookEntry) v).cancel();
859                        }
860                } else {
861                        PropertySet<KP> mount = getMounts().get(path[idx]);
862                        if (mount!=null) {
863                                mount.cancel(buildPath(idx+1, path));
864                        }
865                        
866                        AbstractPropertySet<KP, KE> ret = getSubSetStore().get(path[idx]);
867                        if (ret!=null) {
868                                ret.cancel(path, idx+1);
869                        }
870                }
871        }
872        
873        @Override
874        public void cancelAll() {
875                readLock().lock();
876                try {
877                        for (PropertySet<KP> mount: getMounts().values()) {
878                                mount.cancelAll();
879                        }
880                        for (PropertySet<KP> ss: getSubSetStore().values()) {
881                                ss.cancelAll();
882                        }
883                        for (Object v: getValueStore().values()) {
884                                if (v instanceof CookEntry) {
885                                        ((CookEntry) v).cancel();
886                                }                               
887                        }
888                } finally {
889                        readLock().unlock();
890                }
891        }
892
893        @Override
894        public Object invoke(KP key, Object... args) {
895                return invoke(this, key, args);
896        }
897        
898        private Object invoke(PropertySet<KP> contextPropertySet, KP key, Object... args) {
899                writeLock().lock(); // To avoid deadlocks if invocable has side effects.
900                try {
901                        Object i = contextPropertySet.get(key);
902                        if (i instanceof Invocable) {
903                                Invocable invocable = (Invocable) i;
904                                Class[] parameterTypes = invocable.getParameterTypes();
905                                if (args!=null && parameterTypes!=null) {
906                                        if (parameterTypes.length!=args.length) {
907                                                throw new PropertySetException("Cannot invoke "+key+" - invalid number of arguments");
908                                        }
909                                        args = Arrays.copyOf(args, args.length);
910                                        for (int j=0; j<args.length; ++j) {
911                                                if (args[j]!=null && !parameterTypes[j].isInstance(args[j])) {
912                                                        Object newVal = converter.convert(args[j], parameterTypes[j], context);
913                                                        if (args[j]==null) {
914                                                                throw new PropertySetException("Cannot covert "+args[j]+" to "+parameterTypes[j]);
915                                                        }
916                                                        args[j] = newVal;
917                                                }
918                                        }
919                                }
920                                InvocationImpl invocation = new InvocationImpl(args, contextPropertySet, null);
921                                try {
922                                        return invocable.invoke(contextPropertySet, invocation, executorService);
923                                } catch (Exception e) {
924                                        throw new PropertySetException(e);
925                                }
926                        }
927                        
928                        throw new PropertySetException("Not invocable: "+i);
929                } finally {
930                        writeLock().unlock();
931                }
932        }
933
934        @Override
935        public Object shadowInvoke(KP key, Object... args) {
936                return invoke(shadowPropertySet, key, args);
937        }
938
939        @Override
940        public Object shadowGet(KP key) {
941                return shadowPropertySet.get(key);
942        }
943
944        @Override
945        public Object shadowGet(KP key, Object defaultValue) {
946                return shadowPropertySet.get(key, defaultValue);
947        }
948
949        @Override
950        public <T> T shadowGet(KP key, Class<T> type) {
951                return shadowPropertySet.get(key, type);
952        }
953
954        @Override
955        public <T> T shadowGet(KP key, Class<T> type, T defaultValue) {
956                return shadowPropertySet.get(key, type, defaultValue);
957        }
958        
959        @Override
960        public Object shadowGet(KP key, long timeout, TimeUnit unit) {
961                return shadowPropertySet.get(key, timeout, unit);
962        }
963
964        @Override
965        public Object shadowGet(KP key, Object defaultValue, long timeout, TimeUnit unit) {
966                return shadowPropertySet.get(key, defaultValue, timeout, unit);
967        }
968
969        @Override
970        public <T> T shadowGet(KP key, Class<T> type, long timeout, TimeUnit unit) {
971                return shadowPropertySet.get(key, type, timeout, unit);
972        }
973
974        @Override
975        public <T> T shadowGet(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
976                return shadowPropertySet.get(key, type, defaultValue, timeout, unit);
977        }
978        
979        
980}