Java tutorial
/* * Copyright 2014 The Kuali Foundation. * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ecl2.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kuali.kfs.sys.web.controller; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.kuali.kfs.sys.businessobject.datadictionary.FinancialSystemBusinessObjectEntry; import org.kuali.kfs.sys.businessobject.lookup.LookupableSpringContext; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.service.impl.KfsParameterConstants; import org.kuali.kfs.sys.util.RestXmlUtil; import org.kuali.rice.core.api.config.property.ConfigContext; import org.kuali.rice.core.api.util.type.TypeUtils; import org.kuali.rice.coreservice.framework.parameter.ParameterService; import org.kuali.rice.kim.api.KimConstants; import org.kuali.rice.kim.api.permission.PermissionService; import org.kuali.rice.kim.api.services.KimApiServiceLocator; import org.kuali.rice.kns.datadictionary.InquirySectionDefinition; import org.kuali.rice.kns.lookup.LookupableHelperService; import org.kuali.rice.krad.UserSession; import org.kuali.rice.krad.bo.BusinessObject; import org.kuali.rice.krad.lookup.LookupUtils; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.DataDictionaryService; import org.kuali.rice.krad.service.KRADServiceLocator; import org.kuali.rice.krad.service.KRADServiceLocatorWeb; import org.kuali.rice.krad.service.PersistenceStructureService; import org.kuali.rice.krad.service.XmlObjectSerializerService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.KRADUtils; import org.kuali.rice.krad.util.ObjectUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @Controller public class DataObjectRestServiceController { private static final String RETURN_RANDOM_OBJECT = "returnRandomObject"; private static final String USE_BO_SERVICE = "useBoService"; private static final String LOOKUPABLE_HELPER_SERVICE = "lookupableHelperService"; private static final String LIMIT_RESULT = "limitResult"; private static final String UNBOUNDED = "unbounded"; private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger .getLogger(DataObjectRestServiceController.class); private DataDictionaryService dataDictionaryService; private PersistenceStructureService persistenceStructureService; private ParameterService parameterService; private PermissionService permissionService; private BusinessObjectService businessObjectService; @ExceptionHandler(AccessDeniedException.class) @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Not authorized.") public void handleAccessDeniedException(AccessDeniedException ex, HttpServletResponse response) { } @ExceptionHandler(NoSuchBeanDefinitionException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Bad request.") public void handleNoSuchBeanDefinitionException(NoSuchBeanDefinitionException ex, HttpServletResponse response) { } @ExceptionHandler(RuntimeException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Unexpected exception has occured.") public String handleRuntimeException(RuntimeException ex, HttpServletResponse response) { return ExceptionUtils.getStackTrace(ex); } @RequestMapping(value = "/{namespace}/{dataobject}.json", method = RequestMethod.GET, produces = "application/json") @ResponseBody public ResponseEntity<String> getDataObjectsAsJSON(@PathVariable("namespace") String namespace, @PathVariable("dataobject") String dataobject, HttpServletRequest request) throws Exception { FinancialSystemBusinessObjectEntry boe = getBusinessObject(dataobject); validateRequest(boe, namespace, dataobject, request); try { List<Map<String, String>> resultMap = generateResultMap(request, boe); ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true); String jsonData = null; if (resultMap.size() == 1) { jsonData = mapper.defaultPrettyPrintingWriter().writeValueAsString(resultMap.get(0)) .replaceFirst(HashMap.class.getSimpleName(), boe.getBusinessObjectClass().getName()); } else { jsonData = mapper.defaultPrettyPrintingWriter().writeValueAsString(resultMap).replaceFirst( ArrayList.class.getSimpleName(), ArrayList.class.getSimpleName() + "<" + boe.getBusinessObjectClass().getName() + ">"); } return new ResponseEntity<String>(jsonData, HttpStatus.OK); } catch (Exception e) { LOG.error("Unexpected exception has occured.", e); throw new RuntimeException("Unexpected exception has occured."); } } @RequestMapping(value = "/{namespace}/{dataobject}.xml", method = RequestMethod.GET, produces = "application/xml") @ResponseBody public ResponseEntity<String> getDataObjectsAsXML(@PathVariable("namespace") String namespace, @PathVariable("dataobject") String dataobject, HttpServletRequest request) throws Exception { FinancialSystemBusinessObjectEntry boe = getBusinessObject(dataobject); validateRequest(boe, namespace, dataobject, request); try { List<Map<String, String>> resultMap = generateResultMap(request, boe); String xml = null; if (resultMap.size() == 1) { xml = RestXmlUtil.toXML(boe, resultMap.get(0)); } else { xml = RestXmlUtil.toXML(boe, resultMap); } return new ResponseEntity<String>(xml, HttpStatus.OK); } catch (Exception e) { LOG.error("Unexpected exception has occured.", e); throw new RuntimeException("Unexpected exception has occured."); } } protected List<Map<String, String>> generateResultMap(HttpServletRequest request, FinancialSystemBusinessObjectEntry boe) { List<? extends BusinessObject> results = getSearchResults(request, boe); List<String> inquiryFields = getInquiryFields(boe); List<Map<String, String>> resultMap = new ArrayList<Map<String, String>>(); for (BusinessObject bo : results) { Map<String, String> objectMap = new HashMap<String, String>(); Object object = ObjectUtils.createNewObjectFromClass(boe.getBusinessObjectClass()); for (String propertyName : inquiryFields) { Object propertyValue; try { propertyValue = ObjectUtils.getPropertyValue(bo, propertyName); } catch (RuntimeException e) { //LOG.debug("Failed to process propertyName: " + propertyName); continue; } Class<?> propertyType = ObjectUtils.getPropertyType(bo, propertyName, getPersistenceStructureService()); if (isPropertyTypeValid(propertyType)) { objectMap.put(propertyName, propertyValue + ""); } } resultMap.add(objectMap); } return resultMap; } protected boolean isAuthorized(FinancialSystemBusinessObjectEntry boe) throws Exception { if (boe == null) { LOG.error("boe is null"); return false; } UserSession serverUserSession = GlobalVariables.getUserSession(); if (serverUserSession == null) { LOG.error("serverUserSession is null"); return false; } return getPermissionService().isAuthorizedByTemplate(serverUserSession.getPrincipalId(), KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.LOOK_UP_RECORDS, KRADUtils.getNamespaceAndComponentSimpleName(boe.getBusinessObjectClass()), Collections.<String, String>emptyMap()); } protected boolean isPropertyTypeValid(Class<?> propertyType) { if (propertyType != null && (TypeUtils.isStringClass(propertyType) || TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) || TypeUtils.isTemporalClass(propertyType) || TypeUtils.isBooleanClass(propertyType))) { return true; } return false; } protected List<String> getInquiryFields(FinancialSystemBusinessObjectEntry boe) { List<String> inquiryFields = new ArrayList<String>(); for (InquirySectionDefinition section : boe.getInquiryDefinition().getInquirySections()) { inquiryFields.addAll(section.getInquiryFieldNames()); } return inquiryFields; } protected List<? extends BusinessObject> getSearchResults(HttpServletRequest request, FinancialSystemBusinessObjectEntry boe) { Map<String, String> fieldValues = new HashMap<String, String>(); for (Object o : request.getParameterMap().keySet()) { String[] value = (String[]) request.getParameterMap().get(o); fieldValues.put(o.toString(), value[0]); } // Retrieve all service related parameters Map<String, String> serviceParameterMap = getServiceParameters(fieldValues); List<? extends BusinessObject> searchResults; if (StringUtils.equalsIgnoreCase(serviceParameterMap.get(USE_BO_SERVICE), "Y")) { searchResults = (List<? extends BusinessObject>) getBusinessObjectService() .findMatching(boe.getBusinessObjectClass(), fieldValues); // limit BO service result if (!StringUtils.equalsIgnoreCase(serviceParameterMap.get(UNBOUNDED), "Y") && StringUtils.isEmpty(serviceParameterMap.get(LIMIT_RESULT))) { serviceParameterMap.put(LIMIT_RESULT, LookupUtils.getApplicationSearchResultsLimit().toString()); } } else { LookupableHelperService lookupableHelperService = getLookupableHelperService( boe.getLookupDefinition().getLookupableID()); lookupableHelperService.setBusinessObjectClass(boe.getBusinessObjectClass()); if (StringUtils.equalsIgnoreCase(serviceParameterMap.get(UNBOUNDED), "Y")) { try { searchResults = lookupableHelperService.getSearchResultsUnbounded(fieldValues); } catch (UnsupportedOperationException e) { LOG.warn( "lookupableHelperService.getSearchResultsUnbounded failed. Retrying the lookup using the default search.", e); searchResults = lookupableHelperService.getSearchResults(fieldValues); } } else { searchResults = lookupableHelperService.getSearchResults(fieldValues); } } // Handle single random object request if (StringUtils.isNotEmpty(serviceParameterMap.get(RETURN_RANDOM_OBJECT))) { return getRandomDataObject(searchResults, Integer.parseInt(serviceParameterMap.get(RETURN_RANDOM_OBJECT))); } // Handle limit result request if (StringUtils.isNotEmpty(serviceParameterMap.get(LIMIT_RESULT))) { return getLimitedResult(searchResults, Integer.parseInt(serviceParameterMap.get(LIMIT_RESULT))); } return searchResults; } protected List<? extends BusinessObject> getLimitedResult(List<? extends BusinessObject> searchResults, int searchLimit) { if (searchLimit > 0) { return searchResults.subList(0, Math.min(searchResults.size(), searchLimit)); } return searchResults; } protected Map<String, String> getServiceParameters(Map<String, String> fieldValues) { Map<String, String> serviceParameterMap = new HashMap<String, String>(); serviceParameterMap.put(USE_BO_SERVICE, fieldValues.remove(USE_BO_SERVICE)); serviceParameterMap.put(LIMIT_RESULT, fieldValues.remove(LIMIT_RESULT)); serviceParameterMap.put(RETURN_RANDOM_OBJECT, fieldValues.remove(RETURN_RANDOM_OBJECT)); return serviceParameterMap; } protected List<BusinessObject> getRandomDataObject(List<? extends BusinessObject> searchResults, int objectCount) { // return the list if there is no randomizing possibility if (searchResults.size() < 2 || objectCount > searchResults.size()) { return (List<BusinessObject>) searchResults; } Random random = new Random(); Set<BusinessObject> result = new HashSet<BusinessObject>(); while (result.size() < objectCount) { result.add(searchResults.get(random.nextInt(searchResults.size()))); } return new ArrayList<BusinessObject>(result); } protected void validateRequest(FinancialSystemBusinessObjectEntry boe, String namespace, String dataobject, HttpServletRequest request) throws Exception { // check for https (will be ignored in dev mode), authorization if ((!ConfigContext.getCurrentContextConfig().getDevMode() && !request.isSecure())) { LOG.debug("HTTPS check failed."); throw new AccessDeniedException("Not authorized."); } if (boe == null) { LOG.debug("BusinessObjectEntry is null."); throw new NoSuchBeanDefinitionException("Data object not found."); } if (!namespace.equalsIgnoreCase(KRADUtils.getNamespaceCode(boe.getBusinessObjectClass()))) { LOG.debug("Bad namespace for dataobject: " + boe.getBusinessObjectClass()); throw new NoSuchBeanDefinitionException("Invalid namespace."); } Boolean isModuleLocked = getParameterService().getParameterValueAsBoolean(namespace, KfsParameterConstants.PARAMETER_ALL_DETAIL_TYPE, KRADConstants.SystemGroupParameterNames.OLTP_LOCKOUT_ACTIVE_IND); boolean notAuthorized = !isAuthorized(boe); boolean moduleIsLocked = isModuleLocked != null && isModuleLocked; boolean noInquiryDefinition = !boe.hasInquiryDefinition(); if (notAuthorized || moduleIsLocked || noInquiryDefinition) { LOG.debug("notAuthorized: " + notAuthorized); LOG.debug("moduleIsLocked: " + moduleIsLocked); LOG.debug("noInquiryDefinition: " + noInquiryDefinition); throw new AccessDeniedException("Not authorized."); } } /* protected boolean verifySignature(HttpHeaders headers) { try { String encodedSignature = headers.getRequestHeader(KSBConstants.DIGITAL_SIGNATURE_HEADER).get(0); String encodedCertificate = headers.getRequestHeader(KSBConstants.KEYSTORE_CERTIFICATE_HEADER).get(0); //String verificationAlias = headers.getRequestHeader(KSBConstants.KEYSTORE_ALIAS_HEADER).get(0); Signature verifySig = null; byte[] digitalSignature = null; digitalSignature = Base64.decodeBase64(encodedSignature.getBytes("UTF-8")); if (StringUtils.isNotBlank(encodedCertificate)) { byte[] certificate = Base64.decodeBase64(encodedCertificate.getBytes("UTF-8")); CertificateFactory cf = CertificateFactory.getInstance("X.509"); verifySig = getDigitalSignatureService().getSignatureForVerification(cf.generateCertificate(new ByteArrayInputStream(certificate))); }// else if (StringUtils.isNotBlank(verificationAlias)) { //verifySig = getDigitalSignatureService().getSignatureForVerification(verificationAlias); //} return verifySig.verify(digitalSignature); } catch (Exception e) { LOG.error("Failed to initialize digital signature verification.", e); } return false; }*/ protected FinancialSystemBusinessObjectEntry getBusinessObject(String dataobject) { try { return (FinancialSystemBusinessObjectEntry) getDataDictionaryService().getDictionaryObject(dataobject); } catch (NoSuchBeanDefinitionException e) { LOG.debug("Failed to retrieve data dictionary object.", e); } return null; } protected LookupableHelperService getLookupableHelperService(String lookupableID) { if (lookupableID != null) { return LookupableSpringContext.getLookupable(lookupableID).getLookupableHelperService(); } else { return LookupableSpringContext.getLookupableHelperService(LOOKUPABLE_HELPER_SERVICE); } } public DataDictionaryService getDataDictionaryService() { if (this.dataDictionaryService == null) { this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); } return this.dataDictionaryService; } public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { this.dataDictionaryService = dataDictionaryService; } public PersistenceStructureService getPersistenceStructureService() { if (persistenceStructureService == null) { persistenceStructureService = KRADServiceLocator.getPersistenceStructureService(); } return persistenceStructureService; } public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { this.persistenceStructureService = persistenceStructureService; } public ParameterService getParameterService() { if (parameterService == null) { parameterService = SpringContext.getBean(ParameterService.class); } return parameterService; } public void setParameterService(ParameterService parameterService) { this.parameterService = parameterService; } public XmlObjectSerializerService getXmlObjectSerializerService() { return KRADServiceLocator.getXmlObjectSerializerService(); } public PermissionService getPermissionService() { if (permissionService == null) { permissionService = KimApiServiceLocator.getPermissionService(); } return permissionService; } public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } public BusinessObjectService getBusinessObjectService() { if (businessObjectService == null) { businessObjectService = SpringContext.getBean(BusinessObjectService.class); } return businessObjectService; } public void setBusinessObjectService(BusinessObjectService businessObjectService) { this.businessObjectService = businessObjectService; } }