001package org.cache2k.impl;
002
003/*
004 * #%L
005 * cache2k core package
006 * %%
007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the 
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public 
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import org.cache2k.BulkCacheSource;
026import org.cache2k.ExperimentalBulkCacheSource;
027import org.cache2k.Cache;
028import org.cache2k.CacheBuilder;
029import org.cache2k.CacheConfig;
030import org.cache2k.CacheManager;
031import org.cache2k.CacheSource;
032import org.cache2k.CacheSourceWithMetaInfo;
033import org.cache2k.RefreshController;
034
035import java.lang.reflect.Constructor;
036import java.lang.reflect.Method;
037
038/**
039 * @author Jens Wilke; created: 2013-12-06
040 */
041@SuppressWarnings("unused") // instantiated by reflection from cache builder
042public class CacheBuilderImpl<K, T> extends CacheBuilder<K, T> {
043
044  String deriveNameFromStackTrace() {
045    Exception ex = new Exception();
046    for (StackTraceElement e : ex.getStackTrace()) {
047      if (!e.getClassName().startsWith(this.getClass().getPackage().getName())) {
048        int idx = e.getClassName().lastIndexOf('.');
049        String _simpleClassName = e.getClassName().substring(idx + 1);
050        String _methodName = e.getMethodName();
051        if (_methodName.equals("<init>")) {
052          _methodName = "INIT";
053        }
054        if (_methodName != null && _methodName.length() > 0) {
055          return _simpleClassName + "." + _methodName + "" + "." + e.getLineNumber();
056        }
057      }
058    }
059    return null;
060  }
061
062  Object getConstructorParameter(Class<?> c) {
063    if (CacheConfig.class.isAssignableFrom(c)) { return config; }
064    if (RefreshController.class.isAssignableFrom(c)) { return refreshController; }
065    if (CacheSource.class.isAssignableFrom(c)) { return cacheSource; }
066    if (CacheSourceWithMetaInfo.class.isAssignableFrom(c)) { return cacheSourceWithMetaInfo; }
067    if (ExperimentalBulkCacheSource.class.isAssignableFrom(c)) { return experimentalBulkCacheSource; }
068    if (BulkCacheSource.class.isAssignableFrom(c)) { return bulkCacheSource; }
069    return null;
070  }
071
072  /** return the first constructor with CacheConfig as first parameter */
073  Constructor<?> findConstructor(Class<?> c) {
074    for (Constructor ctr : c.getConstructors()) {
075      Class<?>[] pt = ctr.getParameterTypes();
076      if (pt != null && pt.length > 0 && CacheConfig.class.isAssignableFrom(pt[0])) {
077        return ctr;
078      }
079    }
080    return null;
081  }
082
083  /**
084   * The generic wiring code is not working on android.
085   * Explicitly call the wiring methods.
086   */
087  @SuppressWarnings("unchecked")
088  void confiugreViaSettersDirect(BaseCache c) {
089    if (cacheSource != null) {
090      c.setSource(cacheSource);
091    }
092    if (cacheSourceWithMetaInfo != null) {
093      c.setSource(cacheSourceWithMetaInfo);
094    }
095    if (refreshController != null) {
096      c.setRefreshController(refreshController);
097    }
098    if (entryExpiryCalculator != null) {
099      c.setEntryExpiryCalculator(entryExpiryCalculator);
100    }
101    if (exceptionExpiryCalculator != null) {
102      c.setExceptionExpiryCalculator(exceptionExpiryCalculator);
103    }
104
105    if (config != null) {
106      c.setCacheConfig(config);
107    }
108    if (bulkCacheSource != null) {
109      c.setBulkCacheSource(bulkCacheSource);
110    }
111    if (experimentalBulkCacheSource != null) {
112      c.setExperimentalBulkCacheSource(experimentalBulkCacheSource);
113    }
114  }
115
116  void configureViaSetters(Object o) {
117    if (o instanceof BaseCache) {
118      confiugreViaSettersDirect((BaseCache) o);
119      return;
120    }
121    try {
122      for (Method m : o.getClass().getMethods()) {
123        Class<?>[] ps = m.getParameterTypes();
124        if (ps != null && ps.length == 1 && m.getName().startsWith(("set"))) {
125          Object p = getConstructorParameter(ps[0]);
126          if (p != null) {
127            m.invoke(o, p);
128          }
129        }
130      }
131    } catch (Exception ex) {
132      throw new IllegalArgumentException("Unable to configure cache", ex);
133    }
134  }
135
136  protected Cache<K,T> constructImplementationAndFillParameters(Class<?> cls) {
137    if (!Cache.class.isAssignableFrom(cls)) {
138      throw new IllegalArgumentException("Specified impl not a cache" + cls.getName());
139    }
140    try {
141      Cache<K, T> _cache;
142      Constructor<?> ctr = findConstructor(cls);
143      if (ctr != null) {
144        Class<?>[] pt = ctr.getParameterTypes();
145        Object[] _args = new Object[pt.length];
146        for (int i = 0; i < _args.length; i++) {
147          _args[i] = getConstructorParameter(pt[i]);
148        }
149        _cache = (Cache<K, T>) ctr.newInstance(_args);
150      } else {
151        _cache = (Cache<K, T>) cls.newInstance();
152      }
153      return _cache;
154    } catch (Exception e) {
155      throw new IllegalArgumentException("Not able to instantiate cache implementation", e);
156    }
157  }
158
159  public Cache<K, T> build() {
160    config = createConfiguration();
161    if (config.getName() == null) {
162      config.setName(deriveNameFromStackTrace());
163    }
164    Class<?> _implClass = BaseCache.TUNABLE.defaultImplementation;
165    if (config.getImplementation() != null) {
166      _implClass = config.getImplementation();
167    }
168    Cache<K,T> _cache = constructImplementationAndFillParameters(_implClass);
169    CacheManagerImpl cm = null;
170    if (_cache instanceof BaseCache) {
171      cm = (CacheManagerImpl) CacheManager.getInstance();
172      ((BaseCache) _cache).setCacheManager(cm);
173    }
174    configureViaSetters(_cache);
175    if (cm != null) {
176      cm.newCache(_cache);
177    }
178    if (_cache instanceof BaseCache) {
179      ((BaseCache) _cache).init();
180    }
181    return _cache;
182  }
183
184}