View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.impl.component;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.commons.lang.builder.CompareToBuilder;
21  import org.apache.log4j.Logger;
22  import org.kuali.rice.core.api.component.Component;
23  import org.kuali.rice.core.api.component.ComponentService;
24  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
25  import org.kuali.rice.core.api.util.ChecksumUtils;
26  import org.kuali.rice.krad.service.BusinessObjectService;
27  import org.springframework.transaction.annotation.Transactional;
28  
29  import java.sql.Timestamp;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.Comparator;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  
38  /**
39   * Reference implementation of the {@code ComponentService}.
40   *
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  @Transactional
44  public class ComponentServiceImpl implements ComponentService {
45  
46      private static final Logger LOG = Logger.getLogger(ComponentServiceImpl.class);
47  
48      private BusinessObjectService businessObjectService;
49      private ComponentSetDao componentSetDao;
50  
51      @Override
52      public Component getComponentByCode(String namespaceCode, String componentCode) {
53          if (StringUtils.isBlank(namespaceCode)) {
54              throw new RiceIllegalArgumentException("namespaceCode was a null or blank value");
55          }
56          if (StringUtils.isBlank(componentCode)) {
57              throw new RiceIllegalArgumentException("componentCode was a null or blank value");
58          }
59          Map<String, String> primaryKeys = new HashMap<String, String>();
60          primaryKeys.put("namespaceCode", namespaceCode);
61          primaryKeys.put("code", componentCode);
62          ComponentBo componentBo = getBusinessObjectService().findByPrimaryKey(ComponentBo.class, primaryKeys);
63          if (componentBo != null) {
64              return ComponentBo.to(componentBo);
65          }
66          DerivedComponentBo derivedComponentBo = getBusinessObjectService().findByPrimaryKey(DerivedComponentBo.class, primaryKeys);
67          return derivedComponentBo == null ? null : DerivedComponentBo.to(derivedComponentBo);
68      }
69  
70      @Override
71      public List<Component> getAllComponentsByNamespaceCode(String namespaceCode) {
72          if (StringUtils.isBlank(namespaceCode)) {
73              throw new RiceIllegalArgumentException("namespaceCode was a null or blank value");
74          }
75          Map<String, String> criteria = new HashMap<String, String>();
76          criteria.put("namespaceCode", namespaceCode);
77  
78          List<Component> components = new ArrayList<Component>();
79  
80          Collection<ComponentBo> componentBos =
81                  getBusinessObjectService().findMatching(ComponentBo.class, criteria);
82          Collection<DerivedComponentBo> derivedComponentBos =
83                  getBusinessObjectService().findMatching(DerivedComponentBo.class, criteria);
84          return translateCollections(componentBos, derivedComponentBos);
85      }
86  
87      @Override
88      public List<Component> getActiveComponentsByNamespaceCode(String namespaceCode) {
89          if (StringUtils.isBlank(namespaceCode)) {
90              throw new RiceIllegalArgumentException("namespaceCode was a null or blank value");
91          }
92          Map<String, Object> criteria = new HashMap<String, Object>();
93          criteria.put("namespaceCode", namespaceCode);
94          criteria.put("active", Boolean.TRUE);
95          Collection<ComponentBo> componentBos =
96                  getBusinessObjectService().findMatching(ComponentBo.class, criteria);
97          criteria.remove("active");
98          Collection<DerivedComponentBo> derivedComponentBos =
99                  getBusinessObjectService().findMatching(DerivedComponentBo.class, criteria);
100         return translateCollections(componentBos, derivedComponentBos);
101     }
102 
103     @Override
104     public List<Component> getDerivedComponentSet(String componentSetId) {
105         if (StringUtils.isBlank(componentSetId)) {
106             throw new RiceIllegalArgumentException("componentSetId was a null or blank value");
107         }
108         Map<String, Object> criteria = new HashMap<String, Object>();
109         criteria.put("componentSetId", componentSetId);
110         Collection<DerivedComponentBo> derivedComponentBos =
111                 getBusinessObjectService().findMatching(DerivedComponentBo.class, criteria);
112         return translateCollections(null, derivedComponentBos);
113     }
114 
115     @Override
116     public void publishDerivedComponents(String componentSetId, List<Component> components) {
117         if (StringUtils.isBlank(componentSetId)) {
118             throw new RiceIllegalArgumentException("componentSetId was a null or blank value");
119         }
120         components = validateAndNormalizeComponents(componentSetId, components);
121         LOG.info("Requesting to publish " + components.size() + " derived components for componentSetId=" + componentSetId);
122         ComponentSetBo componentSet = getComponentSetDao().getComponentSet(componentSetId);
123         if (componentSet == null) {
124             componentSet = new ComponentSetBo();
125             componentSet.setComponentSetId(componentSetId);
126         }
127         String checksum = calculateChecksum(components);
128         if (!checksum.equals(componentSet.getChecksum())) {
129             LOG.info("Checksums were different, proceeding with update of derived components for componentSetId=" + componentSetId);
130             componentSet.setChecksum(checksum);
131             componentSet.setLastUpdateTimestamp(new Timestamp(System.currentTimeMillis()));
132             if (getComponentSetDao().saveIgnoreLockingFailure(componentSet)) {
133                 updateDerivedComponents(componentSetId, components);
134             }
135         } else {
136             LOG.info("Checksums were the same, no derived component update needed for componentSetId=" + componentSetId);
137         }
138     }
139 
140     protected List<Component> validateAndNormalizeComponents(String componentSetId, List<Component> components) {
141         List<Component> processedComponents = new ArrayList<Component>();
142 
143         // normalize and copy component list, we will later sort this list possibly so don't want to hold onto the original
144         if (components == null) {
145             components = new ArrayList<Component>();
146         } else {
147             components = new ArrayList<Component>(components);
148         }
149         // components must either have a null componentSetId or one which matches the componentSetId being published
150         for (Component component : components) {
151             // if componentSetId is null, recreate the component with that value
152             if (component.getComponentSetId() == null) {
153                 Component.Builder builder = Component.Builder.create(component);
154                 builder.setComponentSetId(componentSetId);
155                 component = builder.build();
156             }
157             String currentComponentSetId = component.getComponentSetId();
158             if (!componentSetId.equals(currentComponentSetId)) {
159                 throw new RiceIllegalArgumentException("Encountered a component with an invalid componentSetId of '" +
160                         currentComponentSetId + "'.  Expected null or '" + componentSetId + "'.");
161             }
162             processedComponents.add(component);
163         }
164         return processedComponents;
165     }
166 
167     /**
168      * Calculates the checksum for the list of components.  The list of components should be sorted in a
169      * consistent way prior to generation of the checksum to ensure that the checksum value comes out the same regardless
170      * of the ordering of components contained therein.  The checksum allows us to easily determine if the component set
171      * has been updated or not.
172      */
173     protected String calculateChecksum(List<Component> components) {
174         Collections.sort(components, new Comparator<Component>() {
175             @Override
176             public int compare(Component component1, Component component2) {
177                 return CompareToBuilder.reflectionCompare(component1, component2);
178             }
179         });
180         return ChecksumUtils.calculateChecksum(components);
181     }
182 
183     protected void updateDerivedComponents(String componentSetId, List<Component> components) {
184         Map<String, Object> deleteCriteria = new HashMap<String, Object>();
185         deleteCriteria.put("componentSetId", componentSetId);
186         businessObjectService.deleteMatching(DerivedComponentBo.class, deleteCriteria);
187         if (CollectionUtils.isNotEmpty(components)) {
188             List<DerivedComponentBo> derivedComponentBos = new ArrayList<DerivedComponentBo>();
189             for (Component component : components) {
190                 derivedComponentBos.add(DerivedComponentBo.from(component));
191             }
192             businessObjectService.save(derivedComponentBos);
193         }
194     }
195 
196     protected List<Component> translateCollections(Collection<ComponentBo> componentBos, Collection<DerivedComponentBo> derivedComponentBos) {
197         List<Component> components = new ArrayList<Component>();
198         if (CollectionUtils.isNotEmpty(componentBos)) {
199             for (ComponentBo componentBo : componentBos) {
200                 components.add(ComponentBo.to(componentBo));
201             }
202         }
203         if (CollectionUtils.isNotEmpty(derivedComponentBos)) {
204             for (DerivedComponentBo derivedComponentBo : derivedComponentBos) {
205                 components.add(DerivedComponentBo.to(derivedComponentBo));
206             }
207         }
208         return Collections.unmodifiableList(components);
209     }
210 
211     public BusinessObjectService getBusinessObjectService() {
212         return businessObjectService;
213     }
214 
215     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
216         this.businessObjectService = businessObjectService;
217     }
218 
219     public ComponentSetDao getComponentSetDao() {
220         return componentSetDao;
221     }
222 
223     public void setComponentSetDao(ComponentSetDao componentSetDao) {
224         this.componentSetDao = componentSetDao;
225     }
226     
227 }