Package org.rioproject.eventcollector.service

Source Code of org.rioproject.eventcollector.service.EventCollectorImpl$EventNotifier

/*
* 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 com.sun.jini.config.Config;
import com.sun.jini.landlord.*;
import com.sun.jini.start.LifeCycle;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.event.UnknownEventException;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.export.Exporter;
import net.jini.id.Uuid;
import net.jini.id.UuidFactory;
import net.jini.security.BasicProxyPreparer;
import net.jini.security.ProxyPreparer;
import org.rioproject.servicebean.ServiceBeanContext;
import org.rioproject.event.EventDescriptor;
import org.rioproject.impl.event.EventDescriptorFactory;
import org.rioproject.event.RemoteServiceEvent;
import org.rioproject.eventcollector.api.EventCollectorRegistration;
import org.rioproject.eventcollector.api.UnknownEventCollectorRegistration;
import org.rioproject.eventcollector.proxy.EventCollectorBackend;
import org.rioproject.eventcollector.proxy.Registration;
import org.rioproject.impl.servicebean.ServiceBeanActivation;
import org.rioproject.impl.servicebean.ServiceBeanAdapter;
import org.rioproject.log.ServiceLogEvent;
import org.rioproject.impl.client.JiniClient;
import org.rioproject.impl.service.LeaseListener;
import org.rioproject.impl.service.LeaseListenerAdapter;
import org.rioproject.impl.util.ThrowableUtil;
import org.rioproject.sla.SLAThresholdEvent;
import org.rioproject.impl.util.BannerProvider;
import org.rioproject.impl.util.BannerProviderImpl;
import org.rioproject.impl.util.TimeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

/**
* Service implementation of the {@code EventCollector}.
*
* @author Dennis Reedy
*/
public class EventCollectorImpl extends ServiceBeanAdapter implements EventCollectorBackend {
    private RegistrationManager registrationManager;
    private LeaseFactory leaseFactory;
    private ProxyPreparer listenerPreparer;
    private final Map<Uuid, RegisteredNotification> registrations = new ConcurrentHashMap<Uuid, RegisteredNotification>();
    private final ExecutorService execService = Executors.newSingleThreadExecutor();
    private LeasePeriodPolicy leasePolicy;
    private final LeaseListener leaseListener = new LeaseMonitor();
    private final LocalLandlord localLandlord = new LocalLandlordAdaptor();
    private EventManager eventManager;
    private EventCollectorBackend eventCollectorBackendRef;
    private ScheduledExecutorService leaseReaperScheduler;
    private final BlockingQueue<RemoteEvent> eventQ = new LinkedBlockingQueue<RemoteEvent>();
    private static final String CONFIG_COMPONENT = EventCollectorImpl.class.getPackage().getName();
    private static final Logger logger = LoggerFactory.getLogger(EventCollectorImpl.class.getName());

    /**
     * Simple constructor used if the {@code EventCollectorImpl} is created as a dynamic service.
     */
    @SuppressWarnings("unused")
    public EventCollectorImpl() {
        super();
    }

    /**
     * Create an {@code EventCollectorImpl} launched from the ServiceStarter framework
     *
     * @param configArgs Configuration arguments
     * @param lifeCycle Service lifecycle manager
     *
     * @throws Exception If the {@code EventCollectorImpl} cannot be created
     */
    @SuppressWarnings("unused")
    public EventCollectorImpl(String[] configArgs, LifeCycle lifeCycle) throws Exception {
        super();
        bootstrap(configArgs);
    }

