Java tutorial
/** * Copyright 2008-2015 Qualogy Solutions B.V. * * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 * * 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 com.qualogy.qafe.business.integration.adapter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.collections.map.AbstractHashedMap; import org.apache.commons.collections.map.CaseInsensitiveMap; import com.qualogy.qafe.bind.commons.type.AdapterMapping; import com.qualogy.qafe.bind.commons.type.In; import com.qualogy.qafe.bind.commons.type.Parameter; import com.qualogy.qafe.bind.commons.type.Reference; import com.qualogy.qafe.bind.commons.type.TypeDefinition; import com.qualogy.qafe.bind.core.application.ApplicationContext; import com.qualogy.qafe.business.integration.builder.PredefinedClassTypeConverter; import com.qualogy.qafe.core.datastore.DataIdentifier; import com.qualogy.qafe.core.datastore.DataStore; import com.qualogy.qafe.core.datastore.ParameterValueHandler; import com.qualogy.qafe.core.datastore.DataMap; /** * Integration service adpater that can adapt parameters to and from a service call * to an actual servicecall, done by a processor * @author mvanderwurff * */ public class Adapter { /** * Method to adapt an integration service result to one ore more business domain objects. If service result * is a list, the list will be recursively adapted. * * This 'controlling' method for adapting the output controls in 2 steps: * 1. map according to specified service attribute name (regarding the dot notation within the reference key) * 2. map according to mapping or type * * The order in which the mapping upon the outcome will be tried is: * 1. MappingAdapter when mapping not null, * 2. PredefinedMapper when type is a known (for this business project) type * 3. BestEffortAdapter will try to adapt Type(business domain) rules to the given object * * Notes: * - Empty mapping leaves a null object * - A null paramname on an outputmapping doesn't leave an entry * * @param id * @param serviceOutcome - the object from a service to adapt * @param outputMapping - the mapping the object needs to be adapted to */ public static void adaptOut(DataIdentifier id, Object serviceOutcome, List outputMapping) { if (serviceOutcome instanceof Object[]) { serviceOutcome = Arrays.asList((Object[]) serviceOutcome); } if (outputMapping != null) { for (Iterator iter1 = outputMapping.iterator(); iter1.hasNext();) { Parameter param = (Parameter) iter1.next(); if (param.getName() == null) continue; Object dsResult = null; if (serviceOutcome instanceof List) { dsResult = prepareList((List) serviceOutcome, param); } else { dsResult = prepare(serviceOutcome, param); } if (DataStore.findValue(id, DataStore.KEY_WORD_COUNT) != null) { dsResult = ((Map) ((ArrayList) dsResult).get(0)).get("count(*)"); } DataStore.store(id, param.getName(), dsResult); } } retrieveQafeBuiltInList(id, serviceOutcome); } private static void retrieveQafeBuiltInList(DataIdentifier id, Object serviceOutcome) { // A call statement can return a variable containing the json representation of the builtins to execute. // This method is to collect that value to use it in business action without user mentioning it in the out param. if (serviceOutcome instanceof Map) { // Call statement always returns a Map.(In case of java if the same functionality needed it should also return Map) // If we do not do this check excpetion can happen in case of simple value return from Java. Reference ref = new Reference(DataStore.KEY_WORD_QAFE_BUILT_IN_LIST, Reference.SOURCE_DATASTORE_ID); Parameter builtInParameter = new Parameter(ref); builtInParameter.setName(DataStore.KEY_WORD_QAFE_BUILT_IN_LIST); Object dsResult = prepare(serviceOutcome, builtInParameter); if (dsResult != null) { DataStore.store(id, builtInParameter.getName(), dsResult); } } } /** * Method to prepare objects for adaption and the actual adapt action * Method checks if a reference key is supplied to get a nested value from * the serviceoutcome. * * The serviceOutcome must be of type Map, so will be converted to a Map if necessary. * * @param serviceOutcome * @param param * @return */ private static Object prepare(Object serviceOutcome, Parameter param) { Object toBeMapped = null; Object converted = ObjectMapConverter.convert(serviceOutcome); if (converted != null)//if cannot be converted serviceOutcome = converted; if (param.getRef() == null || param.getRef().denotesRoot()) { //need root? toBeMapped = serviceOutcome; } else { //serviceoutcome must be a map if not root is wanted if (!(serviceOutcome instanceof Map)) throw new UnableToAdaptException( "cannot get attributes like '" + param.getRef() + "' from a non-complex type (" + (serviceOutcome != null ? "" + serviceOutcome.getClass() : "null") + ")"); toBeMapped = (serviceOutcome == null || ((Map) serviceOutcome).isEmpty()) ? null : ((Map) serviceOutcome).get(param.getRef().toString()); } return adaptFromService(toBeMapped, param.getAdapter(), param.getType()); } /** * Method to prepare a list of objects for actual adaption to do's, * uses Adapter.prepare(Object, Parameter), so actually a convinience * method that loops through the list and calls prepare. * * upon the objects * @param serviceOutcome * @param param * @return */ private static Object prepareList(List serviceOutcome, Parameter param) { List result = null; if (!serviceOutcome.isEmpty()) { result = new ArrayList(); for (Iterator iter = serviceOutcome.iterator(); iter.hasNext();) { result.add(prepare(iter.next(), param)); } } return result; } /** * Method to adapt a (part of a) serviceOutcome to a type or mapping specified in the configuration. * * If List or array, this method is called recursively. * * @param toAdapt * @param mapping * @param type * @return */ private static Object adaptFromService(Object toAdapt, AdapterMapping mapping, TypeDefinition type) { Object adapted = null; if (toAdapt instanceof Object[]) { toAdapt = Arrays.asList((Object[]) toAdapt); } if (toAdapt instanceof Collection) { List result = new ArrayList(); for (Iterator iter = ((List) toAdapt).iterator(); iter.hasNext();) { Object tmpResult = adaptFromService(iter.next(), mapping, type); result.add(tmpResult); } adapted = result; } else { if (toAdapt != null) { if (mapping != null) {//map according to adapter, it's always a map adapted = MappingAdapter.adaptFromService(toAdapt, mapping); } else if (PredefinedAdapterFactory.canObjectBeConverted(type, toAdapt)) {//simple type adapted = PredefinedAdapterFactory.create(type).convert(toAdapt); } else if (type != null) {//either create mapping based upon typedefinition otherwise see if we have some conversion todo adapted = BestEffortAdapter.adapt((Map) toAdapt, type); } else { adapted = toAdapt; } } } return adapted; } // public static Map adaptIn(ApplicationContext context, DataIdentifier id, List inputMapping) { // return Adapter.adaptIn(context, id, inputMapping, null); // } /** * Method adapts the data stored under the uniqueidentifier to the types and adapters * supplied through the mapping. Afterwards the method wil create adapted objects * for processing. * * First there needs to be determined if the in parameter has an adaptermapping supplied. * If so, the MappingAdapter will handle accordingly and adapts the object structure * to the mapping. Else the single value is retrieved and converted if necessary. * Secondly the data is prepared for the service call. * - If the adapted value is not null, this will be the adpated object * - Otherwise the type will be inspected and taken as being adapted * - If the type is also not supplied, the adapter is checked for an outputclass, * which will than be the adapted object * - If none of above applies the method will throw an exception because later on the * method can not be determined because of the missing argument. * * @param id * @param inputMapping * @return */ @SuppressWarnings("unchecked") public static Map<String, AdaptedToService> adaptIn(ClassLoader classLoader, ApplicationContext context, DataIdentifier id, List<In> inputMapping) { // Map<String, AdaptedToService> adaptedMap = new CaseInsensitiveMap(); Map<String, AdaptedToService> adaptedMap = new DataMap<String, AdaptedToService>(); for (int i = 0; inputMapping != null && i < inputMapping.size(); i++) { In in = (In) inputMapping.get(i); Object adapted = null;//conversion-/adapter-result if (in.getAdapter() != null && in.getAdapter().getOutputClass() != null) { adapted = MappingAdapter.adaptToService(classLoader, id, in); } else { adapted = ParameterValueHandler.get(context, id, in); if (in.getOutputClass() != null && adapted instanceof Map) { adapted = ObjectMapConverter.convert(in.getOutputClass(), (Map) adapted); } else { if (in.getType() != null && PredefinedAdapterFactory.canObjectBeConverted(in.getType(), adapted)) adapted = PredefinedAdapterFactory.create(in.getType()).convert(adapted); } } if (adapted != null || (in.getUseWhenNotSet() != null && in.getUseWhenNotSet() == true)) { adaptedMap = addAdaptedToServiceMap(adaptedMap, in, adapted); } } return adaptedMap; } private static Map<String, AdaptedToService> addAdaptedToServiceMap(Map<String, AdaptedToService> adaptedMap, In in, Object adapted) { AdaptedToService ats = null; if (adapted != null) { ats = AdaptedToService.create(adapted); } else if (in.getUseWhenNotSet() != null && in.getUseWhenNotSet() == true) { if (in.getType() != null && PredefinedClassTypeConverter.isPredefined(in.getType())) {//if value is null set type if not null, so method can be determined and called with null param ats = AdaptedToService.create(PredefinedClassTypeConverter.convert(in.getType())); } else if (in.getOutputClass() != null) { ats = AdaptedToService.create(in.getOutputClass()); } else if (in.getAdapter() != null && in.getAdapter().getOutputClass() != null) { ats = AdaptedToService.create(in.getAdapter().getOutputClass()); } } adaptedMap.put(in.getName(), ats); return adaptedMap; } }