Package com.dotmarketing.cache

Source Code of com.dotmarketing.cache.DotJbossCacheLoader2

package com.dotmarketing.cache;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

import com.dotcms.repackage.org.apache.commons.collections.map.LRUMap;
import com.dotcms.repackage.org.jboss.cache.CacheException;
import com.dotcms.repackage.org.jboss.cache.CacheSPI;
import com.dotcms.repackage.org.jboss.cache.Fqn;
import com.dotcms.repackage.org.jboss.cache.Modification;
import com.dotcms.repackage.org.jboss.cache.RegionManager;
import com.dotcms.repackage.org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
import com.dotcms.repackage.org.jboss.cache.loader.CacheLoader;

import com.dotmarketing.util.Config;
import com.dotmarketing.util.ConfigUtils;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.RegEX;
import com.dotmarketing.util.TrashUtils;

public class DotJbossCacheLoader2 implements CacheLoader {

  File root = null;
  String rootPath = null;

  /**
   * For full path, check '*' '<' '>' '|' '"' '?' Regex: [\*<>|"?]
   */
  public static String PATH_PATTERN = "[\\*<>|\"?]";

  /**
   * For fqn, check '*' '<' '>' '|' '"' '?' and also '\' '/' and ':'
   */
  public static final String FQN_PATTERN = "[\\\\\\/*<>|\"?]";
  private static LRUMap cannotCacheCache = new LRUMap(1000);
  private FileCacheLoaderConfig config;


  private boolean paused=false;

  private boolean enabled=true;

  /**
   * CacheImpl data file.
   */
  public static final String DATA = "data.dat";

  /**
   * CacheImpl directory suffix.
   */
  public static final String DIR_SUFFIX = "fdb";
  private static boolean isOldWindows;

  static {
    float osVersion;
    try {
      osVersion = Float.parseFloat(System.getProperty("os.version")
          .trim());
    } catch (Exception e) {
      osVersion = -1;
    }
    // 4.x is windows NT/2000 and 5.x is XP.
    isOldWindows = System.getProperty("os.name").toLowerCase().startsWith(
        "windows")
        && osVersion < 4;
  }

  public DotJbossCacheLoader2() {
    Logger.debug(DotJbossCacheLoader2.class, "Creating Cache Loader");
  }

  public void commit(Object arg0) throws Exception {
    // TODO Auto-generated method stub

  }

  public void create() throws Exception {
    root = new File(ConfigUtils.getDynamicContentPath() + File.separator
        + "dotcache");
    rootPath = root.getAbsolutePath() + File.separator;

    if (root == null) {
      String tmpLocation = System.getProperty("java.io.tmpdir", "./temp");
      root = new File(tmpLocation);

      rootPath = root.getAbsolutePath() + File.separator;
    }
    if (!root.exists()) {

      if (config.isCheckCharacterPortability()) {
        /*
         * Before creating the root, check whether the path is character
         * portable. Anything that comes after is part of the fqn which
         * is inspected later.
         */
        isCharacterPortableLocation(root.getAbsolutePath());
      }

      boolean created = root.mkdirs();
      if (!created) {
        throw new IOException("Unable to create cache loader location "
            + root);
      }
    }

    if (!root.isDirectory()) {
      throw new IOException("Cache loader location [" + root
          + "] is not a directory!");
    }

  }

  public void destroy() {
    // TODO Auto-generated method stub

  }

  public boolean exists(Fqn arg0) throws Exception {
    File f = getDirectory(arg0, false);
    return f != null;
  }

  public Map<Object, Object> get(Fqn fqn) throws Exception {
    if (isPaused() || !isEnabled()) {
      return null;
    }
    return loadAttributes(fqn);
  }

  public Set<?> getChildrenNames(Fqn arg0) throws Exception {
    File parent = getDirectory(arg0, false);
    if (parent == null) {
      return null;
    }
    File[] children = parent.listFiles();
    Set<String> s = new HashSet<String>();
    for (File child : children) {
      if (child.isDirectory() && child.getName().endsWith(DIR_SUFFIX)) {
        String child_name = child.getName();
        child_name = child_name.substring(0, child_name
            .lastIndexOf(DIR_SUFFIX) - 1);
        s.add(child_name);
      }
    }
    return s.size() == 0 ? null : s;

  }

