org.red5.server.persistence.FilePersistence.java Source code

Java tutorial

Introduction

Here is the source code for org.red5.server.persistence.FilePersistence.java

Source

package org.red5.server.persistence;

/*
 * RED5 Open Source Flash Server - http://www.osflash.org/red5
 * 
 * Copyright (c) 2006-2007 by respective authors (see below). All rights reserved.
 * 
 * This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.common.ByteBuffer;
import org.red5.io.amf.Input;
import org.red5.io.amf.Output;
import org.red5.io.object.Deserializer;
import org.red5.server.api.IScope;
import org.red5.server.api.persistence.IPersistable;
import org.red5.server.net.servlet.ServletUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;

/**
 * Simple file-based persistence for objects. Lowers memory usage if used instead of RAM memoty storage.
 * 
 * @author The Red5 Project (red5@osflash.org)
 * @author Joachim Bauch (jojo@struktur.de)
 *
 */
public class FilePersistence extends RamPersistence {

    /**
     * Logger
     */
    private Log log = LogFactory.getLog(FilePersistence.class.getName());
    /**
     * Files path
     */
    private String path = "persistence";
    /**
     * Root directory under file storage path
     */
    private String rootDir = "";
    /**
     * File extension for persistent objects
     */
    private String extension = ".red5";

    /**
     * Whether there's ned to check for empty directories
     */
    // TODO: make this configurable
    private boolean checkForEmptyDirectories = true;

    /**
     * Create file persistence object from given resource pattern resolver
     * @param resolver            Resource pattern resolver and loader
     */
    public FilePersistence(ResourcePatternResolver resolver) {
        super(resolver);
        setPath(path);
    }

    /**
     * Create file persistence object for given scope
     * @param scope               Scope
     */
    public FilePersistence(IScope scope) {
        super(scope);
        setPath(path);
    }

    /**
      * Setter for file path.
      *
      * @param path  New path
      */
    public void setPath(String path) {
        Resource rootFile = resources.getResource(path);
        try {
            rootDir = rootFile.getFile().getAbsolutePath();
            this.path = path;
        } catch (IOException err) {
            log.error("I/O exception thrown when setting file path to " + path);
            throw (new RuntimeException(err));
        }
    }

    /**
      * Setter for extension.
      *
      * @param extension  New extension.
      */
    public void setExtension(String extension) {
        this.extension = extension;
    }

    /**
     * Return file path for persistable object
     * @param object          Object to obtain file path for
     * @return                Path on disk
     */
    private String getObjectFilepath(IPersistable object) {
        return getObjectFilepath(object, false);
    }

