001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.ksb.impl.bus.diff;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    
023    import javax.xml.namespace.QName;
024    
025    import org.apache.commons.collections.MapUtils;
026    import org.apache.log4j.Logger;
027    import org.kuali.rice.ksb.api.registry.ServiceInfo;
028    import org.kuali.rice.ksb.api.registry.ServiceRegistry;
029    import org.kuali.rice.ksb.impl.bus.LocalService;
030    import org.kuali.rice.ksb.impl.bus.RemoteService;
031    
032    /**
033     * Default implementation of the {@link ServiceRegistryDiffCalculator} which calculates
034     * differences between client service bus state and service registry state.
035     * 
036     * @author Kuali Rice Team (rice.collab@kuali.org)
037     *
038     */
039    public class ServiceRegistryDiffCalculatorImpl implements ServiceRegistryDiffCalculator {
040    
041            private static final Logger LOG = Logger.getLogger(ServiceRegistryDiffCalculatorImpl.class);
042            
043            private ServiceRegistry serviceRegistry;
044            
045            public void setServiceRegistry(ServiceRegistry serviceRegistry) {
046                    this.serviceRegistry = serviceRegistry;
047            }
048            
049            @Override
050            public CompleteServiceDiff diffServices(String instanceId, List<LocalService> localServices, List<RemoteService> clientRegistryCache) {
051            List<ServiceInfo> allRegistryServices = serviceRegistry.getAllOnlineServices();
052            List<ServiceInfo> allRegistryServicesForInstance = serviceRegistry.getAllServicesForInstance(instanceId);
053                    LocalServicesDiff localServicesDiff = calculateLocalServicesDiff(allRegistryServicesForInstance, instanceId, localServices);
054                    RemoteServicesDiff remoteServicesDiff = calculateRemoteServicesDiff(allRegistryServices, clientRegistryCache);
055                    return new CompleteServiceDiff(localServicesDiff, remoteServicesDiff);
056            }
057    
058            protected LocalServicesDiff calculateLocalServicesDiff(List<ServiceInfo> allRegistryServicesForInstance, String instanceId, List<LocalService> localServices) {
059                    
060                    List<ServiceInfo> servicesToRemoveFromRegistry = new ArrayList<ServiceInfo>();
061                    List<LocalService> localServicesToPublish = new ArrayList<LocalService>();
062                    Map<LocalService, ServiceInfo> localServicesToUpdate = new HashMap<LocalService, ServiceInfo>();
063                    
064                    Map<QName, LocalService> localServiceIndex = indexLocalServices(instanceId, localServices);
065                    for (ServiceInfo serviceInfo : allRegistryServicesForInstance) {
066                            // first validate that the service has a valid instance id
067                            if (!instanceId.equals(serviceInfo.getInstanceId())) {
068                                    throw new IllegalArgumentException("ServiceInfo given for local service diff does not have a valid instance id.  Should have been '" + instanceId + "' but was '" + serviceInfo.getInstanceId() + "'");
069                            }
070                            LocalService localService = localServiceIndex.get(serviceInfo.getServiceName());
071                            if (localService == null) {
072                                    // this means the registry has the service but there is no local service, it has been unregistered
073                                    servicesToRemoveFromRegistry.add(serviceInfo);
074                            } else {
075                                    // if the LocalService is not null, that means that it exists but it may have changed, or this may be the first time the service
076                                    // is being published upon startup in which case it's service id will be null
077                                    if (!localService.getServiceEndpoint().getInfo().equals(serviceInfo)) {
078                                            // if the service infos don't match, that means we need to re-publish our current copy of the local service
079                                            localServicesToUpdate.put(localService, serviceInfo);
080                                    }
081                                    // whether or not it matches, remove it from the index
082                                    localServiceIndex.remove(serviceInfo.getServiceName());
083                            }
084                    }
085                    // what's left in the localServiceIndex will be services that weren't in the registry at all, they need to be published
086                    localServicesToPublish.addAll(localServiceIndex.values());
087                    
088                    if (LOG.isDebugEnabled()) {
089                            LOG.info("For instance '" + instanceId + "', found " + servicesToRemoveFromRegistry.size() + " services to remove from registry, "+
090                                    localServicesToPublish.size() + " local services to publish");
091                    }
092                    
093                    return new LocalServicesDiff(servicesToRemoveFromRegistry, localServicesToPublish, localServicesToUpdate);
094                                    
095            }
096            
097            private Map<QName, LocalService> indexLocalServices(String instanceId, List<LocalService> localServices) {
098                    Map<QName, LocalService> localServiceIndex = new HashMap<QName, LocalService>(localServices.size());
099                    for (LocalService localService : localServices) {
100                            String localServiceInstanceId = localService.getServiceEndpoint().getInfo().getInstanceId(); 
101                            if (!instanceId.equals(localServiceInstanceId)) {
102                                    throw new IllegalStateException("Instance id of local service (" + localServiceInstanceId + ") does not match instance id given to the diff calculator (" + instanceId + ")");
103                            }
104                            localServiceIndex.put(localService.getServiceName(), localService);
105                    }
106                    return localServiceIndex;
107            }
108                    
109            protected RemoteServicesDiff calculateRemoteServicesDiff(List<ServiceInfo> allRegistryServices, List<RemoteService> clientRegistryCache) {
110    
111            Map<String, ServiceInfo> indexedRegistryServices = indexRegistryServices(allRegistryServices);
112                    Map<String, ServiceInfo> servicesToAddToClientRegistryCache = new HashMap<String, ServiceInfo>(indexedRegistryServices);
113                    List<RemoteService> servicesToRemoveFromClientRegistryCache = new ArrayList<RemoteService>();
114    
115                    for (RemoteService remoteService : clientRegistryCache) {
116                            ServiceInfo indexedRegistryService = indexedRegistryServices.get(remoteService.getServiceInfo().getServiceId());
117                            if (indexedRegistryService == null) {
118                                    servicesToRemoveFromClientRegistryCache.add(remoteService);
119                            } else {
120                                    if (!remoteService.getServiceInfo().getChecksum().equals(indexedRegistryService.getChecksum())) {
121                                            servicesToRemoveFromClientRegistryCache.add(remoteService);
122                                    } else {
123                                            servicesToAddToClientRegistryCache.remove(remoteService.getServiceInfo().getServiceId());
124                                    }
125                            }
126                    }
127                    
128                    if (LOG.isDebugEnabled()) {
129                            LOG.debug("For instance found " + servicesToRemoveFromClientRegistryCache.size() + " services to remove from client registry cache, "+
130                                    servicesToAddToClientRegistryCache.size() + " services to add to client registry cache");
131                    }
132                    
133                    return new RemoteServicesDiff(new ArrayList<ServiceInfo>(servicesToAddToClientRegistryCache.values()),
134                    servicesToRemoveFromClientRegistryCache);
135            }
136            
137            private Map<String, ServiceInfo> indexRegistryServices(List<ServiceInfo> allRegistryServices) {
138                    Map<String, ServiceInfo> indexedRegistryServices = new HashMap<String, ServiceInfo>(allRegistryServices.size());
139                    for (ServiceInfo serviceInfo : allRegistryServices) {
140                            indexedRegistryServices.put(serviceInfo.getServiceId(), serviceInfo);
141                    }
142                    return indexedRegistryServices;
143            }
144    
145    }