  public IndividualCacheLoaderConfig getConfig() {
    return config;
  }

  public void loadEntireState(ObjectOutputStream arg0) throws Exception {
    // TODO Auto-generated method stub

  }

  public void loadState(Fqn arg0, ObjectOutputStream arg1) throws Exception {
    // TODO Auto-generated method stub

  }

  public void prepare(Object arg0, List<Modification> arg1, boolean arg2)
      throws Exception {
    put(arg1);

  }

  public void put(List<Modification> arg0) throws Exception {
    if (!isEnabled()) {
      return ;
    }
      ensureUnpaused();
      for (Modification mod : arg0) {
        put(mod.getFqn(), mod.getData());

      }

  }

  public void put(Fqn arg0, Map<Object, Object> arg1) throws Exception {
    if (!isEnabled()) {
      return ;
    }
      ensureUnpaused();
      doMarshall(arg0, arg1);


  }

  public Object put(Fqn arg0, Object key, Object value) throws Exception {
    if (!isEnabled()) {
      return null;
    }
    ensureUnpaused();
    if (!cacheToDisk(key.toString())) {
      return null;
    }
    Object retval;
    Map m = new HashMap();
    retval = m.put(key, value);
    put(arg0, m);
    return retval;
  }

  public void remove(Fqn fqn) throws Exception {
    if (!isEnabled()) {
      return ;
    }
    ensureUnpaused();
    if (!cacheToDisk(fqn.toString())) {
      return;
    }
    cannotCacheCache.remove(fqn.toString());
    File dir = getDirectory(fqn, false);
    if (dir != null) {
      boolean flag = moveDirectoryToTrash(dir);
      if (!flag) {
        Logger.warn(this, "failed removing " + fqn);
      }
    }


  }

  public Object remove(Fqn fqn, Object arg1) throws Exception {
    if (!isEnabled()) {
      return null;
    }
    ensureUnpaused();
    if (!cacheToDisk(fqn.toString())) {
      return null;
    }
    cannotCacheCache.remove(fqn.toString());

    File dir = getDirectory(fqn, false);
    if (dir != null) {
      boolean flag = moveDirectoryToTrash(dir);
      if (!flag) {
        Logger.warn(this, "failed removing " + fqn);
      }
    }
    return null;
  }

  public void removeData(Fqn fqn) throws Exception {
    if (!isEnabled()) {
      return ;
    }
    ensureUnpaused();
    if (!cacheToDisk(fqn.toString())) {
      return;
    }
    cannotCacheCache.remove(fqn.toString());

    File dir = getDirectory(fqn, false);
    if (dir != null) {
      boolean flag = moveDirectoryToTrash(dir);
      if (!flag) {
        Logger.warn(this, "failed removing " + fqn);
      }
    }

  }

  public void rollback(Object arg0) {
    // TODO Auto-generated method stub

  }

  public void setCache(CacheSPI arg0) {
    // TODO Auto-generated method stub

  }

  public void setConfig(IndividualCacheLoaderConfig base) {
    if (base instanceof FileCacheLoaderConfig) {
      this.config = (FileCacheLoaderConfig) base;
    } else if (base != null) {
      this.config = new FileCacheLoaderConfig(base);
    }
  }

  public void setRegionManager(RegionManager arg0) {
    // TODO Auto-generated method stub

  }

  public void start() throws Exception {
    // TODO Auto-generated method stub

  }

  public void stop() {
    // TODO Auto-generated method stub

  }

  public void storeEntireState(ObjectInputStream arg0) throws Exception {
    // TODO Auto-generated method stub

  }

  public void storeState(Fqn arg0, ObjectInputStream arg1) throws Exception {
    // TODO Auto-generated method stub

  }

  private File getDirectory(Fqn fqn, boolean create) throws IOException {
    File f = new File(getFullPath(fqn));
    if (!f.exists()) {
      if (create) {
        boolean make = f.mkdirs();
        if (!make)
          throw new IOException("Unable to mkdirs " + f);
      } else {
        return null;
      }
    }
    return f;
  }

