    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}