All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.ehcache.distribution.jms.JMSCachePeer Maven / Gradle / Ivy

/**
 *  Copyright 2003-2008 Luck Consulting Pty Ltd
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package net.sf.ehcache.distribution.jms;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.MimeTypeByteArray;
import net.sf.ehcache.distribution.CachePeer;
import static net.sf.ehcache.distribution.jms.JMSEventMessage.ACTION_PROPERTY;
import static net.sf.ehcache.distribution.jms.JMSEventMessage.CACHE_NAME_PROPERTY;
import static net.sf.ehcache.distribution.jms.JMSEventMessage.KEY_PROPERTY;
import static net.sf.ehcache.distribution.jms.JMSUtil.CACHE_MANAGER_UID;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A JMS Cache Peer subscribes to JMS messages, both from the replication topic and the get queue.
 *
 * @author [email protected]
 * @author Greg Luck
 */
public class JMSCachePeer implements CachePeer, MessageListener {

    private static final Logger LOG = Logger.getLogger(JMSCachePeer.class.getName());

    /**
     * Used only in testing
     */
    private static final int TEST_DELAY = 11000;

     /***/
    protected Session producerSession;

    /***/
    protected MessageProducer messageProducer;

    private CacheManager cacheManager;
    private boolean shutdown;
    private QueueSession getQueueSession;




    /**
     * Constructor
     */
    public JMSCachePeer(CacheManager cacheManager,
                        MessageProducer messageProducer,
                        Session producerSession,
                        QueueSession getQueueSession) {


        if (LOG.isLoggable(Level.FINEST)) {
            LOG.finest("JMSCachePeer constructor ( cacheManager = "
                    + cacheManager
                    + ", messageProducer = " + messageProducer + " ) called");
        }

        this.cacheManager = cacheManager;
        this.messageProducer = messageProducer;
        this.producerSession = producerSession;
        this.getQueueSession = getQueueSession;
    }


    /**
     * Cleanup on shutdown
     */
    public void dispose() throws JMSException {

        producerSession.close();
        cacheManager = null;
        messageProducer.close();
        getQueueSession.close();

        shutdown = true;

    }