  private void doMarshall(Fqn fqn, Map attrs) throws Exception {
    if (fqn.toString().length() > 255) {
      return;
    }
    if (!cacheToDisk(fqn.toString())) {
      return;
    }
    if (cannotCacheCache.get(fqn.toString()) != null) {
      Logger.debug(this,
          "returning because object is in cannot cache cache");
      return;
    }

    File f = getDirectory(fqn, true);
    Set keys = attrs.keySet();
    File child = new File(f, DATA);
    if (!child.exists()) {
      if (config.isCheckCharacterPortability()) {
        /*
         * Check whether the entire file path (root + fqn + data file
         * name), is length portable
         */
        isLengthPortablePath(child.getAbsolutePath());
        /*
         * Check whether the fqn tree we're trying to store could
         * contain non portable characters
         */
        isCharacterPortableTree(child.getAbsolutePath());
      }

      if (!child.createNewFile()) {
        throw new IOException("Unable to create file: " + child);
      }
    }
    ObjectOutputStream output = null;
    OutputStream bout =null ;
    FileOutputStream os=null;
    try {
      if (Config.getBooleanProperty("USE_CACHE_COMPRESSION", true)) {
        os=new FileOutputStream(child);
        bout = new DeflaterOutputStream(os);
      } else {
        os=new FileOutputStream(child);
        bout = new BufferedOutputStream(os,
            8192);
      }
      // GZIPOutputStream bout = new GZIPOutputStream(new
      // FileOutputStream(child), 8192);
      // ZipOutputStream bout = new ZipOutputStream(new
      // FileOutputStream(child));
      output = new ObjectOutputStream(bout);
      output.writeObject(attrs);
    } catch (StackOverflowError e) {
      Logger.debug(this, "Unable to serialize object with FQN "
          + fqn.toString(), e);
      closeOutputStreams(bout,output,os);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
    } catch (CacheException e) {
      Logger.debug(this, "Unable to serialize object with FQN "
          + fqn.toString(), e);
      closeOutputStreams(bout,output,os);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
    } catch (RuntimeException e) {
      Logger.debug(this, "Unable to serialize object with FQN "
          + fqn.toString(), e);
      closeOutputStreams(bout,output,os);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
    } catch (StreamCorruptedException e) {
      Logger.debug(this, "Unable to serialize object with FQN "
          + fqn.toString(), e);
      closeOutputStreams(bout,output,os);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
    } catch (Exception e) {
      closeOutputStreams(bout,output,os);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
      Logger.debug(this, "Unable to serialize object with FQN "
          + fqn.toString(), e);
    } finally {
      closeOutputStreams(bout,output,os);
    }
  }
 
  private void closeOutputStreams(OutputStream... outputStreams ) {
    for (OutputStream os:outputStreams) {
      if (os!=null) {
        try {
          os.close();
        } catch (Exception e) {
          Logger.error(this, "Error closing stream: " + e.getMessage(),e);
        }
      }
    }
  }

 
 
  private void closeInputStreams(InputStream... inputStreams ) {
    for (InputStream is:inputStreams) {
      if (is!=null) {
        try {
          is.close();
        } catch (Exception e) {
          Logger.error(this, "Error closing stream: " + e.getMessage(),e);
        }
      }
    }
  }

  private String getFullPath(Fqn fqn) {
    StringBuilder sb = new StringBuilder(rootPath);
    if (fqn.size() == 1) {
      sb.append(fqn.get(0)).append(".").append(DIR_SUFFIX).append(
          File.separator);
    } else {
      for (int i = 0; i < fqn.size(); i++) {
        Object tmp = fqn.get(i);
        // This is where we convert from Object to String!
        String tmp_dir = tmp.toString().replace(':', '-'); // returns
        // tmp.this
        // if it's a
        if (fqn.size() == i + 1) {
          sb.append(buildBTreePath(tmp.hashCode() + "")
              + File.separator);
        }
        // String
        sb.append(tmp_dir).append(".").append(DIR_SUFFIX).append(
            File.separator);
      }
    }
    return sb.toString();
  }