    private void bootstrap(String[] configArgs) throws Exception {
        ServiceBeanContext context = ServiceBeanActivation.getServiceBeanContext(CONFIG_COMPONENT,
                                                                                 "Event Collector",
                                                                                 configArgs,
                                                                                 getClass().getClassLoader());
        BannerProvider bannerProvider =
            (BannerProvider)context.getConfiguration().getEntry(CONFIG_COMPONENT,
                                                                "bannerProvider",
                                                                BannerProvider.class,
                                                                new BannerProviderImpl());
        logger.info(bannerProvider.getBanner(context.getServiceElement().getName()));
        try {
            start(context);
            ServiceBeanActivation.LifeCycleManager lMgr =
                (ServiceBeanActivation.LifeCycleManager)context.getServiceBeanManager().getDiscardManager();
            lMgr.register(getServiceProxy(), context);
        } catch(Exception e) {
            logger.error("Register to LifeCycleManager", e);
            throw e;
        }
    }

    @Override
    public void initialize(final ServiceBeanContext context) throws Exception {
        super.initialize(context);
        final Configuration config = getConfiguration();
        /* 5 minute default Lease time */
        final long DEFAULT_LEASE_TIME = TimeConstants.FIVE_MINUTES;
        /* 1 day max lease time */
        final long DEFAULT_MAX_LEASE_TIME = TimeConstants.ONE_DAY;
        /* Get the Lease policy */
        leasePolicy = (LeasePeriodPolicy) Config.getNonNullEntry(config,
                                                                 CONFIG_COMPONENT,
                                                                 "leasePeriodPolicy",
                                                                 LeasePeriodPolicy.class,
                                                                 new FixedLeasePeriodPolicy(DEFAULT_MAX_LEASE_TIME,
                                                                                            DEFAULT_LEASE_TIME));

        /* Get the reaping interval */
        long reapingInterval;
        try {
            reapingInterval = Config.getLongEntry(config,
                                                  CONFIG_COMPONENT,
                                                  "reapingInterval",
                                                  TimeConstants.ONE_SECOND*10,
                                                  1,
                                                  Long.MAX_VALUE);

        } catch (ConfigurationException e) {
            logger.warn("Getting reapingInterval", e);
            reapingInterval = TimeConstants.ONE_SECOND*10;
        }

        if(logger.isDebugEnabled())
            logger.debug(String.format("Reaping interval set to %d", reapingInterval));
        leaseReaperScheduler = Executors.newSingleThreadScheduledExecutor();
        leaseReaperScheduler.scheduleAtFixedRate(new RegistrationLeaseReaper(), 0, reapingInterval, TimeUnit.MILLISECONDS);

        final StringBuilder pathBuilder = new StringBuilder();
        pathBuilder.append(System.getProperty("user.home")).append(File.separator).append(".rio");
        pathBuilder.append(File.separator).append("events");

        final File defaultPersistentDirectoryRoot = new File(pathBuilder.toString());
        File persistentDirectoryRoot;
        try {
            persistentDirectoryRoot = (File) config.getEntry(EventCollectorImpl.class.getPackage().getName(),
                                                             "persistentDirectoryRoot",
                                                             File.class,
                                                             defaultPersistentDirectoryRoot);
        } catch (ConfigurationException e) {
            persistentDirectoryRoot = defaultPersistentDirectoryRoot;
        }

        /* Get the ProxyPreparer for ServiceInstantiator instances */
        listenerPreparer = (ProxyPreparer)config.getEntry(CONFIG_COMPONENT,
                                                          "instantiatorPreparer",
                                                          ProxyPreparer.class,
                                                          new BasicProxyPreparer());

        eventManager = (EventManager) config.getEntry(CONFIG_COMPONENT,
                                                      "eventManager",
                                                      EventManager.class,
                                                      new PersistentEventManager());
        /*new TransientEventManager());*/
        execService.submit(new EventNotifier());


        final List<EventDescriptor> descList = new ArrayList<EventDescriptor>();
        descList.addAll(EventDescriptorFactory.createEventDescriptors("org.rioproject.monitor:monitor-api:5.0-M4",
                                                                      "org.rioproject.monitor.ProvisionFailureEvent",
                                                                      "org.rioproject.monitor.ProvisionMonitorEvent"));
        descList.add(SLAThresholdEvent.getEventDescriptor());
        descList.add(ServiceLogEvent.getEventDescriptor());

        final EventDescriptor[] eventDescriptors =
            (EventDescriptor[]) config.getEntry(CONFIG_COMPONENT,
                                                "eventDescriptors",
                                                EventDescriptor[].class,
                                                descList.toArray(new EventDescriptor[descList.size()]));

        final EventCollectorContext eventCollectorContext = new EventCollectorContext(config,
                                                                                      eventQ,
                                                                                      eventDescriptors,
                                                                                      context.getDiscoveryManagement(),
                                                                                      persistentDirectoryRoot);

        //Uuid serviceUuid = UuidFactory.create(serviceID.getMostSignificantBits(), serviceID.getLeastSignificantBits());
        final Uuid serviceUuid = UuidFactory.generate();
        leaseFactory = new LeaseFactory(eventCollectorBackendRef, serviceUuid);
        registrationManager = new RegistrationManager(eventCollectorContext);
        for(RegisteredNotification registeredNotification : registrationManager.getRegisteredNotifications(listenerPreparer)) {
            registrations.put(registeredNotification.getCookie(), registeredNotification);
        }
        eventManager.initialize(eventCollectorContext);
        final StringBuilder groups = new StringBuilder();
        for(String group : context.getServiceBeanConfig().getGroups()) {
            if(groups.length()>0)
                groups.append(", ");
            groups.append(group);
        }
        logger.info("{}: started [{}]", getServiceBeanContext().getServiceElement().getName(),
                    JiniClient.getDiscoveryAttributes(getServiceBeanContext()));
    }

