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 }