  private String buildBTreePath(String hashcode) {
    String result = "";
    try {
      result = hashcode.substring(1, 2);
      result += File.separator + hashcode.substring(2, 3);
      result += File.separator + hashcode.substring(3, 4);
      result += File.separator + hashcode.substring(4, 5);
    } catch (IndexOutOfBoundsException e) {
      Logger.debug(this, "hashcode too short returning path");
    }
    return result;
  }

  private boolean isLengthPortablePath(String absoluteFqnPath) {

    if (isOldWindows && absoluteFqnPath.length() > 255) {
      Logger
          .warn(
              this,
              "The full absolute path to the fqn that you are trying to store is bigger than 255 characters, this could lead to problems on certain Windows systems: "
                  + absoluteFqnPath);
      return false;
    }

    return true;
  }

  private boolean isCharacterPortableTree(String fqn) {
    StringTokenizer st = new StringTokenizer(fqn, File.separator);
    // Don't assume the Fqn is composed of Strings!!
    while (st.hasMoreTokens()) {
      String element = st.nextToken();
      // getFullPath converts Object to String via toString(), so we do
      // too
      boolean contains = RegEX.contains(element, FQN_PATTERN);

      if (contains) {
        Logger
            .warn(
                this,
                "One of the Fqn ( "
                    + fqn
                    + " ) elements contains one of these characters: '*' '<' '>' '|' '\"' '?' '\\' '/' ':' ");
        Logger
            .warn(
                this,
                "Directories containing these characters are illegal in some operating systems and could lead to portability issues");
        return false;
      }
    }

    return true;
  }

  private Map loadAttributes(Fqn fqn) throws Exception {
    File f = getDirectory(fqn, false);
    if (f == null)
      return null; // i.e., this node does not exist.
    // this node exists so we should never return a null after this... at
    // worst case, an empty HashMap.
    File child = new File(f, DATA);
    if (!child.exists()) {

      return null; // no node attribs exist hence the empty
    }
    // HashMap.
    // if(!child.exists()) return null;

    Map m;
    try {
      m = (Map) doUnmarshall(fqn, child);
      // m = (Map) regionAwareUnmarshall(fqn, child);
    } catch (FileNotFoundException fnfe) {
      // child no longer exists!
      m = null;
    } catch (Exception e) {

      m = null;
    }
    return m;
  }

  private Object doUnmarshall(Fqn fqn, Object fromFile) throws Exception {
    ObjectInputStream input = null;
    InputStream bin = null;
    FileInputStream is = null;
    try {
      if (Config.getBooleanProperty("USE_CACHE_COMPRESSION", true)) {
        is=new FileInputStream((File) fromFile);
        bin = new InflaterInputStream(is);
      } else {
        is=new FileInputStream((File) fromFile);
        bin = new BufferedInputStream(is, 8192);
      }
      // GZIPInputStream bin = new GZIPInputStream(new
      // FileInputStream((File) fromFile), 8192);
      // ZipInputStream bin = new ZipInputStream(new
      // FileInputStream((File) fromFile));
      input = new ObjectInputStream(bin);
      Object unmarshalledObj = input.readObject();
      return unmarshalledObj;
    } catch (StackOverflowError e) {
      Logger.debug(this, "Unable to unserialize object with FQN "
          + fqn.toString(), e);
      closeInputStreams(input,bin,is);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
      return null;
    } catch (CacheException e) {
      Logger.debug(this, "Unable to unserialize object with FQN "
          + fqn.toString(), e);
      closeInputStreams(input,bin,is);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
      return null;
    } catch (RuntimeException e) {
      Logger.debug(this, "Unable to unserialize object with FQN "
          + fqn.toString(), e);
      closeInputStreams(input,bin,is);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
      return null;
    } catch (StreamCorruptedException e) {
      Logger.debug(this, "Unable to unserialize object with FQN "
          + fqn.toString(), e);
      closeInputStreams(input,bin,is);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
      return null;
    } catch (IOException e) {
      return null;
    } catch (Exception e) {
      Logger.debug(this, "Unable to unserialize object with FQN "
          + fqn.toString(), e);
      closeInputStreams(input,bin,is);
      try {
        removeData(fqn);
        cannotCacheCache.put(fqn.toString(), fqn.toString());
      } catch (Exception e1) {
        Logger.warn(this, "Unable to delete file", e1);
      }
      return null;
    } finally {
      closeInputStreams(input,bin,is);
     
    }
  }

