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 }