    /**
     * Process a cache replication message.
     * 

* Unwraps the JMSEventMessage and performs the cache action *

* * @param message the message, which contains a payload and action * @param cache the cache to perform the action upon */ private void handleNotification(JMSEventMessage message, Ehcache cache) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("handleNotification ( message = " + message + " ) called "); } int event = message.getEvent(); switch (event) { case JMSEventMessage.PUT: put(cache, message.getElement()); break; case JMSEventMessage.REMOVE: remove(cache, message.getSerializableKey()); break; case JMSEventMessage.REMOVE_ALL: removeAll(cache); break; default: if (LOG.isLoggable(Level.FINE)) { LOG.severe(" Undefined action " + event); } } } /** * Process a non-cache message *

* Performs the cache action * * @param element the element which was sent over JMS in an ObjectMessage * @param cache the cache to perform the action upon * @param action the action to perform */ private void handleNotification(Element element, Serializable key, Ehcache cache, Action action) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("handleNotification ( element = " + element + " ) called "); } if (action.equals(Action.PUT)) { put(cache, element); } else if (action.equals(Action.REMOVE)) { remove(cache, key); } else if (action.equals(Action.REMOVE_ALL)) { removeAll(cache); } } /** * Process a non-cache message *

* Performs the cache action * * @param cache the cache to perform the action upon * @param action the action to perform */ private void handleNotification(Object object, Serializable key, Ehcache cache, Action action) { Element element = new Element(key, object); if (action.equals(Action.PUT)) { put(cache, element); } else if (action.equals(Action.REMOVE)) { remove(cache, key); } else if (action.equals(Action.REMOVE_ALL)) { removeAll(cache); } } private void removeAll(Ehcache cache) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("removeAll "); } cache.removeAll(true); } private void remove(Ehcache cache, Serializable key) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("remove ( key = " + key + " ) "); } cache.remove(key, true); } private void put(Ehcache cache, Element element) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("put ( element = " + element + " ) "); } cache.put(element, true); } /** * Send the cache peer with an ordered list of {@link net.sf.ehcache.distribution.EventMessage}s. *

* This enables multiple messages to be delivered in one network invocation. * * @param eventMessages a list of type {@link net.sf.ehcache.distribution.EventMessage} */ public void send(List eventMessages) throws RemoteException { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("send ( eventMessages = " + eventMessages + " ) called "); } for (Object eventMessage : eventMessages) { try { ObjectMessage message = producerSession.createObjectMessage((JMSEventMessage) eventMessage); messageProducer.send(message); } catch (JMSException e) { LOG.log(Level.SEVERE, e.getMessage(), e); throw new RemoteException(e.getMessage()); } } } /** * @param message a JMSMessage guaranteed to not be sent to the publishing CacheManager instance. */ public void onMessage(Message message) { assert !shutdown : "Peer is shutdown. " + message; try { if (message instanceof ObjectMessage) { handleObjectMessage(message); } else if (message instanceof TextMessage) { handleTextMessage(message); } else if (message instanceof BytesMessage) { handleBytesMessage(message); } else { throw new InvalidJMSMessageException("Cannot handle message of type (class=" + message.getClass().getName() + "). Notification ignored."); } } catch (Exception e) { LOG.log(Level.WARNING, "Unable to handle JMS Notification: " + e.getMessage(), e); } } private void handleObjectMessage(Message message) throws JMSException, RemoteException { ObjectMessage objectMessage = (ObjectMessage) message; Object object = objectMessage.getObject(); //If a non-cache publisher sends an Element if (object instanceof Element) { if (LOG.isLoggable(Level.FINE)) { LOG.fine(getName() + ": Element message received - " + object); } Element element = (Element) object; Cache cache = extractAndValidateCache(objectMessage); Action action = extractAndValidateAction(objectMessage); //not required for Element Serializable key = extractAndValidateKey(objectMessage, action); handleNotification(element, key, cache, action); } else if (object instanceof JMSEventMessage) { if (LOG.isLoggable(Level.FINE)) { LOG.fine(getName() + ": JMSEventMessage message received - " + object); } //no need for cacheName, mimeType, key or action properties as all are in message. JMSEventMessage jmsEventMessage = (JMSEventMessage) object; if (LOG.isLoggable(Level.FINE)) { LOG.fine(jmsEventMessage.toString()); } Cache cache; String cacheName; try { cacheName = jmsEventMessage.getCacheName(); cache = cacheManager.getCache(cacheName); assert cache != null : "Cache was null, which is an illegal state. " + jmsEventMessage; } catch (Exception e) { LOG.log(Level.SEVERE, e.getMessage(), e); return; } if (jmsEventMessage.getEvent() == Action.GET.toInt()) { handleGetRequest(objectMessage, jmsEventMessage, cache); } else { handleNotification(jmsEventMessage, cache); } } else { LOG.fine(getName() + ": Other ObjectMessage received - " + object); //no need for mimeType. An object has a type Cache cache = extractAndValidateCache(objectMessage); Action action = extractAndValidateAction(objectMessage); Serializable key = extractAndValidateKey(objectMessage, action); handleNotification(object, key, cache, action); } } private void handleTextMessage(Message message) throws RemoteException, JMSException { TextMessage textMessage = (TextMessage) message; if (LOG.isLoggable(Level.FINE)) { LOG.fine(getName() + ": Other ObjectMessage received - " + textMessage); } Cache cache = extractAndValidateCache(message); Action action = extractAndValidateAction(message); Serializable key = extractAndValidateKey(message, action); String mimeType = extractAndValidateMimeType(message, action); byte[] payload = new byte[0]; if (textMessage.getText() != null) { payload = textMessage.getText().getBytes(); } MimeTypeByteArray value = new MimeTypeByteArray(mimeType, payload); handleNotification(value, key, cache, action); } private void handleBytesMessage(Message message) throws RemoteException, JMSException { BytesMessage bytesMessage = (BytesMessage) message; if (LOG.isLoggable(Level.FINE)) { LOG.fine(getName() + ": Other ObjectMessage received - " + bytesMessage); } Cache cache = extractAndValidateCache(message); Action action = extractAndValidateAction(message); Serializable key = extractAndValidateKey(message, action); String mimeType = extractAndValidateMimeType(message, action); byte[] payload = new byte[(int) bytesMessage.getBodyLength()]; bytesMessage.readBytes(payload); MimeTypeByteArray value = new MimeTypeByteArray(mimeType, payload); handleNotification(value, key, cache, action); } private void handleGetRequest(ObjectMessage objectMessage, JMSEventMessage jmsEventMessage, Cache cache) throws JMSException { if (LOG.isLoggable(Level.FINE)) { LOG.fine(cacheManager.getName() + ": JMSEventMessage message received - " + objectMessage.getJMSMessageID()); } Serializable keyOrKeys = jmsEventMessage.getSerializableKey(); boolean collectionLoad = false; if (keyOrKeys instanceof ArrayList) { collectionLoad = true; } QueueSender replyQueueSender = null; try { Serializable value = loadKeyOrKeys(cache, keyOrKeys, collectionLoad); int localCacheManagerUid = JMSUtil.localCacheManagerUid(cache); LOG.log(Level.FINE, "Receiver CacheManager UID: {}", localCacheManagerUid); assert (objectMessage.getIntProperty(CACHE_MANAGER_UID) != localCacheManagerUid) : "The JMSCachePeer received a getQueue request sent by a JMSCacheLoader belonging to the same" + "CacheManager, which is invalid"; ObjectMessage reply = getQueueSession.createObjectMessage(value); String name = null; try { name = getName(); } catch (RemoteException e) { //impossible - local call } reply.setStringProperty("responder", name); reply.setJMSCorrelationID(objectMessage.getJMSMessageID()); Queue replyQueue = (Queue) objectMessage.getJMSReplyTo(); replyQueueSender = getQueueSession.createSender(replyQueue); replyQueueSender.send(reply); } finally { if (replyQueueSender != null) { replyQueueSender.close(); } } } private Serializable loadKeyOrKeys(Cache cache, Serializable keyOrKeys, boolean collectionLoad) { if (collectionLoad) { ArrayList keys = (ArrayList) keyOrKeys; return loadKeys(cache, keys); } else { return loadKey(cache, keyOrKeys); } } private Serializable loadKey(Cache cache, Serializable key) { Element element = cache.get(key); delayForTest(key); Serializable value = null; if (element != null) { value = element.getValue(); } return value; } private HashMap loadKeys(Cache cache, ArrayList keys) { HashMap responseMap = new HashMap(keys.size()); for (Object listKey : keys) { Serializable key = (Serializable) listKey; Element element = cache.get(listKey); Serializable value; if (element != null) { value = element.getValue(); responseMap.put(key, value); } } return responseMap; } /** * @param key */ private void delayForTest(Serializable key) { if (key.equals("net.sf.ehcache.distribution.jms.Delay")) { try { Thread.sleep(TEST_DELAY); } catch (InterruptedException e) { // } } } private Serializable extractAndValidateKey(Message message, Action action) throws JMSException { String key = message.getStringProperty(KEY_PROPERTY); if (key == null && action.equals(Action.REMOVE)) { throw new InvalidJMSMessageException("No key property specified. The key is required when the action is REMOVE."); } return key; } private String extractAndValidateMimeType(Message message, Action action) throws JMSException { String mimeType = message.getStringProperty(JMSEventMessage.MIME_TYPE_PROPERTY); if (mimeType == null && action.equals(Action.PUT)) { if (message instanceof TextMessage) { mimeType = "text/plain"; } else if (message instanceof BytesMessage) { mimeType = "application/octet-stream"; } if (LOG.isLoggable(Level.FINE)) { LOG.fine("mimeType property not set. Auto setting MIME Type for message " + message.getJMSMessageID() + " to " + mimeType); } } return mimeType; } private Action extractAndValidateAction(Message message) throws JMSException { String actionString = message.getStringProperty(ACTION_PROPERTY); Action action = Action.valueOf(actionString); if (actionString == null || action == null) { throw new InvalidJMSMessageException("No action specified. Must be one of PUT, REMOVE or REMOVE_ALL"); } return action; } private Cache extractAndValidateCache(Message message) throws JMSException { Cache cache; String cacheName = message.getStringProperty(CACHE_NAME_PROPERTY); if (cacheName == null) { throw new InvalidJMSMessageException("No cache name specified."); } cache = cacheManager.getCache(cacheName); if (cache == null) { throw new InvalidJMSMessageException("No cache named " + cacheName + "exists in the target CacheManager."); } return cache; } /** * Not implemented for JMS * * @param keys a list of serializable values which represent keys * @return a list of Elements. If an element was not found or null, it will not be in the list. */ public List getElements(List keys) throws RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS * * @return a String representation of the GUID * @throws RemoteException */ public String getGuid() throws RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS * * @return a list of {@link Object} keys */ public List getKeys() throws RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS */ public String getName() throws RemoteException { return cacheManager.getName() + " JMSCachePeer"; } /** * Not implemented for JMS * * @param key a serializable value * @return the element, or null, if it does not exist. */ public Element getQuiet(Serializable key) throws RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS * * @return the URL as a string */ public String getUrl() throws RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * The URL base for the remote replicator to connect. The value will have meaning * only to a specific implementation of replicator and remote peer. */ public String getUrlBase() throws RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS * * @param element the element to put * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE} * @throws IllegalArgumentException if the element is null */ public void put(Element element) throws IllegalArgumentException, IllegalStateException, RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS * * @param key the element key * @return true if the element was removed, false if it was not found in the cache * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE} */ public boolean remove(Serializable key) throws IllegalStateException, RemoteException { throw new RemoteException("Not implemented for JMS"); } /** * Not implemented for JMS * * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE} */ public void removeAll() throws RemoteException, IllegalStateException { throw new RemoteException("Not implemented for JMS"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy