001/** 002 * Copyright 2005-2017 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 */ 016package org.kuali.rice.ksb.impl.bus.diff; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import javax.xml.namespace.QName; 024 025import org.apache.commons.collections.MapUtils; 026import org.apache.log4j.Logger; 027import org.kuali.rice.ksb.api.registry.ServiceInfo; 028import org.kuali.rice.ksb.api.registry.ServiceRegistry; 029import org.kuali.rice.ksb.impl.bus.LocalService; 030import 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 */ 039public 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 StringBuffer errorMessage = new StringBuffer("ServiceInfo given for local service diff does not have a valid instance id. Should have been '" + instanceId + "' but was '" + serviceInfo.getInstanceId() + "'"); 069 if (serviceInfo.getInstanceId() == null) { 070 errorMessage.append(" Null instanceIds can be the result of multiple asm.jars or none in the classpath."); 071 } 072 throw new IllegalArgumentException(errorMessage.toString()); 073 } 074 LocalService localService = localServiceIndex.get(serviceInfo.getServiceName()); 075 if (localService == null) { 076 // this means the registry has the service but there is no local service, it has been unregistered 077 servicesToRemoveFromRegistry.add(serviceInfo); 078 } else { 079 // 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 080 // is being published upon startup in which case it's service id will be null 081 if (!localService.getServiceEndpoint().getInfo().equals(serviceInfo)) { 082 // if the service infos don't match, that means we need to re-publish our current copy of the local service 083 localServicesToUpdate.put(localService, serviceInfo); 084 } 085 // whether or not it matches, remove it from the index 086 localServiceIndex.remove(serviceInfo.getServiceName()); 087 } 088 } 089 // what's left in the localServiceIndex will be services that weren't in the registry at all, they need to be published 090 localServicesToPublish.addAll(localServiceIndex.values()); 091 092 if (LOG.isDebugEnabled()) { 093 LOG.info("For instance '" + instanceId + "', found " + servicesToRemoveFromRegistry.size() + " services to remove from registry, "+ 094 localServicesToPublish.size() + " local services to publish"); 095 } 096 097 return new LocalServicesDiff(servicesToRemoveFromRegistry, localServicesToPublish, localServicesToUpdate); 098 099 } 100 101 private Map<QName, LocalService> indexLocalServices(String instanceId, List<LocalService> localServices) { 102 Map<QName, LocalService> localServiceIndex = new HashMap<QName, LocalService>(localServices.size()); 103 for (LocalService localService : localServices) { 104 String localServiceInstanceId = localService.getServiceEndpoint().getInfo().getInstanceId(); 105 if (!instanceId.equals(localServiceInstanceId)) { 106 throw new IllegalStateException("Instance id of local service (" + localServiceInstanceId + ") does not match instance id given to the diff calculator (" + instanceId + ")"); 107 } 108 localServiceIndex.put(localService.getServiceName(), localService); 109 } 110 return localServiceIndex; 111 } 112 113 protected RemoteServicesDiff calculateRemoteServicesDiff(List<ServiceInfo> allRegistryServices, List<RemoteService> clientRegistryCache) { 114 115 Map<String, ServiceInfo> indexedRegistryServices = indexRegistryServices(allRegistryServices); 116 Map<String, ServiceInfo> servicesToAddToClientRegistryCache = new HashMap<String, ServiceInfo>(indexedRegistryServices); 117 List<RemoteService> servicesToRemoveFromClientRegistryCache = new ArrayList<RemoteService>(); 118 119 for (RemoteService remoteService : clientRegistryCache) { 120 ServiceInfo indexedRegistryService = indexedRegistryServices.get(remoteService.getServiceInfo().getServiceId()); 121 if (indexedRegistryService == null) { 122 servicesToRemoveFromClientRegistryCache.add(remoteService); 123 } else { 124 if (!remoteService.getServiceInfo().getChecksum().equals(indexedRegistryService.getChecksum())) { 125 servicesToRemoveFromClientRegistryCache.add(remoteService); 126 } else { 127 servicesToAddToClientRegistryCache.remove(remoteService.getServiceInfo().getServiceId()); 128 } 129 } 130 } 131 132 if (LOG.isDebugEnabled()) { 133 LOG.debug("For instance found " + servicesToRemoveFromClientRegistryCache.size() + " services to remove from client registry cache, "+ 134 servicesToAddToClientRegistryCache.size() + " services to add to client registry cache"); 135 } 136 137 return new RemoteServicesDiff(new ArrayList<ServiceInfo>(servicesToAddToClientRegistryCache.values()), 138 servicesToRemoveFromClientRegistryCache); 139 } 140 141 private Map<String, ServiceInfo> indexRegistryServices(List<ServiceInfo> allRegistryServices) { 142 Map<String, ServiceInfo> indexedRegistryServices = new HashMap<String, ServiceInfo>(allRegistryServices.size()); 143 for (ServiceInfo serviceInfo : allRegistryServices) { 144 indexedRegistryServices.put(serviceInfo.getServiceId(), serviceInfo); 145 } 146 return indexedRegistryServices; 147 } 148 149}