    /**
     * Override parent's getAdmin to return custom service admin
     *
     * @return A EventCollectorAdminImpl instance
     */
    public Object getAdmin() {
        Object adminProxy = null;
        try {
            if(admin == null) {
                Exporter adminExporter = getAdminExporter();
                admin = new EventCollectorAdminImpl(this, adminExporter);
            }
            admin.setServiceBeanContext(getServiceBeanContext());
            adminProxy = admin.getServiceAdmin();
        } catch (Throwable t) {
            logger.error("Getting EventCollectorAdminImpl", t);
        }
        return adminProxy;
    }

    @Override
    protected Remote exportDo(Exporter exporter) throws Exception {
        if(exporter==null)
            throw new IllegalArgumentException("exporter is null");
        if(eventCollectorBackendRef==null) {
            eventCollectorBackendRef = (EventCollectorBackend) exporter.export(this);
        }
        return(eventCollectorBackendRef);
    }

    @Override
    public void destroy() {
        logger.info("{}: destroy() notification", getServiceBeanContext().getServiceElement().getName());
        for(Map.Entry<Uuid, RegisteredNotification> entry : registrations.entrySet()) {
            RegisteredNotification registeredNotification = entry.getValue();
            if(registeredNotification.getEventListener()!=null) {
                registeredNotification.setEventIndex(eventManager.getLastRecordedDate());
                registrationManager.update(registeredNotification);
            }
        }
        if(execService!=null) {
            execService.shutdownNow();
        }
        if(eventManager!=null) {
            eventManager.terminate();
        }
        if(leaseReaperScheduler!=null) {
            leaseReaperScheduler.shutdownNow();
        }
        super.destroy();
    }

    public EventCollectorRegistration register(final long duration) throws IOException, LeaseDeniedException {
        if (duration < 1 && duration != Lease.ANY)
            throw new IllegalArgumentException("Duration must be a positive value");
        Uuid registrationID = UuidFactory.generate();
        RegisteredNotification registeredNotification = new RegisteredNotification(registrationID);

        LeasePeriodPolicy.Result leasePeriod = leasePolicy.grant(registeredNotification, duration);
        Lease lease = leaseFactory.newLease(registeredNotification.getCookie(), leasePeriod.expiration);
        registeredNotification.setExpiration(leasePeriod.expiration);

        if(logger.isDebugEnabled()) {
            DateFormat formatter = new SimpleDateFormat("HH:mm:ss,SSS");
            long t1 = System.currentTimeMillis();
            long actual = lease.getExpiration()-t1;
            logger.debug(String.format("Lease duration requested: %d, granted, expires %s, actual: %d",
                                      duration, formatter.format(new Date(lease.getExpiration())), actual));
        }
        registrations.put(registrationID, registeredNotification);
        leaseListener.register(registeredNotification);
        return new Registration((EventCollectorBackend)getServiceProxy(), registrationID, lease);
    }

