| /******************************************************************************* |
| * Copyright (c) 2008, 2010 VMware Inc. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * VMware Inc. - initial contribution |
| *******************************************************************************/ |
| |
| package org.eclipse.virgo.snaps.core.internal; |
| |
| import java.util.Collection; |
| import java.util.Dictionary; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| |
| import javax.servlet.ServletContext; |
| import javax.servlet.ServletException; |
| |
| import org.eclipse.virgo.medic.eventlog.EventLogger; |
| import org.eclipse.virgo.snaps.core.RequestRouter; |
| import org.eclipse.virgo.snaps.core.internal.Snap; |
| import org.eclipse.virgo.snaps.core.SnapRegistry; |
| import org.eclipse.virgo.snaps.core.internal.deployer.SnapFactory; |
| import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceEvent; |
| import org.osgi.framework.ServiceListener; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.osgi.util.tracker.ServiceTrackerCustomizer; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| final class SnapFactoryMonitor implements ServiceTrackerCustomizer<SnapFactory, Object> { |
| |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| private final BundleContext bundleContext; |
| |
| private final ServiceTracker<SnapFactory, Object> snapFactoryTracker; |
| |
| private final EventLogger eventLogger; |
| |
| private final SnapRegistry snapRegistry; |
| |
| public SnapFactoryMonitor(BundleContext bundleContext, EventLogger eventLogger, SnapRegistry snapRegistry) { |
| this.bundleContext = bundleContext; |
| this.snapFactoryTracker = new ServiceTracker<SnapFactory, Object>(bundleContext, SnapFactory.class, this); |
| this.eventLogger = eventLogger; |
| this.snapRegistry = snapRegistry; |
| } |
| |
| public void start() { |
| this.snapFactoryTracker.open(); |
| } |
| |
| public void stop() { |
| this.snapFactoryTracker.close(); |
| } |
| |
| public Object addingService(ServiceReference<SnapFactory> reference) { |
| SnapFactory snapFactory = this.bundleContext.getService(reference); |
| if (snapFactory != null) { |
| BundleContext snapBundleContext = reference.getBundle().getBundleContext(); |
| SnapBinder snapBinder = new SnapBinder(snapBundleContext, snapFactory, SnapHostDefinition.fromServiceReference(reference), |
| this.eventLogger, this.snapRegistry); |
| snapBinder.start(); |
| return snapBinder; |
| } |
| logger.warn("Unable to create SnapBinder due to missing SnapFactory"); |
| return null; |
| } |
| |
| public void modifiedService(ServiceReference<SnapFactory> reference, Object service) { |
| } |
| |
| public void removedService(ServiceReference<SnapFactory> reference, Object service) { |
| logger.info("Destroying SnapBinder for bundle '{}'", reference.getBundle()); |
| ((SnapBinder) service).destroy(); |
| } |
| |
| private static enum SnapLifecycleState { |
| AWAITING_INIT, |
| INIT_SUCCEEDED, |
| INIT_FAILED |
| } |
| |
| private static final class SnapBinder implements ServiceListener { |
| |
| private static final String SNAP_ORDER = "snap.order"; |
| |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| private final BundleContext context; |
| |
| private final SnapFactory factory; |
| |
| private final HostSelector hostSelector; |
| |
| private final Object hostStateMonitor = new Object(); |
| |
| private final Object snapStateMonitor = new Object(); |
| |
| private boolean queriedInitialHosts = false; |
| |
| private ServiceReference<ServletContext> hostReference; |
| |
| private final ServiceRegistrationTracker registrationTracker = new ServiceRegistrationTracker(); |
| |
| private final EventLogger eventLogger; |
| |
| private final SnapRegistry snapRegistry; |
| |
| private Snap snap; |
| |
| public SnapBinder(final BundleContext context, final SnapFactory factory, final SnapHostDefinition hostDefinition, final EventLogger eventLogger, final SnapRegistry snapRegistry) { |
| this.context = context; |
| this.factory = factory; |
| this.hostSelector = new HostSelector(hostDefinition, (String)context.getBundle().getHeaders().get("Module-Scope")); |
| this.eventLogger = eventLogger; |
| this.snapRegistry = snapRegistry; |
| } |
| |
| private void start() { |
| registerHostListener(); |
| } |
| |
| private void registerHostListener() { |
| try { |
| this.context.addServiceListener(this, "(objectClass=javax.servlet.ServletContext)"); |
| logger.info("Listening for hosts to be registered."); |
| searchForExistingHost(); |
| } catch (InvalidSyntaxException e) { |
| logger.error("Filter syntax invalid"); |
| } |
| } |
| |
| private void hostPublished(ServiceReference<ServletContext> hostReference) { |
| assert (!Thread.holdsLock(this.hostStateMonitor)); |
| |
| ServletContext servletContext = this.context.getService(hostReference); |
| if (servletContext != null) { |
| synchronized (this.hostStateMonitor) { |
| |
| Collection<ServiceReference<ServletContext>> references = new HashSet<ServiceReference<ServletContext>>(); |
| references.add(hostReference); |
| ServiceReference<ServletContext> matchedHost = this.hostSelector.selectHost(references); |
| |
| if (matchedHost == null) { |
| logger.info("Host {} did not match {} ", hostReference.getBundle().getSymbolicName(), |
| this.hostSelector.getHostDefinition().toString()); |
| return; |
| } |
| } |
| |
| Bundle hostBundle = hostReference.getBundle(); |
| |
| Host host = new Host(hostBundle, servletContext, new RequestRouter(this.snapRegistry, servletContext)); |
| |
| synchronized (this.snapStateMonitor) { |
| if (this.factory.hasSnap()) { |
| Snap snap = this.factory.getSnap(); |
| snap.addHost(host); |
| publishSnapService(snap, hostBundle); |
| } else { |
| SnapLifecycleState newState = SnapLifecycleState.INIT_FAILED; |
| Snap snap = this.factory.createSnap(host); |
| try { |
| logger.info("Initializing snap '{}'", snap.getContextPath()); |
| snap.init(); |
| |
| newState = SnapLifecycleState.INIT_SUCCEEDED; |
| |
| logger.info("Publishing snap '{}'", snap.getContextPath()); |
| publishSnapService(snap, hostBundle); |
| } catch (ServletException e) { |
| this.eventLogger.log(SnapsLogEvents.SNAP_INIT_FAILURE, SnapUtils.boundContextPath(servletContext.getContextPath(), snap.getContextPath())); |
| } finally { |
| if (newState == SnapLifecycleState.INIT_SUCCEEDED) { |
| this.snap = snap; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void publishSnapService(Snap snap, Bundle hostBundle) { |
| Hashtable<Object, Object> props = snap.getSnapProperties(); |
| Dictionary<String, Object> serviceProperties = new Hashtable<String, Object>(); |
| |
| for(Object key : props.keySet()){ |
| serviceProperties.put(key.toString(), props.get(key)); |
| } |
| |
| String snapOrder = (String) serviceProperties.get(SNAP_ORDER); |
| if (snapOrder != null) { |
| serviceProperties.put(Constants.SERVICE_RANKING, Integer.parseInt(snapOrder)); |
| } |
| serviceProperties.put("snap.host.id", Long.toString(hostBundle.getBundleId())); |
| serviceProperties.put("snap.context.path", snap.getContextPath()); |
| serviceProperties.put("snap.name", (String) this.context.getBundle().getHeaders().get("Bundle-Name")); |
| |
| ServiceRegistration<Snap> registration = this.context.registerService(Snap.class, snap, serviceProperties); |
| this.registrationTracker.track(registration); |
| logger.info("Published snap service for '{}'", snap.getContextPath()); |
| } |
| |
| private void destroy() { |
| try { |
| destroySnap(); |
| } finally { |
| unregisterHostListener(); |
| } |
| } |
| |
| private void unregisterHostListener() { |
| logger.info("No longer listening for hosts to be registered."); |
| try { |
| this.context.removeServiceListener(this); |
| } catch (IllegalStateException e) { |
| logger.warn("Could not remove host listener. Reason: " + e.getMessage()); |
| } |
| } |
| |
| public void serviceChanged(ServiceEvent event) { |
| synchronized (this.hostStateMonitor) { |
| while (!queriedInitialHosts) { |
| try { |
| this.hostStateMonitor.wait(); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| } |
| |
| int type = event.getType(); |
| @SuppressWarnings("unchecked") |
| ServiceReference<ServletContext> serviceReference = (ServiceReference<ServletContext>) event.getServiceReference(); |
| |
| if (type == ServiceEvent.REGISTERED && this.hostReference == null) { |
| hostPublished(serviceReference); |
| } else if (type == ServiceEvent.UNREGISTERING) { |
| if (serviceReference.equals(this.hostReference)) { |
| hostRetracted(serviceReference); |
| } |
| } |
| } |
| |
| private void hostRetracted(ServiceReference<ServletContext> serviceReference) { |
| try { |
| destroySnap(); |
| } finally { |
| synchronized (this.hostStateMonitor) { |
| this.hostReference = null; |
| } |
| } |
| } |
| |
| private void destroySnap() { |
| Snap s = null; |
| synchronized (this.snapStateMonitor) { |
| s = this.snap; |
| this.snap = null; |
| } |
| this.registrationTracker.unregisterAll(); |
| if(s != null) { |
| logger.info("Retracted snap service for '{}'", s.getContextPath()); |
| s.destroy(); |
| } |
| } |
| |
| private void searchForExistingHost() { |
| ServiceReference<ServletContext> existingHost = null; |
| Collection<ServiceReference<ServletContext>> candidates = findHostCandidiates(); |
| if (candidates != null && !candidates.isEmpty()) { |
| logger.info("{} host candidates found", candidates.size()); |
| } else { |
| logger.info("No host candidates found"); |
| } |
| |
| synchronized (this.hostStateMonitor) { |
| try { |
| existingHost = this.hostSelector.selectHost(candidates); |
| this.queriedInitialHosts = true; |
| } finally { |
| this.hostStateMonitor.notifyAll(); |
| } |
| } |
| if (existingHost != null) { |
| hostPublished(existingHost); |
| } |
| } |
| |
| private Collection<ServiceReference<ServletContext>> findHostCandidiates() { |
| try { |
| return this.context.getServiceReferences(ServletContext.class, null); |
| } catch (InvalidSyntaxException ise) { |
| throw new IllegalStateException("Unexpected invalid filter syntax with null filter", ise); |
| } |
| } |
| } |
| } |