/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.lock.StripedLock; import org.jboss.util.stream.MarshalledValueInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Simple file-based CacheLoader implementation. Nodes are directories, attributes of a node is a file in the directory *
* The FileCacheLoader has some severe limitations which restrict its use in a production * environment, or if used in such an environment, it should be used with due care and sufficient * understanding of these limitations. *true
if directory was removed,
* false
if not.
*/
boolean removeDirectory(File dir, boolean include_start_dir)
{
boolean success = true;
File[] subdirs = dir.listFiles();
if (subdirs == null)
{
if (log.isWarnEnabled()) log.warn("Null list of files for dir: " + dir.getAbsolutePath());
return false;
}
for (File file : subdirs)
{
if (file.isFile() && file.getName().equals(DATA))
{
if (!file.delete())
{
success = false;
}
continue;
}
if (file.isDirectory() && file.getName().endsWith(DIR_SUFFIX))
{
if (!removeDirectory(file, false))
{
success = false;
}
if (!file.delete())
{
success = false;
}
}
}
if (include_start_dir && !dir.equals(root))
{
if (dir.delete())
{
return success;
}
success = false;
}
return success;
}
String getFullPath(Fqn fqn)
{
StringBuilder sb = new StringBuilder(rootPath);
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(); // returns tmp.this if it's a String
sb.append(tmp_dir).append(".").append(DIR_SUFFIX).append(File.separator);
}
return sb.toString();
}
protected 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 new HashMap(0); // no node attribs exist hence the empty HashMap.
//if(!child.exists()) return null;
Map m;
try
{
//m = (Map) unmarshall(child);
m = (Map) regionAwareUnmarshall(fqn, child);
}
catch (FileNotFoundException fnfe)
{
// child no longer exists!
m = Collections.emptyMap();
}
return m;
}
protected void storeAttributes(Fqn fqn, Map attrs) throws Exception
{
if (attrs != null && !(attrs instanceof HashMap))
throw new RuntimeException("Unsupporte dmap type " + attrs.getClass());
regionAwareMarshall(fqn, attrs);
}
@Override
protected void doMarshall(Fqn fqn, Object toMarshall) throws Exception
{
Map attrs = (Map) toMarshall;
if (attrs != null && !(attrs instanceof HashMap)) throw new RuntimeException("Map is " + attrs.getClass());
File f = getDirectory(fqn, true);
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(fqn);
}
if (!child.createNewFile())
{
throw new IOException("Unable to create file: " + child);
}
}
FileOutputStream fileOut = new FileOutputStream(child);
ObjectOutputStream output = new ObjectOutputStream(fileOut);
getMarshaller().objectToObjectStream(attrs, output);
output.close();
}
@Override
protected Object doUnmarshall(Fqn fqn, Object fromFile) throws Exception
{
FileInputStream fileIn = new FileInputStream((File) fromFile);
ObjectInputStream input = new MarshalledValueInputStream(fileIn);
Object unmarshalledObj = getMarshaller().objectFromObjectStream(input);
input.close();
return unmarshalledObj;
}
protected boolean isCharacterPortableLocation(String fileAbsolutePath)
{
Matcher matcher = PATH_PATTERN.matcher(fileAbsolutePath);
if (matcher.find())
{
log.warn("Cache loader location ( " + fileAbsolutePath + " ) contains one of these characters: '*' '<' '>' '|' '\"' '?'");
log.warn("Directories containing these characters are illegal in some operative systems and could lead to portability issues");
return false;
}
return true;
}
protected boolean isCharacterPortableTree(Fqn fqn)
{
List elements = fqn.peekElements();
// Don't assume the Fqn is composed of Strings!!
for (Object anElement : elements)
{
// getFullPath converts Object to String via toString(), so we do too
Matcher matcher = FQN_PATTERN.matcher(anElement.toString());
if (matcher.find())
{
log.warn("One of the Fqn ( " + fqn + " ) elements contains one of these characters: '*' '<' '>' '|' '\"' '?' '\\' '/' ':' ");
log.warn("Directories containing these characters are illegal in some operating systems and could lead to portability issues");
return false;
}
}
return true;
}
protected boolean isLengthPortablePath(String absoluteFqnPath)
{
if (isOldWindows && absoluteFqnPath.length() > 255)
{
log.warn("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;
}
}