    public void enableDelivery(final Uuid uuid, final RemoteEventListener remoteEventListener)
        throws UnknownEventCollectorRegistration, IOException {
        if (remoteEventListener == null) {
            disableDelivery(uuid);
        } else {
            RemoteEventListener preparedListener = (RemoteEventListener) listenerPreparer.prepareProxy(remoteEventListener);
            if (logger.isTraceEnabled()) {
                logger.trace("prepared listener: {}", preparedListener);
            }
            RegisteredNotification registeredNotification = registrations.get(uuid);
            if(registeredNotification!=null) {
                registeredNotification.setRemoteEventListener(preparedListener);
                eventManager.historyNotification(registeredNotification);
            } else {
                logger.warn("Unable to enable delivery for unknown registration ID");
                throw new UnknownEventCollectorRegistration("Unable to enable delivery for unknown registration ID");
            }
        }
    }

    public void disableDelivery(Uuid uuid) throws UnknownEventCollectorRegistration, IOException {
        RegisteredNotification registeredNotification = registrations.get(uuid);
        if(registeredNotification!=null) {
            registeredNotification.setRemoteEventListener(null);
            registeredNotification.setEventIndex(eventManager.getLastRecordedDate());
        } else {
            logger.warn("Unable to disable delivery for unknown registration ID");
            throw new UnknownEventCollectorRegistration("Unable to disable delivery for unknown registration ID");
        }
    }

    public long renew(Uuid uuid, long duration) throws LeaseDeniedException, UnknownLeaseException {
        return renewDo(uuid, duration);
    }

    public void cancel(Uuid uuid) throws UnknownLeaseException {
        cancelDo(uuid);
    }

    public Landlord.RenewResults renewAll(Uuid[] uuids, long[] extensions)  {
        return LandlordUtil.renewAll(localLandlord, uuids, extensions);
    }

    public Map cancelAll(Uuid[] uuids) {
        for(Uuid uuid : uuids) {
            RegisteredNotification registeredNotification = registrations.remove(uuid);
            if(registeredNotification!=null) {
                leaseListener.removed(registeredNotification);
            }
        }
        return LandlordUtil.cancelAll(localLandlord, uuids);
    }


    int delete(Collection<RemoteServiceEvent> events) {
        logger.info(String.format("Delete %d events", events.size()));
        return eventManager.delete(events);
    }

    /*
     * Added for testing support
     */
    EventManager getEventManager() {
        return eventManager;
    }

    private long renewDo(Uuid uuid, long duration) throws LeaseDeniedException, UnknownLeaseException {
        RegisteredNotification registeredNotification = registrations.get(uuid);
        LeasePeriodPolicy.Result result;
        if(registeredNotification!=null) {
            result = leasePolicy.renew(registeredNotification, duration);
            registeredNotification.setExpiration(result.expiration);
            registrations.put(uuid, registeredNotification);
            leaseListener.renewed(registeredNotification);
        } else {
            throw new UnknownLeaseException("Unable to renew unknown lease");
        }
        return result.duration;
    }

    private void cancelDo(Uuid uuid) throws UnknownLeaseException {
        RegisteredNotification registeredNotification = registrations.get(uuid);
        if(registeredNotification!=null) {
            registrations.remove(uuid);
            leaseListener.removed(registeredNotification);
        } else {
            throw new UnknownLeaseException("Unable to cancel unknown lease");
        }
    }

    /**
     * Handles notifying registered consumers.
     */
    class EventNotifier implements Runnable {