  private synchronized void disable() {
    if (!enabled) {
      return;
    }
    Logger.info(this, "Temporarily disabling on disk cache");
    enabled=false;
    Thread t=new Thread() {
      public void run() {
        boolean done=false;
        Fqn fqn=Fqn.fromString("");
        while (!done) {
          try {
            Thread.sleep(120000);
          } catch (InterruptedException e) {
          }
            try {
              File dir=getDirectory(fqn, false);
              moveDirectoryToTrash(dir, true);
              enable();
              done=true;
            } catch (IOException e) {
              Logger.error(DotJbossCacheLoader2.class,"IOException: " +e.getMessage(),e);
            }

        }

      }
    };
    t.setName("OnDiskCPRThread");
    t.start();
  }

  private synchronized void enable() {
    enabled=true;
    paused=false;
    Logger.info(this,"On disk cache enabled");
  }

  private boolean isEnabled() {
    return enabled;
  }

  private boolean isPaused() {
    return paused;
  }


  private  void ensureUnpaused () throws IOException {
    int count=0;

    while (isPaused()) {
      count ++;
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
      }
      if (count > 200) {
        throw new IOException("Timeout while waiting for cache to unlock");
      }
    }
    if (!enabled) {
      throw new IOException("Cache is disabled");
    }
    return ;
  }

  private void moveDirectoryToTrash(File dir, boolean isRoot) throws IOException {
    if (isRoot) {
     File[] subfiles = dir.listFiles();
     if (!TrashUtils.moveFileToThrash(subfiles, "dotcache")) {
      throw new IOException("Could not move directory to trash");
     }
    } else {
     if (!TrashUtils.moveFileToThrash(dir, "dotcache")) {
      throw new IOException("Could not move directory to trash");
     }
    }
   }

  /**
   * Recursively removes this and all subdirectories, plus all DATA files in
   * them. To prevent damage, we only remove files under the cache root.
   *
   * @return <code>true</code> if directory was removed, <code>false</code> if
   *         not.
   * @throws Exception
   */
  private boolean moveDirectoryToTrash(File dir) throws Exception {
    if (!dir.getCanonicalPath().toLowerCase().contains(
        root.getCanonicalPath().toLowerCase())) {
      Logger.error(this.getClass(),
          "Cache trying to delete a non-cache dir : "
              + dir.getAbsolutePath());
      return false;
    }
    boolean isRoot = dir.getCanonicalPath().toLowerCase().equals(
        root.getCanonicalPath().toLowerCase());
    try {
      moveDirectoryToTrash(dir, isRoot);
    } catch (IOException e) {
      synchronized (this) {
        paused = true;
        int count = 0;
        while (paused) {

          try {
            moveDirectoryToTrash(dir, isRoot);
            paused = false;
          } catch (IOException e2) {
            count++;
            Thread.sleep(100);
            if (count > 100) {
              disable();
            }
          }
        }

      }
    } finally {
      paused=false;
    }
    return true;
  }

  private boolean isCharacterPortableLocation(String fileAbsolutePath) {
    boolean contains = RegEX.contains(fileAbsolutePath, PATH_PATTERN);
    if (contains) {
      Logger
          .warn(
              this,
              "Cache loader location ( "
                  + fileAbsolutePath
                  + " ) contains one of these characters: '*' '<' '>' '|' '\"' '?'");
      Logger
          .warn(
              this,
              "Directories containing these characters are illegal in some operative systems and could lead to portability issues");
      return false;
    }

    return true;
  }

  private boolean cacheToDisk(String key) {
    if (key.startsWith("VelocityMenuCache")) {
      return false;
    }
    if (key.startsWith("VelocityCache")) {
      if (!(key.contains("live") || key.contains("working"))) {
        return false;
      }
    }
    return true;
  }

}
TOP

Related Classes of com.dotmarketing.cache.DotJbossCacheLoader2

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.