com.flexive.shared.mbeans.FxCacheProxy.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.shared.mbeans.FxCacheProxy.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) framework.
 *
 *  Copyright (c) 1999-2014
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) project is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser General Public
 *  License version 2.1 or higher as published by the Free Software Foundation.
 *
 *  The GNU Lesser General Public License can be found at
 *  http://www.gnu.org/licenses/lgpl.html.
 *  A copy is found in the textfile LGPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  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 General Public License for more details.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.shared.mbeans;

import com.flexive.shared.CacheAdmin;
import com.flexive.shared.FxContext;
import com.flexive.shared.cache.FxBackingCache;
import com.flexive.shared.cache.FxCacheException;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Cache;

import javax.management.*;
import java.io.Serializable;
import java.util.Set;

/**
 * Proxy for the FxCache MBean (only for internal use!)
 *
 * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 */
public class FxCacheProxy implements FxCacheMBean {
    private static final Log LOG = LogFactory.getLog(FxCacheProxy.class);

    private MBeanServer server;
    private ObjectName name;

    public FxCacheProxy(MBeanServer server) throws MalformedObjectNameException {
        this.server = server;
        this.name = new ObjectName(CacheAdmin.CACHE_SERVICE_NAME);
    }

    public ObjectName getName() {
        return name;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void create() throws Exception {
        server.invoke(name, "create", new Object[0], new String[0]);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void destroy() throws Exception {
        server.invoke(name, "destroy", new Object[0], new String[0]);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Cache<Object, Object> getCache() throws FxCacheException {
        return getBackingCache().getCache();
    }

    private FxBackingCache getBackingCache() {
        try {
            return (FxBackingCache) server.getAttribute(name, "FxCache");
        } catch (MBeanException e) {
            throw new RuntimeException(
                    "Cache error invoking the managed bean: " + e.getMessage() + ", TargetException: "
                            + (e.getTargetException() == null ? "(unknown)" : e.getTargetException().getMessage()));
        } catch (AttributeNotFoundException e) {
            throw new RuntimeException("Attribute FxCache not found in managed cache bean: " + e.getMessage());
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException("No FxCache instance found!");
        } catch (ReflectionException e) {
            throw new RuntimeException(
                    "Could not invoke operation on FxCache (reflection error): " + e.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object get(String path, Object key) throws FxCacheException {
        try {
            return unmarshal(server.invoke(name, "get", new Object[] { divisionEncodePath(path), key },
                    new String[] { "java.lang.String", "java.lang.Object" }));
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void put(String path, Object key, Object value) throws FxCacheException {
        try {
            server.invoke(name, "put", new Object[] { divisionEncodePath(path), key, marshal(value) },
                    new String[] { "java.lang.String", "java.lang.Object", "java.lang.Object" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void remove(String path) throws FxCacheException {
        try {
            server.invoke(name, "remove", new Object[] { divisionEncodePath(path) },
                    new String[] { "java.lang.String" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void remove(String path, Object key) throws FxCacheException {
        try {
            server.invoke(name, "remove", new Object[] { divisionEncodePath(path), key },
                    new String[] { "java.lang.String", "java.lang.Object" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set getKeys(String path) throws FxCacheException {
        try {
            return (Set) server.invoke(name, "getKeys", new Object[] { divisionEncodePath(path) },
                    new String[] { "java.lang.String" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set globalGetKeys(String path) throws FxCacheException {
        try {
            return (Set) server.invoke(name, "globalGetKeys", new Object[] { globalDivisionEncodePath(path) },
                    new String[] { "java.lang.String" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object globalGet(String path, Object key) throws FxCacheException {
        try {
            return unmarshal(server.invoke(name, "globalGet", new Object[] { globalDivisionEncodePath(path), key },
                    new String[] { "java.lang.String", "java.lang.Object" }));
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean globalExists(String path, Object key) throws FxCacheException {
        try {
            return (Boolean) server.invoke(name, "globalExists",
                    new Object[] { globalDivisionEncodePath(path), key },
                    new String[] { "java.lang.String", "java.lang.Object" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean exists(String path, Object key) throws FxCacheException {
        try {
            return (Boolean) server.invoke(name, "exists", new Object[] { divisionEncodePath(path), key },
                    new String[] { "java.lang.String", "java.lang.Object" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void globalPut(String path, Object key, Object value) throws FxCacheException {
        try {
            server.invoke(name, "globalPut", new Object[] { globalDivisionEncodePath(path), key, marshal(value) },
                    new String[] { "java.lang.String", "java.lang.Object", "java.lang.Object" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void globalRemove(String path) throws FxCacheException {
        try {
            server.invoke(name, "globalRemove", new Object[] { globalDivisionEncodePath(path) },
                    new String[] { "java.lang.String" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void globalRemove(String path, Object key) throws FxCacheException {
        try {
            server.invoke(name, "globalRemove", new Object[] { globalDivisionEncodePath(path), key },
                    new String[] { "java.lang.String", "java.lang.Object" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set getChildrenNames(String path) throws FxCacheException {
        try {
            return (Set) server.invoke(name, "getChildrenNames", new Object[] { divisionEncodePath(path) },
                    new String[] { "java.lang.String" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    @Override
    public boolean isPathLockedInTx(String path) throws FxCacheException {
        try {
            return (Boolean) server.invoke(name, "isPathLockedInTx", new Object[] { divisionEncodePath(path) },
                    new String[] { "java.lang.String" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getDeploymentId() {
        try {
            return (String) server.getAttribute(name, "DeploymentId");
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException("No FxCache instance found!");
        } catch (MBeanException e) {
            throw new RuntimeException(
                    "Cache error invoking the managed bean: " + e.getMessage() + ", TargetException: "
                            + (e.getTargetException() == null ? "(unknown)" : e.getTargetException().getMessage()));
        } catch (ReflectionException e) {
            throw new RuntimeException(
                    "Could not invoke operation on FxCache (reflection error): " + e.getMessage());
        } catch (AttributeNotFoundException e) {
            throw new RuntimeException("Attribute DeploymentId not found in managed cache bean: " + e.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getSystemStartTime() {
        try {
            return (Long) server.getAttribute(name, "SystemStartTime");
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException("No FxCache instance found!");
        } catch (MBeanException e) {
            throw new RuntimeException(
                    "Cache error invoking the managed bean: " + e.getMessage() + ", TargetException: "
                            + (e.getTargetException() == null ? "(unknown)" : e.getTargetException().getMessage()));
        } catch (ReflectionException e) {
            throw new RuntimeException(
                    "Could not invoke operation on FxCache (reflection error): " + e.getMessage());
        } catch (AttributeNotFoundException e) {
            throw new RuntimeException(
                    "Attribute SystemStartTime not found in managed cache bean: " + e.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getNodeStartTime() {
        try {
            return (Long) server.getAttribute(name, "NodeStartTime");
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException("No FxCache instance found!");
        } catch (MBeanException e) {
            throw new RuntimeException(
                    "Cache error invoking the managed bean: " + e.getMessage() + ", TargetException: "
                            + (e.getTargetException() == null ? "(unknown)" : e.getTargetException().getMessage()));
        } catch (ReflectionException e) {
            throw new RuntimeException(
                    "Could not invoke operation on FxCache (reflection error): " + e.getMessage());
        } catch (AttributeNotFoundException e) {
            throw new RuntimeException(
                    "Attribute NodeStartTime not found in managed cache bean: " + e.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void reloadEnvironment(Integer divisionId) throws Exception {
        try {
            server.invoke(name, "reloadEnvironment", new Object[] { divisionId },
                    new String[] { "java.lang.Integer" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setEvictionStrategy(Integer divisionId, String path, Integer maxContents, Integer timeToIdle,
            Integer timeToLive) {
        try {
            server.invoke(name, "setEvictionStrategy",
                    new Object[] { divisionId, path, maxContents, timeToIdle, timeToLive },
                    new String[] { "java.lang.Integer", "java.lang.String", "java.lang.Integer",
                            "java.lang.Integer", "java.lang.Integer" });
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException("No FxCache instance found!");
        } catch (MBeanException e) {
            throw new RuntimeException(
                    "Cache error invoking the managed bean: " + e.getMessage() + ", TargetException: "
                            + (e.getTargetException() == null ? "(unknown)" : e.getTargetException().getMessage()));
        } catch (ReflectionException e) {
            throw new RuntimeException(
                    "Could not invoke operation on FxCache (reflection error): " + e.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setEvictionStrategy(Integer divisionId, String path, Integer maxContents, Integer timeToIdle,
            Integer timeToLive, Boolean overwrite) throws FxCacheException {
        try {
            server.invoke(name, "setEvictionStrategy",
                    new Object[] { divisionId, path, maxContents, timeToIdle, timeToLive, overwrite },
                    new String[] { "java.lang.Integer", "java.lang.String", "java.lang.Integer",
                            "java.lang.Integer", "java.lang.Integer", "java.lang.Boolean" });
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    @Override
    public void cleanupAfterRequest() throws FxCacheException {
        try {
            server.invoke(name, "cleanupAfterRequest", new Object[0], new String[0]);
        } catch (InstanceNotFoundException e) {
            throw newNotFoundException(e);
        } catch (MBeanException e) {
            throw newMBeanException(e);
        } catch (ReflectionException e) {
            throw newReflectionException(e);
        }
    }

    private FxCacheException newReflectionException(ReflectionException e) {
        return new FxCacheException("Could not invoke operation on FxCache (reflection error): " + e.getMessage(),
                e);
    }

    private FxCacheException newMBeanException(MBeanException e) {
        return new FxCacheException(
                "Cache error invoking the managed bean: " + e.getMessage() + ", TargetException: "
                        + (e.getTargetException() == null ? "(unknown)" : e.getTargetException().getMessage()),
                e.getTargetException());
    }

    private FxCacheException newNotFoundException(InstanceNotFoundException e) {
        return new FxCacheException("No FxCache instance found!", e);
    }

    /**
     * Marshal a value before writing it to the cache, if necessary.
     *
     * <p>If the cache is shared between several deployments (class loaders) in a VM, we need
     * to serialize all values before writing them to the cache to avoid ClassCastExceptions.
     * This obviously has a grave performance penalty, but really seems to be the only solution in this scenario.
     * For optimal cache performance, deploy your applications in a single EAR
     * or disabled the shared cache if you deploy only a single WAR file.</p>
        
     * @param value   the value to be written
     * @return        the marshaled value
     * @since         3.1.4
     * @see           CacheAdmin#isSharedCache() 
     */
    public static Object marshal(Object value) {

        // marshal only if the cache is shared between deployments, otherwise let JBoss Cache handle this
        return CacheAdmin.isSharedCache() ? SerializationUtils.serialize((Serializable) value) : value;
    }

    /**
     * Un-marshal a value processed by {@link #marshal(Object)} before.
     *
     * @param value the value to be de-marshaled
     * @return      the original value
     * @since       3.1.4
     */
    public static Object unmarshal(Object value) {
        return value != null ? (CacheAdmin.isSharedCache() ? SerializationUtils.deserialize((byte[]) value) : value)
                : null;
    }

    /**
     * Includes the division id into the path.
     *
     * @param path the path to encode
     * @return the encoded path
     * @throws FxCacheException if the division id could not be resolved
     */
    private String divisionEncodePath(String path) throws FxCacheException {
        try {
            int divId;
            //#<id>  - purposely undocumented hack to force a division ;) - used during environment loading
            if (path.charAt(0) == '#') {
                try {
                    divId = Integer.parseInt(path.substring(1, path.indexOf('/')));
                    path = path.substring(path.indexOf('/'));
                } catch (Exception e) {
                    throw new FxCacheException("Invalid Division Id in path [" + path + "]!");
                }
            } else {
                FxContext ri = FxContext.get();
                if (ri.getDivisionId() == -1) {
                    throw new FxCacheException(
                            "Division ID missing in request information [" + ri.getRequestURI() + "]");
                }
                divId = ri.getDivisionId();
            }
            return "/Division" + divId + (path.startsWith("/") ? "" : "/") + path;
        } catch (Throwable t) {
            LOG.error("Unable to encode division ID in cache path: " + t.getMessage(), t);
            throw new FxCacheException("Unable to encode path: " + t.getMessage());
        }
    }

    /**
     * Includes the global division id into the path.
     *
     * @param path the path to encode
     * @return the encoded path
     */
    public static String globalDivisionEncodePath(final String path) {
        return "/GlobalConfiguration" + (path.startsWith("/") ? "" : "/") + path;
    }

}