001 /* 002 * Copyright 2005-2009 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); you may not use this file except in 005 * compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.opensource.org/licenses/ecl2.php 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS 010 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 011 * language governing permissions and limitations under the License. 012 */ 013 package org.kuali.rice.krad.service.impl; 014 015 import com.google.common.collect.MapMaker; 016 import org.kuali.rice.core.api.component.Component; 017 import org.kuali.rice.core.api.namespace.Namespace; 018 import org.kuali.rice.core.api.namespace.NamespaceService; 019 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 020 import org.kuali.rice.core.api.search.SearchOperator; 021 import org.kuali.rice.krad.datadictionary.AttributeDefinition; 022 import org.kuali.rice.krad.service.KRADServiceLocatorInternal; 023 import org.kuali.rice.krad.service.RiceApplicationConfigurationMediationService; 024 import org.kuali.rice.krad.service.RiceApplicationConfigurationService; 025 import org.kuali.rice.ksb.api.KsbApiServiceLocator; 026 import org.kuali.rice.ksb.api.bus.Endpoint; 027 028 import javax.xml.namespace.QName; 029 import java.util.ArrayList; 030 import java.util.HashSet; 031 import java.util.List; 032 import java.util.Set; 033 import java.util.concurrent.ConcurrentMap; 034 import java.util.concurrent.TimeUnit; 035 036 //@Transactional 037 public class RiceApplicationConfigurationMediationServiceImpl implements RiceApplicationConfigurationMediationService { 038 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RiceApplicationConfigurationMediationServiceImpl.class); 039 040 // Max age defined in seconds 041 protected int configurationParameterCacheMaxSize = 200; 042 protected int configurationParameterCacheMaxAgeSeconds = 3600; 043 protected int nonDatabaseComponentsCacheMaxSize = 50; 044 protected int nonDatabaseComponentsCacheMaxAgeSeconds = 3600; 045 046 //TODO: use the concurrentMap properties rather than synchronized blocks 047 protected final ConcurrentMap<String, String> configurationParameterCache = new MapMaker().maximumSize(configurationParameterCacheMaxSize).expireAfterAccess(configurationParameterCacheMaxAgeSeconds, TimeUnit.SECONDS).softValues().makeMap(); 048 protected final ConcurrentMap<String,List<Component>> nonDatabaseComponentsCache = new MapMaker().maximumSize(nonDatabaseComponentsCacheMaxSize).expireAfterAccess(nonDatabaseComponentsCacheMaxAgeSeconds, TimeUnit.SECONDS).softValues().makeMap(); 049 protected final ConcurrentMap<String,RiceApplicationConfigurationService> responsibleServiceByPackageClass = new MapMaker().maximumSize(configurationParameterCacheMaxSize).expireAfterAccess(configurationParameterCacheMaxAgeSeconds, TimeUnit.SECONDS).softValues().makeMap(); 050 051 public String getConfigurationParameter( String namespaceCode, String parameterName ){ 052 053 String parameterValue = null; 054 if ( namespaceCode != null ) { 055 String parameterKey = (new StringBuffer(namespaceCode).append(SearchOperator.OR.op()).append(parameterName)).toString(); 056 parameterValue = getParameterValueFromConfigurationParameterCache( parameterKey ); 057 if ( parameterValue != null ) { 058 return parameterValue; 059 } 060 NamespaceService nsService = KRADServiceLocatorInternal.getNamespaceService(); 061 final String applicationNamespaceCode; 062 Namespace namespace = nsService.getNamespace(namespaceCode); 063 if (namespace != null) { 064 applicationNamespaceCode = namespace.getApplicationId(); 065 } else { 066 applicationNamespaceCode = namespaceCode; 067 } 068 if (applicationNamespaceCode != null) { 069 RiceApplicationConfigurationService rac = findRiceApplicationConfigurationService(applicationNamespaceCode); 070 if (rac != null) { 071 parameterValue = rac.getConfigurationParameter(parameterName); 072 } 073 } 074 if (parameterValue != null){ 075 synchronized (configurationParameterCache) { 076 configurationParameterCache.put( parameterKey, parameterValue); 077 } 078 } 079 } 080 return parameterValue; 081 } 082 083 084 protected String getParameterValueFromConfigurationParameterCache(String parameterKey) { 085 return configurationParameterCache.get( parameterKey ); 086 } 087 088 protected List<Component> getComponentListFromNonDatabaseComponentsCache(String nonDatabaseServiceNameKey) { 089 return nonDatabaseComponentsCache.get( nonDatabaseServiceNameKey ); 090 } 091 092 public List<Component> getNonDatabaseComponents() { 093 094 // TODO I think the code that's below here will actually pull in more than 095 // one reference to a particular application's config service if it is deployed 096 // in a cluster, it needs to only pull a single RiceApplicationConfigurationService 097 // implementation per application id. Also, may want to consider load balancing 098 // and failover in those cases? It might be best to try and utilize the client-side 099 // KSB proxies that handle a lot of this stuff for us 100 101 102 Set<QName> serviceNames = findApplicationConfigurationServices(); 103 104 List<Component> nonDatabaseComponents = new ArrayList<Component>(); 105 //add cache per serviceName 106 for ( QName serviceName : serviceNames ) { 107 List<Component> nonDatabaseComponentFromCache = this.getComponentListFromNonDatabaseComponentsCache(serviceName.toString()); 108 if (nonDatabaseComponentFromCache != null) { 109 nonDatabaseComponents.addAll(nonDatabaseComponentFromCache); 110 } else { 111 RiceApplicationConfigurationService rac = findRiceApplicationConfigurationService(serviceName); 112 try { 113 if (rac != null) { 114 nonDatabaseComponents.addAll(rac.getNonDatabaseComponents()); 115 synchronized (nonDatabaseComponentsCache) { 116 nonDatabaseComponentsCache.put(serviceName.toString(), rac.getNonDatabaseComponents() ); 117 } 118 } 119 } catch (Exception e) { 120 //TODO : Need a better way to catch if service is not active (404 error) 121 LOG.warn("Invalid RiceApplicationConfigurationService with name: " + serviceName + ". "); 122 } 123 } 124 125 } 126 127 return nonDatabaseComponents; 128 } 129 130 protected Set<QName> findApplicationConfigurationServices() { 131 Set<QName> names = new HashSet<QName>(); 132 List<Endpoint> allEndpoints = KsbApiServiceLocator.getServiceBus().getAllEndpoints(); 133 for (Endpoint endpoint : allEndpoints) { 134 QName serviceName = endpoint.getServiceConfiguration().getServiceName(); 135 if (serviceName.getLocalPart().equals(KRADServiceLocatorInternal.RICE_APPLICATION_CONFIGURATION_SERVICE)) { 136 names.add(serviceName); 137 } 138 } 139 return names; 140 } 141 142 protected RiceApplicationConfigurationService findRiceApplicationConfigurationService(QName serviceName) { 143 try { 144 return (RiceApplicationConfigurationService) GlobalResourceLoader.getService(serviceName); 145 } catch (Exception e) { 146 // if the service doesn't exist an exception is thrown 147 LOG.warn("Failed to locate RiceApplicationConfigurationService with name: " + serviceName,e); 148 } 149 return null; 150 } 151 152 protected RiceApplicationConfigurationService findRiceApplicationConfigurationService(String namespace) { 153 try { 154 return (RiceApplicationConfigurationService)GlobalResourceLoader.getService(new QName(namespace, KRADServiceLocatorInternal.RICE_APPLICATION_CONFIGURATION_SERVICE)); 155 } catch (Exception e) { 156 // if the service doesn't exist an exception is thrown 157 LOG.warn("Failed to locate RiceApplicationConfigurationService with namespace: " + namespace,e); 158 } 159 return null; 160 } 161 162 163 public void setConfigurationParameterCacheMaxSize( 164 int configurationParameterCacheMaxSize) { 165 this.configurationParameterCacheMaxSize = configurationParameterCacheMaxSize; 166 } 167 168 169 public void setConfigurationParameterCacheMaxAgeSeconds( 170 int configurationParameterCacheMaxAgeSeconds) { 171 this.configurationParameterCacheMaxAgeSeconds = configurationParameterCacheMaxAgeSeconds; 172 } 173 174 175 public void setNonDatabaseComponentsCacheMaxSize( 176 int nonDatabaseComponentsCacheMaxSize) { 177 this.nonDatabaseComponentsCacheMaxSize = nonDatabaseComponentsCacheMaxSize; 178 } 179 180 181 public void setNonDatabaseComponentsCacheMaxAgeSeconds( 182 int nonDatabaseComponentsCacheMaxAgeSeconds) { 183 this.nonDatabaseComponentsCacheMaxAgeSeconds = nonDatabaseComponentsCacheMaxAgeSeconds; 184 } 185 186 /** 187 * Call each available service to see if it's responsible for the given package. When found, cache the result 188 * to prevent need for future service lookups for the same package. 189 */ 190 protected RiceApplicationConfigurationService findServiceResponsibleForPackageOrClass( String packageOrClassName ) { 191 if ( LOG.isDebugEnabled() ) { 192 LOG.debug( "Checking for app config service responsible for: " + packageOrClassName ); 193 } 194 RiceApplicationConfigurationService racService = responsibleServiceByPackageClass.get(packageOrClassName); 195 196 197 if ( racService != null ) { 198 if ( LOG.isDebugEnabled() ) { 199 LOG.debug( "Service found in cache: " + racService.getClass().getName() ); 200 } 201 } 202 203 if ( racService == null ) { 204 Set<QName> serviceNames = findApplicationConfigurationServices(); 205 for ( QName serviceName : serviceNames ) { 206 racService = findRiceApplicationConfigurationService(serviceName); 207 if ( racService != null ) { 208 209 try { 210 if ( racService.isResponsibleForPackage(packageOrClassName) ) { 211 if ( LOG.isDebugEnabled() ) { 212 LOG.debug( "Found responsible class on bus with name: " + serviceName ); 213 } 214 responsibleServiceByPackageClass.put(packageOrClassName, racService ); 215 break; 216 } else { 217 racService = null; // null it out in case this is the last iteration 218 } 219 } catch (Exception e) { 220 LOG.warn( "Assuming this racService is not responsible for the package or class. racService: " + 221 racService.toString() + " ; packageOrClassName: " + packageOrClassName); 222 } 223 } 224 } 225 } 226 if ( racService == null ) { 227 LOG.warn( "Unable to find service which handles package/class: " + packageOrClassName + " -- returning null." ); 228 } 229 return racService; 230 } 231 232 /** 233 * @see org.kuali.rice.krad.service.RiceApplicationConfigurationMediationService#getBaseInquiryUrl(java.lang.String) 234 */ 235 public String getBaseInquiryUrl(String businessObjectClassName) { 236 RiceApplicationConfigurationService racService = findServiceResponsibleForPackageOrClass(businessObjectClassName); 237 if ( racService != null ) { 238 return racService.getBaseInquiryUrl(businessObjectClassName); 239 } 240 return null; 241 } 242 243 /** 244 * @see org.kuali.rice.krad.service.RiceApplicationConfigurationMediationService#getBaseLookupUrl(java.lang.String) 245 */ 246 public String getBaseLookupUrl(String businessObjectClassName) { 247 RiceApplicationConfigurationService racService = findServiceResponsibleForPackageOrClass(businessObjectClassName); 248 if ( racService != null ) { 249 return racService.getBaseLookupUrl(businessObjectClassName); 250 } 251 return null; 252 } 253 254 /** 255 * @see org.kuali.rice.krad.service.RiceApplicationConfigurationMediationService#getBaseHelpUrl(java.lang.String) 256 */ 257 public String getBaseHelpUrl(String businessObjectClassName) { 258 RiceApplicationConfigurationService racService = findServiceResponsibleForPackageOrClass(businessObjectClassName); 259 if ( racService != null ) { 260 return racService.getBaseHelpUrl(businessObjectClassName); 261 } 262 return null; 263 } 264 265 /** 266 * @see org.kuali.rice.krad.service.RiceApplicationConfigurationMediationService#getBusinessObjectAttributeDefinition(java.lang.String, java.lang.String) 267 */ 268 public AttributeDefinition getBusinessObjectAttributeDefinition(String businessObjectClassName, String attributeName) { 269 if ( LOG.isDebugEnabled() ) { 270 LOG.debug( "Querying for an AttributeDefinition for: " + businessObjectClassName + " / " + attributeName ); 271 } 272 RiceApplicationConfigurationService racService = findServiceResponsibleForPackageOrClass(businessObjectClassName); 273 if ( racService != null ) { 274 return racService.getBusinessObjectAttributeDefinition(businessObjectClassName, attributeName); 275 } 276 return null; 277 } 278 }