com.oracle.coherence.patterns.messaging.MessageEngine Maven / Gradle / Ivy
Show all versions of coherence-messagingpattern Show documentation
/*
* File: MessageEngine.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* The contents of this file are subject to the terms and conditions of
* the Common Development and Distribution License 1.0 (the "License").
*
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License by consulting the LICENSE.txt file
* distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
*
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file LICENSE.txt.
*
* MODIFICATIONS:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*/
package com.oracle.coherence.patterns.messaging;
import com.oracle.coherence.common.finitestatemachines.AnnotationDrivenModel;
import com.oracle.coherence.common.finitestatemachines.Event;
import com.oracle.coherence.common.finitestatemachines.Instruction;
import com.oracle.coherence.common.finitestatemachines.NonBlockingFiniteStateMachine;
import com.oracle.coherence.common.finitestatemachines.NonBlockingFiniteStateMachine.CoalescedEvent;
import com.oracle.coherence.common.identifiers.Identifier;
import com.oracle.coherence.common.processors.InvokeMethodProcessor;
import com.oracle.coherence.common.threading.ExecutorServiceFactory;
import com.oracle.coherence.common.threading.ThreadFactories;
import com.oracle.coherence.patterns.messaging.entryprocessors.ExposeMessageToQueueProcessor;
import com.oracle.coherence.patterns.messaging.entryprocessors.RegisterSubscriptionsWithMessageProcessor;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.net.cache.ContinuousQueryCache;
import com.tangosol.util.Filter;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.filter.EqualsFilter;
import com.tangosol.util.processor.UpdaterProcessor;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
/**
* The finite state machine for coalescing of message event processing.
*
* Copyright (c) 2013. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author David Rowlands
*/
public class MessageEngine extends AbstractEngine
{
/**
* A continuous query cache used to optimize access to the {@link Topic} subscription lists.
*/
private volatile static ContinuousQueryCache cqcTopicCache = null;
/**
* Constructor for message finite state machine.
* @param destinationIdentifier identifier for destination.
*/
protected MessageEngine(Identifier destinationIdentifier)
{
super(destinationIdentifier);
ScheduledExecutorService executor =
ExecutorServiceFactory.newSingleThreadScheduledExecutor(ThreadFactories.newThreadFactory(true,
"MessagingEngine",
null));
m_fsm = new NonBlockingFiniteStateMachine("Message",
new AnnotationDrivenModel(State.class,
new Model()),
State.IDLE,
executor,
false);
}
/**
* Method description
*
* @param msgpublisher
*/
public void processRunEvent(MessagePublisher msgpublisher)
{
Event messageEvent = new MessageEvent(new StateEvent(State.RUNNING),
NonBlockingFiniteStateMachine.CoalescedEvent.Process
.MOST_RECENT,
msgpublisher,
msgpublisher);
m_fsm.process(messageEvent);
}
Instruction onRunning(Event event)
{
MessageTracker tracker = MessagesToExpose.getInstance().getTrackerSnapShot(getDestinationIdentifier());
MessagePublisher publisher = ((MessageEvent) event).getMessagePublisher();
if ((tracker != null) && (!tracker.isEmpty()))
{
if (publisher.isQueue())
{
exposeQueueMessageBatch(getDestinationIdentifier(), tracker);
}
else if (publisher.isTopic())
{
exposeTopicMessageBatch(getDestinationIdentifier(), tracker);
}
}
return new Instruction.TransitionTo(State.IDLE);
}
/**
* Expose the message batch to a queue or topic.
*
* @param destinationIdentifier destination identifier
* @param tracker messages to expose
*/
private void exposeQueueMessageBatch(Identifier destinationIdentifier,
MessageTracker tracker)
{
NamedCache destinations = CacheFactory.getCache(Destination.CACHENAME);
destinations.invoke(destinationIdentifier, new ExposeMessageToQueueProcessor(tracker));
ArrayList messageKeys = tracker.getMessageKeys(destinationIdentifier);
CacheFactory.getCache(Message.CACHENAME).invokeAll(messageKeys,
new UpdaterProcessor("setVisible", Boolean.TRUE));
}
/**
* Expose all of the messages identifiers in the delivery tracker to all of the topic subscriptions. Then call
* the "makeVisibleTo" method for each message.
*
* @param destinationIdentifier destination identifier
* @param tracker message tracker
*/
@SuppressWarnings("unchecked")
private void exposeTopicMessageBatch(Identifier destinationIdentifier,
MessageTracker tracker)
{
NamedCache messageCache = CacheFactory.getCache(Message.CACHENAME);
// Get the subscriptions to be notified about the messages.
NamedCache cqcTopicCache = getDestinationCache();
Destination destination = (Destination) cqcTopicCache.get(destinationIdentifier);
Set subscriptions = destination.getSubscriptionIdentifiers();
// If there are no subscriptions then delete the message we just put
// in the cache.
if (subscriptions.isEmpty())
{
Iterator iter = tracker.iterator();
while (iter.hasNext())
{
MessageIdentifier messageIdentifier = iter.next();
MessageKey messageKey = Message.getKey(destinationIdentifier, messageIdentifier);
messageCache.remove(messageKey);
}
return;
}
// Tell all of the subscriptions about the messages
NamedCache subscriptionCache = CacheFactory.getCache(Subscription.CACHENAME);
Map mapSubscriptions =
(Map) subscriptionCache.invokeAll(subscriptions,
new InvokeMethodProcessor("onAcceptMessage", new Object[] {
tracker}));
// build a set of the subscriptions that accepted the messages
HashSet activeSubscriptions = new HashSet();
for (SubscriptionIdentifier subscriptionIdentifier : mapSubscriptions.keySet())
{
if (mapSubscriptions.get(subscriptionIdentifier))
{
activeSubscriptions.add(subscriptionIdentifier);
}
}
// Call the makeVisibleTo method for each message to let the message know about the
// subscriptions that are consuming the message.
// INC-1033. There is a race condition where the subscription could ack the message before
// the message is visible. This is why a custom EntryProcessor is used here (it handles that case)
// The old code used and UpdaterProcessor which is not good enough.
ArrayList messageKeys = tracker.getMessageKeys(destinationIdentifier);
messageCache.invokeAll(messageKeys,
new RegisterSubscriptionsWithMessageProcessor(new SubscriptionIdentifierSet(activeSubscriptions)));
}
/**
* Get the continuous query cache for the {@link Topic}.
*
* @return continuous query cache topic cache
*/
private ContinuousQueryCache getDestinationCache()
{
if (cqcTopicCache == null)
{
// Create a continuous query cache for the topic so that the subscription list can be retrieved quickly
NamedCache cache = CacheFactory.getCache(Destination.CACHENAME);
Filter filter = new EqualsFilter(new ChainedExtractor("getClass.getName"), Topic.class.getName());
cqcTopicCache = new ContinuousQueryCache(cache, filter);
}
return cqcTopicCache;
}
/**
* Class description
*
* @param
*
* @version Enter version here..., 13/07/03
* @author Enter your name here...
*/
public static class MessageEvent> extends NonBlockingFiniteStateMachine.CoalescedEvent
{
private MessagePublisher publisher;
/**
* Constructs ...
*
* @param event the {@link Event} to be coalesced
* @param mode which {@link CoalescedEvent}s to process
* @param discriminator the descriminator used to uniquely coalesce the {@link Event}
* @param publisher the message publisher which is stored in the event is used in the event processing
*/
public MessageEvent(Event event,
Process mode,
Object discriminator,
MessagePublisher publisher)
{
super(event, mode, discriminator);
this.publisher = publisher;
}
/**
* Method description
*
* @return MessagePublisher
*/
public MessagePublisher getMessagePublisher()
{
return publisher;
}
}
}