// Path: src/main/java/net/unicon/cas/addons/support/ResourceChangeDetectingEventNotifier.java
// public class ResourceChangeDetectingEventNotifier implements ApplicationEventPublisherAware {
// 
//     /**
//      * Application event representing the resource contents change.
//      * Intended to be processed by subscribed <code>ApplicationListener</code>s
//      * managed by ApplicationContext.
//      */
//     public static class ResourceChangedEvent extends ApplicationEvent {
//         private static final long serialVersionUID = 1L;
// 
//         private final URI resourceUri;
// 
//         public ResourceChangedEvent(final Object source, final URI resourceUri) {
//             super(source);
//             this.resourceUri = resourceUri;
//         }
// 
//         public URI getResourceUri() {
//             return this.resourceUri;
//         }
//     }
// 
//     private ApplicationEventPublisher applicationEventPublisher;
// 
//     private final Resource watchedResource;
// 
//     private volatile String resourceSha1Hex;
// 
//     private static final Logger logger = LoggerFactory.getLogger(ResourceChangeDetectingEventNotifier.class);
// 
//     private static final String EMPTY_STRING_SHA1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
// 
//     public ResourceChangeDetectingEventNotifier(final Resource watchedResource) throws Exception {
//         this.watchedResource = watchedResource;
//         if (!this.watchedResource.exists()) {
//             throw new BeanCreationException(String.format("The 'watchedResource' [%s] must point to an existing resource. " +
//                     "Please double-check that such resource exists.", this.watchedResource.getURI()));
//         }
//         //Initial SHA1 of the resource
//         //TODO: currently assumes file-based resources. Think about refactoring later to support diff kinds of resources?
//         final String initialSha1Hex = new Sha1Hash(this.watchedResource.getFile()).toHex();
//         if (EMPTY_STRING_SHA1.equals(initialSha1Hex)) {
//             logger.warn("The 'watchedResource' [{}] is empty!", this.watchedResource.getURI());
//         }
//         this.resourceSha1Hex = initialSha1Hex;
//     }
// 
//     /**
//      * Compare the SHA1 digests (since last check and the latest) of the configured resource, and if change is detected,
//      * publish the <code>ResourceChangeEvent</code> to ApplicationContext.
//      */
//     public void notifyOfTheResourceChangeEventIfNecessary() {
//         final String currentResourceSha1 = this.resourceSha1Hex;
//         String newResourceSha1 = null;
//         try {
//             newResourceSha1 = new Sha1Hash(this.watchedResource.getFile()).toHex();
//             if (!newResourceSha1.equals(currentResourceSha1)) {
//                 logger.debug("Resource: [{}] | Old Hash: [{}] | New Hash: [{}]", new Object[] {this.watchedResource.getURI(), currentResourceSha1, newResourceSha1});
//                 synchronized (this.resourceSha1Hex) {
//                     this.resourceSha1Hex = newResourceSha1;
//                     this.applicationEventPublisher.publishEvent(new ResourceChangedEvent(this, this.watchedResource.getURI()));
//                 }
//             }
//         }
//         catch (final Throwable e) {
//             //TODO: Possibly introduce an exception handling strategy?
//             logger.error("An exception is caught during 'watchedResource' access", e);
//             return;
//         }
//     }
// 
//     @Override
//     public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
//         this.applicationEventPublisher = applicationEventPublisher;
//     }
// }


import com.fasterxml.jackson.databind.ObjectMapper;
import net.unicon.cas.addons.support.GuardedBy;
import net.unicon.cas.addons.support.ResourceChangeDetectingEventNotifier;
import net.unicon.cas.addons.support.ThreadSafe;
import org.jasig.cas.services.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
package net.unicon.cas.addons.serviceregistry;





/**
 * Implementation of <code>ServiceRegistryDao</code> that reads services definition from JSON configuration file at the Spring Application Context
 * initialization time. After un-marshaling services from JSON blob, delegates the storage and retrieval to <code>InMemoryServiceRegistryDaoImpl</code>
 * <p/>
 * This class implements ${link ApplicationListener<ResourceChangeDetectingEventNotifier.ResourceChangedEvent>} to reload services definitions in real-time.
 * This class is thread safe.
 *
 * @author Dmitriy Kopylenko
 * @author Unicon, inc.
 * @since 0.8
 */