    /**
     * Return file path for persistable object
     * @param object          Object to obtain file path for
     * @param completePath    Whether it full path full path sould be returned
     * @return                Path on disk
     */
    private String getObjectFilepath(IPersistable object, boolean completePath) {
        String result = path + '/' + object.getType() + '/' + object.getPath();
        if (!result.endsWith("/")) {
            result += '/';
        }

        if (completePath) {
            String name = object.getName();
            int pos = name.lastIndexOf('/');
            if (pos >= 0) {
                result += name.substring(0, pos);
            }
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    protected String getObjectPath(String id, String name) {
        if (id.startsWith(path)) {
            id = id.substring(path.length() + 1);
        }

        return super.getObjectPath(id, name);
    }

    /**
     * Get filename for persistable object
     * @param object          Persistable object
     * @return                Name of file where given object is persisted to
     */
    private String getObjectFilename(IPersistable object) {
        String path = getObjectFilepath(object);
        String name = object.getName();
        if (name == null) {
            name = PERSISTENCE_NO_NAME;
        }
        return path + name + extension;
    }

    /**
     * Load resource with given name
     * @param name             Resource name
     * @return                 Persistable object
     */
    private IPersistable doLoad(String name) {
        return doLoad(name, null);
    }

    /**
     * Load resource with given name and attaches to persistable object
     * @param name             Resource name
     * @param object           Object to attach to
     * @return                 Persistable object
     */
    private IPersistable doLoad(String name, IPersistable object) {
        IPersistable result = object;
        Resource data = resources.getResource(name);
        if (data == null || !data.exists()) {
            // No such file
            return null;
        }

        FileInputStream input;
        String filename;
        try {
            File fp = data.getFile();
            if (fp.length() == 0) {
                // File is empty
                log.error("The file at " + data.getFilename() + " is empty.");
                return null;
            }

            filename = fp.getAbsolutePath();
            input = new FileInputStream(filename);
        } catch (FileNotFoundException e) {
            log.error("The file at " + data.getFilename() + " does not exist.");
            return null;
        } catch (IOException e) {
            log.error("Could not load file from " + data.getFilename() + '.', e);
            return null;
        }

        try {
            ByteBuffer buf = ByteBuffer.allocate(input.available());
            try {
                ServletUtils.copy(input, buf.asOutputStream());
                buf.flip();
                Input in = new Input(buf);
                Deserializer deserializer = new Deserializer();
                String className = (String) deserializer.deserialize(in);
                if (result == null) {
                    // We need to create the object first
                    try {
                        Class theClass = Class.forName(className);
                        Constructor constructor = null;
                        try {
                            // Try to create object by calling constructor with Input stream as
                            // parameter.
                            for (Class interfaceClass : in.getClass().getInterfaces()) {
                                constructor = theClass.getConstructor(new Class[] { interfaceClass });
                                if (constructor != null) {
                                    break;
                                }
                            }
                            if (constructor == null) {
                                throw new NoSuchMethodException();
                            }

                            result = (IPersistable) constructor.newInstance(in);
                        } catch (NoSuchMethodException err) {
                            // No valid constructor found, use empty
                            // constructor.
                            result = (IPersistable) theClass.newInstance();
                            result.deserialize(in);
                        } catch (InvocationTargetException err) {
                            // Error while invoking found constructor, use empty
                            // constructor.
                            result = (IPersistable) theClass.newInstance();
                            result.deserialize(in);
                        }
                    } catch (ClassNotFoundException cnfe) {
                        log.error("Unknown class " + className);
                        return null;
                    } catch (IllegalAccessException iae) {
                        log.error("Illegal access.", iae);
                        return null;
                    } catch (InstantiationException ie) {
                        log.error("Could not instantiate class " + className);
                        return null;
                    }

                    // Set object's properties
                    result.setName(getObjectName(name));
                    result.setPath(getObjectPath(name, result.getName()));
                } else {
                    // Initialize existing object
                    String resultClass = result.getClass().getName();
                    if (!resultClass.equals(className)) {
                        log.error("The classes differ: " + resultClass + " != " + className);
                        return null;
                    }

                    result.deserialize(in);
                }
            } finally {
                buf.release();
                buf = null;
            }
            if (result.getStore() != this) {
                result.setStore(this);
            }
            super.save(result);
            if (log.isDebugEnabled()) {
                log.debug("Loaded persistent object " + result + " from " + filename);
            }
        } catch (IOException e) {
            log.error("Could not load file at " + filename);
            return null;
        }

        return result;
    }

    /** {@inheritDoc} */
    @Override
    public IPersistable load(String name) {
        IPersistable result = super.load(name);
        if (result != null) {
            // Object has already been loaded
            return result;
        }

        return doLoad(path + '/' + name + extension);
    }

    /** {@inheritDoc} */
    @Override
    public boolean load(IPersistable object) {
        if (object.isPersistent()) {
            // Already loaded
            return true;
        }

        return (doLoad(getObjectFilename(object), object) != null);
    }

    /**
     * Save persistable object
     * @param object           Persistable object
     * @return                 <code>true</code> on success, <code>false</code> otherwise
     */
    private boolean saveObject(IPersistable object) {
        String path = getObjectFilepath(object, true);
        Resource resPath = resources.getResource(path);
        File file;
        try {
            file = resPath.getFile();
        } catch (IOException err) {
            log.error("Could not create resource file for path " + path, err);
            return false;
        }

        if (!file.isDirectory() && !file.mkdirs()) {
            log.error("Could not create directory " + file.getAbsolutePath());
            return false;
        }

        String filename = getObjectFilename(object);
        Resource resFile = resources.getResource(filename);
        try {
            ByteBuffer buf = ByteBuffer.allocate(1024);
            try {
                buf.setAutoExpand(true);
                Output out = new Output(buf);
                out.writeString(object.getClass().getName());
                object.serialize(out);
                buf.flip();

                FileOutputStream output = new FileOutputStream(resFile.getFile().getAbsolutePath());
                ServletUtils.copy(buf.asInputStream(), output);
                output.close();
            } finally {
                buf.release();
                buf = null;
            }
            if (log.isDebugEnabled()) {
                log.debug("Stored persistent object " + object + " at " + filename);
            }
            return true;
        } catch (IOException e) {
            log.error("Could not create / write file " + filename);
            return false;
        }
    }

    /** {@inheritDoc} */
    @Override
    public boolean save(IPersistable object) {
        if (!super.save(object)) {
            return false;
        }

        boolean persistent = this.saveObject(object);
        object.setPersistent(persistent);
        return persistent;
    }

    /**
     * Remove empty dirs
     * @param base             Base directory
     */
    protected void checkRemoveEmptyDirectories(String base) {
        if (!checkForEmptyDirectories) {
            return;
        }

        String dir;
        Resource resFile = resources.getResource(base.substring(0, base.lastIndexOf('/')));
        try {
            dir = resFile.getFile().getAbsolutePath();
        } catch (IOException err) {
            return;
        }

        while (!dir.equals(rootDir)) {
            File fp = new File(dir);
            if (!fp.isDirectory()) {
                // This should never happen
                break;
            }

            if (fp.list().length != 0) {
                // Directory is not empty
                break;
            }

            if (!fp.delete()) {
                // Could not remove directory
                break;
            }

            // Move up one directory
            dir = fp.getParent();
        }
    }

    /** {@inheritDoc} */
    @Override
    public boolean remove(String name) {
        super.remove(name);
        String filename = path + '/' + name + extension;
        Resource resFile = resources.getResource(filename);
        if (!resFile.exists()) {
            // File already deleted
            return true;
        }

        try {
            boolean result = resFile.getFile().delete();
            if (result) {
                checkRemoveEmptyDirectories(filename);
            }
            return result;
        } catch (IOException err) {
            return false;
        }
    }

    /** {@inheritDoc} */
    @Override
    public boolean remove(IPersistable object) {
        return remove(getObjectId(object));
    }

}