/*
* Copyright to the original author or authors
*
* 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 org.rioproject.eventcollector.service;
import net.jini.core.entry.Entry;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.UnknownEventException;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.LookupCache;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.lookup.ServiceDiscoveryManager;
import org.rioproject.impl.event.BasicEventConsumer;
import org.rioproject.event.EventDescriptor;
import org.rioproject.event.RemoteServiceEvent;
import org.rioproject.event.RemoteServiceEventListener;
import org.rioproject.impl.client.ServiceDiscoveryAdapter;
import org.rioproject.impl.util.ThrowableUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Provides basic support for an {@code EventManager}.
*
* @author Dennis Reedy
*/
public abstract class AbstractEventManager implements RemoteServiceEventListener, EventManager {
private static final Logger logger = LoggerFactory.getLogger(AbstractEventManager.class.getName());
private BlockingQueue<RemoteEvent> eventQ;
private final List<EventProducerManager> eventProducerManagers = new ArrayList<EventProducerManager>();
private final ExecutorService execService = Executors.newCachedThreadPool();
public void initialize(final EventCollectorContext context) throws Exception {
if(context==null)
throw new IllegalArgumentException("context must not be null");
this.eventQ = context.getEventQueue();
ServiceDiscoveryManager sdm = new ServiceDiscoveryManager(context.getDiscoveryManager(),
new LeaseRenewalManager(context.getConfiguration()),
context.getConfiguration());
for(EventDescriptor eventDescriptor : context.getEventDescriptors()) {
ServiceTemplate template = new ServiceTemplate(null, null, new Entry[]{eventDescriptor});
LookupCache lCache = sdm.createLookupCache(template, null, null);
EventProducerManager eventProducerManager = new EventProducerManager(eventDescriptor, this, lCache);
lCache.addListener(eventProducerManager);
eventProducerManagers.add(eventProducerManager);
}
}
public void terminate() {
for(EventProducerManager eventProducerManager : eventProducerManagers) {
eventProducerManager.terminate();
}
}
public void historyNotification(final RegisteredNotification registeredNotification) {
if(registeredNotification==null)
throw new IllegalArgumentException("The RegisteredNotification must not be null");
execService.submit(new EventHistoryNotifier(registeredNotification));
}
public void notify(RemoteServiceEvent event) {
if(logger.isDebugEnabled())
logger.debug(event.toString());
eventQ.offer(event);
postNotify(event);
}
public abstract void postNotify(RemoteServiceEvent event);
protected ExecutorService getExecutorService() {
return execService;
}
/**
* The EventProducerManager responds to serviceAdded transitions for
* EventProducer instances which match the EventDescriptor
*/
class EventProducerManager extends ServiceDiscoveryAdapter {
final LookupCache lookupCache;
BasicEventConsumer eventConsumer;
final EventDescriptor eventDescriptor;
final RemoteServiceEventListener eventListener;
EventProducerManager(final EventDescriptor eventDescriptor,
final RemoteServiceEventListener eventListener,
final LookupCache lookupCache) throws Exception {
this.lookupCache = lookupCache;
this.eventDescriptor = eventDescriptor;
this.eventListener = eventListener;
}
/**
* An EventProducer has been added
*/
public void serviceAdded(final ServiceDiscoveryEvent sdEvent) {
ServiceItem item = sdEvent.getPostEventServiceItem();
try {
if(item != null && item.service != null) {
if(logger.isInfoEnabled()) {
String name = item.service.getClass().getName();
if(logger.isDebugEnabled()) {
logger.debug(String.format("EventProducer discovered %s for EventDescriptor %s",
name, eventDescriptor.toString()));
}
}
if(eventConsumer==null) {
final EventDescriptor eventDescriptorToUse;
if(eventDescriptor.eventID==null) {
eventDescriptorToUse = getEventDescriptor(item.attributeSets, eventDescriptor);
} else {
eventDescriptorToUse = eventDescriptor;
}
eventConsumer = new BasicEventConsumer(eventDescriptorToUse, eventListener);
}
eventConsumer.register(item);
ServiceFaultListener faultListener = new ServiceFaultListener(item.serviceID,
lookupCache,
eventConsumer);
lookupCache.addListener(faultListener);
} else {
logger.warn("Unable to register EventProducer {}", item);
}
} catch(Exception e) {
logger.error("Adding EventProducer", e);
}
}
void terminate() {
if(eventConsumer!=null)
eventConsumer.terminate();
}
EventDescriptor getEventDescriptor(Entry[] attributes, EventDescriptor toMatch) {
EventDescriptor matchedDescriptor = null;
for(Entry entry : attributes) {
if(entry instanceof EventDescriptor) {
if(((EventDescriptor)entry).matches(toMatch)) {
matchedDescriptor = (EventDescriptor)entry;
break;
}
}
}
return matchedDescriptor;
}
}
/**
* Manage service failure notifications
*/
class ServiceFaultListener extends ServiceDiscoveryAdapter {
final ServiceID serviceID;
final LookupCache lookupCache;
final BasicEventConsumer eventConsumer;
ServiceFaultListener(final ServiceID serviceID, LookupCache lookupCache, final BasicEventConsumer eventConsumer) {
this.serviceID = serviceID;
this.lookupCache = lookupCache;
this.eventConsumer = eventConsumer;
}
/**
* An EventProducer has been removed
*/
public void serviceRemoved(final ServiceDiscoveryEvent sdEvent) {
ServiceItem item = sdEvent.getPreEventServiceItem();
if(item.service != null) {
if(item.serviceID.equals(serviceID)) {
if(logger.isTraceEnabled()) {
String name = item.service.getClass().getName();
logger.trace("EventProducer removed {}", name);
}
terminate();
}
} else {
logger.error("Unable to deregister EventProducer {}, unknown service", item);
}
}
/**
* Stop listening and remove from local tables
*/
void terminate() {
lookupCache.removeListener(this);
eventConsumer.deregister(serviceID);
}
}
/**
* This class will read the history and perform the notification to the newly registered listener
*/
class EventHistoryNotifier implements Runnable {
final RegisteredNotification registeredNotification;
EventHistoryNotifier(final RegisteredNotification registeredNotification) {
this.registeredNotification = registeredNotification;
}
public void run() {
try {
registeredNotification.setHistoryUpdating(true);
for(RemoteEvent event : getEvents(registeredNotification.getEventIndex())) {
if(!doNotify(event)) {
break;
}
}
} finally {
registeredNotification.setHistoryUpdating(false);
for(RemoteEvent event : registeredNotification.getAndClearMissedEvents()) {
if(!doNotify(event)) {
break;
}
}
}
}
private boolean doNotify(RemoteEvent event) {
boolean success = false;
try {
if(!registeredNotification.isUnknown(event))
registeredNotification.getEventListener().notify(event);
success = true;
} catch (UnknownEventException e) {
logger.warn("UnknownEventException return from listener", e);
registeredNotification.addUnknown(event);
} catch (RemoteException e) {
logger.warn("RemoteException return from listener", e);
if(!ThrowableUtil.isRetryable(e)) {
logger.warn(String.format("Unrecoverable exception from %s", registeredNotification));
}
}
return success;
}
}
}