001package org.cache2k.impl.util;
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.impl.CacheInternalError;
026
027import java.io.IOException;
028import java.io.InputStream;
029import java.lang.reflect.Field;
030import java.util.HashMap;
031import java.util.Map;
032import java.util.Properties;
033
034/**
035 * Provides an instance of a tunable after applying changes taken
036 * from configuration file or system properties.
037 *
038 * @see TunableConstants
039 * @author Jens Wilke; created: 2014-04-27
040 */
041public final class TunableFactory {
042
043  static Log log = Log.getLog(TunableFactory.class);
044
045  public final static String DEFAULT_TUNING_FILE_NAME =
046    "/org/cache2k/default-tuning.properties";
047
048  public final static String CUSTOM_TUNING_FILE_NAME =
049    "/org/cache2k/tuning.properties";
050
051  public final static String TUNE_MARKER = "org.cache2k.tuning";
052
053  private static Map<Class<?>, Object> map;
054
055  private static Properties defaultProperties;
056
057  private static Properties customProperties;
058
059  /**
060   * Reload the tunable configuration from the system properties
061   * and the configuration file.
062   */
063  public static synchronized void reload() {
064    map = new HashMap<Class<?>, Object>();
065    customProperties = loadFile(CUSTOM_TUNING_FILE_NAME);
066    defaultProperties = loadFile(DEFAULT_TUNING_FILE_NAME);
067  }
068
069  static Properties loadFile(final String _fileName) {
070    InputStream in =
071      TunableConstants.class.getResourceAsStream(_fileName);
072    if (in != null) {
073      try {
074        Properties p = new Properties();
075        p.load(in);
076        in.close();
077        return p;
078      } catch (IOException ex) {
079        throw new CacheInternalError("tuning properties not readable", ex);
080      }
081    }
082    return null;
083  }
084
085  public synchronized static <T extends TunableConstants> T get(Properties p, Class<T> c) {
086    T cfg = getDefault(c);
087    if (p != null
088      && p.containsKey(TUNE_MARKER)
089      && p.containsKey(cfg.getClass().getName() + ".tuning")) {
090      cfg = (T) cfg.clone();
091      apply(p, cfg);
092    }
093    return cfg;
094  }
095
096  public synchronized static <T extends TunableConstants> T get(Class<T> c) {
097    return getDefault(c);
098  }
099
100  private static <T extends TunableConstants> T getDefault(Class<T> c) {
101    if (map == null) {
102      reload();
103    }
104    @SuppressWarnings("unchecked")
105    T cfg = (T) map.get(c);
106    if (cfg == null) {
107      try {
108        cfg = c.newInstance();
109      } catch (Exception ex) {
110        throw new CacheInternalError("cannot instantiate tunables", ex);
111      }
112      apply(defaultProperties, cfg);
113      apply(customProperties, cfg);
114      apply(System.getProperties(), cfg);
115      map.put(c, cfg);
116    }
117    return cfg;
118  }
119
120  static void apply(Properties p, Object cfg) {
121    if (p == null) {
122      return;
123    }
124    String _propName = null;
125    try {
126      for (Field f : cfg.getClass().getFields()) {
127        _propName =
128          cfg.getClass().getName().replace('$', '.') + "." + f.getName();
129        String o = p.getProperty(_propName);
130        if (o != null) {
131          if (f.getType() == Boolean.TYPE) {
132            o = o.toLowerCase();
133            if (
134              "off".equals(o) ||
135              "false".equals(o) ||
136              "disable".equals(o)) {
137              f.set(cfg, false);
138            } else {
139              f.set(cfg, true);
140            }
141            if (log.isDebugEnabled()) {
142              log.debug(_propName + "=" + f.get(cfg));
143            }
144          } else if (f.getType() == Integer.TYPE) {
145            f.set(cfg, Integer.valueOf(o));
146            if (log.isDebugEnabled()) {
147              log.debug(_propName + "=" + f.get(cfg));
148            }
149          } else if (f.getType() == String.class) {
150            f.set(cfg, o);
151            if (log.isDebugEnabled()) {
152              log.debug(_propName + "=" + f.get(cfg));
153            }
154          } else if (f.getType() == Class.class) {
155            Class<?> c = Class.forName(o);
156            f.set(cfg, c);
157            if (log.isDebugEnabled()) {
158              log.debug(_propName + "=" + c.getName());
159            }
160          } else {
161            throw new CacheInternalError("no policy to change this tunable type");
162          }
163        }
164      }
165    } catch (Exception ex) {
166      if (_propName != null) {
167        throw new CacheInternalError("error applying tuning setup, property=" + _propName, ex);
168      } else {
169        throw new CacheInternalError("error applying tuning setup" , ex);
170      }
171    }
172  }
173
174}