@ThreadSafe
public class JsonServiceRegistryDao implements ServiceRegistryDao,
// Path: src/main/java/net/unicon/cas/addons/support/ResourceChangeDetectingEventNotifier.java
// public class ResourceChangeDetectingEventNotifier implements ApplicationEventPublisherAware {
// 
//     /**
//      * Application event representing the resource contents change.
//      * Intended to be processed by subscribed <code>ApplicationListener</code>s
//      * managed by ApplicationContext.
//      */
//     public static class ResourceChangedEvent extends ApplicationEvent {
//         private static final long serialVersionUID = 1L;
// 
//         private final URI resourceUri;
// 
//         public ResourceChangedEvent(final Object source, final URI resourceUri) {
//             super(source);
//             this.resourceUri = resourceUri;
//         }
// 
//         public URI getResourceUri() {
//             return this.resourceUri;
//         }
//     }
// 
//     private ApplicationEventPublisher applicationEventPublisher;
// 
//     private final Resource watchedResource;
// 
//     private volatile String resourceSha1Hex;
// 
//     private static final Logger logger = LoggerFactory.getLogger(ResourceChangeDetectingEventNotifier.class);
// 
//     private static final String EMPTY_STRING_SHA1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
// 
//     public ResourceChangeDetectingEventNotifier(final Resource watchedResource) throws Exception {
//         this.watchedResource = watchedResource;
//         if (!this.watchedResource.exists()) {
//             throw new BeanCreationException(String.format("The 'watchedResource' [%s] must point to an existing resource. " +
//                     "Please double-check that such resource exists.", this.watchedResource.getURI()));
//         }
//         //Initial SHA1 of the resource
//         //TODO: currently assumes file-based resources. Think about refactoring later to support diff kinds of resources?
//         final String initialSha1Hex = new Sha1Hash(this.watchedResource.getFile()).toHex();
//         if (EMPTY_STRING_SHA1.equals(initialSha1Hex)) {
//             logger.warn("The 'watchedResource' [{}] is empty!", this.watchedResource.getURI());
//         }
//         this.resourceSha1Hex = initialSha1Hex;
//     }
// 
//     /**
//      * Compare the SHA1 digests (since last check and the latest) of the configured resource, and if change is detected,
//      * publish the <code>ResourceChangeEvent</code> to ApplicationContext.
//      */
//     public void notifyOfTheResourceChangeEventIfNecessary() {
//         final String currentResourceSha1 = this.resourceSha1Hex;
//         String newResourceSha1 = null;
//         try {
//             newResourceSha1 = new Sha1Hash(this.watchedResource.getFile()).toHex();
//             if (!newResourceSha1.equals(currentResourceSha1)) {
//                 logger.debug("Resource: [{}] | Old Hash: [{}] | New Hash: [{}]", new Object[] {this.watchedResource.getURI(), currentResourceSha1, newResourceSha1});
//                 synchronized (this.resourceSha1Hex) {
//                     this.resourceSha1Hex = newResourceSha1;
//                     this.applicationEventPublisher.publishEvent(new ResourceChangedEvent(this, this.watchedResource.getURI()));
//                 }
//             }
//         }
//         catch (final Throwable e) {
//             //TODO: Possibly introduce an exception handling strategy?
//             logger.error("An exception is caught during 'watchedResource' access", e);
//             return;
//         }
//     }
// 
//     @Override
//     public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
//         this.applicationEventPublisher = applicationEventPublisher;
//     }
// }


// Path: src/main/java/net/unicon/cas/addons/serviceregistry/JsonServiceRegistryDao.java
import com.fasterxml.jackson.databind.ObjectMapper;
import net.unicon.cas.addons.support.GuardedBy;
import net.unicon.cas.addons.support.ResourceChangeDetectingEventNotifier;
import net.unicon.cas.addons.support.ThreadSafe;
import org.jasig.cas.services.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

package net.unicon.cas.addons.serviceregistry;





/**
 * Implementation of <code>ServiceRegistryDao</code> that reads services definition from JSON configuration file at the Spring Application Context
 * initialization time. After un-marshaling services from JSON blob, delegates the storage and retrieval to <code>InMemoryServiceRegistryDaoImpl</code>
 * <p/>
 * This class implements ${link ApplicationListener<ResourceChangeDetectingEventNotifier.ResourceChangedEvent>} to reload services definitions in real-time.
 * This class is thread safe.
 *
 * @author Dmitriy Kopylenko
 * @author Unicon, inc.
 * @since 0.8
 */
@ThreadSafe
public class JsonServiceRegistryDao implements ServiceRegistryDao,
		ApplicationListener<ResourceChangeDetectingEventNotifier.ResourceChangedEvent> {