        public void run() {
            while (true) {
                RemoteEvent event;
                try {
                    logger.info("EventNotifier waiting for an event");
                    event = eventQ.take();
                } catch (InterruptedException e) {
                    logger.warn("EventNotifier breaking out of main loop");
                    break;
                }
                List<Uuid> removals = new ArrayList<Uuid>();
                if(event!=null) {
                    for(Map.Entry<Uuid, RegisteredNotification> entry : registrations.entrySet()) {
                        if(entry.getValue().getEventListener()!=null) {
                            RegisteredNotification registeredNotification = entry.getValue();
                            if(!registeredNotification.getHistoryUpdating()) {
                                try {
                                    registeredNotification.getEventListener().notify(event);
                                } catch (UnknownEventException e) {
                                    logger.warn("UnknownEventException return from listener", e);
                                } catch (RemoteException e) {
                                    if(!ThrowableUtil.isRetryable(e)) {
                                        if(logger.isTraceEnabled()) {
                                            logger.warn("Unrecoverable RemoteException returned from listener", e);
                                        } else {
                                            logger.warn(String.format("Unrecoverable RemoteException returned from listener: %s",
                                                                      e.getMessage()));
                                        }
                                        removals.add(entry.getKey());
                                    }
                                }
                            } else {
                                registeredNotification.addMissedEvent(event);
                            }
                        }
                    }

                    /* Remove registrations if notifications have failed */
                    for(Uuid uuid : removals) {
                        RegisteredNotification registeredNotification = registrations.remove(uuid);
                        if(registeredNotification!=null) {
                            leaseListener.removed(registeredNotification);
                        }
                    }
                }
            }
        }
    }

    /*
     * Added for test access.
     */
    int getRegistrationSize() {
        return registrations.size();
    }

    /**
     * Adaptor class implementation of LocalLandlord. We use this adaptor class with LandlordUtil.
     */
    private class LocalLandlordAdaptor implements LocalLandlord {

        public long renew(Uuid cookie, long extension) throws LeaseDeniedException, UnknownLeaseException {
            return renewDo(cookie, extension);
        }

        public void cancel(Uuid cookie) throws UnknownLeaseException {
            cancelDo(cookie);
        }
    }

    /**
     * Listens for lease transition notifications.
     */
    class LeaseMonitor extends LeaseListenerAdapter {
        @Override
        public void register(final LeasedResource resource) {
            logger.info(String.format("Added registration, count now [%d]", registrations.size()));
            registrationManager.persist((RegisteredNotification)resource);
        }

        @Override
        public void expired(final LeasedResource resource) {
            logger.info(String.format("Expired registration, count now [%d]", registrations.size()));
            registrationManager.remove((RegisteredNotification)resource);
        }

        @Override
        public void removed(final LeasedResource resource) {
            logger.info(String.format("Removed registration, count now [%d]", registrations.size()));
            registrationManager.remove((RegisteredNotification)resource);
        }

        @Override
        public void renewed(LeasedResource resource) {
            registrationManager.update((RegisteredNotification)resource);
        }
    }

    /**
     * Periodically checks for lease validity.
     */
    class RegistrationLeaseReaper implements Runnable {

        public void run() {
            Set<Map.Entry<Uuid, RegisteredNotification>> mapEntries = registrations.entrySet();
            for (Map.Entry<Uuid, RegisteredNotification> entry : mapEntries) {
                RegisteredNotification registeredNotification = entry.getValue();
                if (!ensure(registeredNotification)) {
                    if (logger.isDebugEnabled()) {
                        logger.warn("Lease expired for resource, cookie {}", registeredNotification.getCookie());
                    }
                    registrations.remove(entry.getKey());
                    leaseListener.removed(registeredNotification);
                }
            }
        }

        private boolean ensure(final LeasedResource resource) {
            return(resource.getExpiration() > System.currentTimeMillis());
        }
    }

}
TOP

Related Classes of org.rioproject.eventcollector.service.EventCollectorImpl$EventNotifier

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.