io.atlasmap.core.DefaultAtlasFieldActionService Maven / Gradle / Ivy
package io.atlasmap.core;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.atlasmap.api.AtlasException;
import io.atlasmap.api.AtlasFieldAction;
import io.atlasmap.converters.DateTimeHelper;
import io.atlasmap.spi.AtlasConversionService;
import io.atlasmap.spi.AtlasFieldActionInfo;
import io.atlasmap.spi.AtlasFieldActionService;
import io.atlasmap.spi.AtlasInternalSession;
import io.atlasmap.v2.Action;
import io.atlasmap.v2.ActionDetail;
import io.atlasmap.v2.ActionDetails;
import io.atlasmap.v2.ActionParameter;
import io.atlasmap.v2.ActionParameters;
import io.atlasmap.v2.Actions;
import io.atlasmap.v2.AtlasModelFactory;
import io.atlasmap.v2.AuditStatus;
import io.atlasmap.v2.CollectionType;
import io.atlasmap.v2.CustomAction;
import io.atlasmap.v2.Field;
import io.atlasmap.v2.FieldGroup;
import io.atlasmap.v2.FieldType;
import io.atlasmap.v2.SimpleField;
public class DefaultAtlasFieldActionService implements AtlasFieldActionService {
private static final Logger LOG = LoggerFactory.getLogger(DefaultAtlasFieldActionService.class);
private ActionDetails actionDetails = new ActionDetails();
private AtlasConversionService conversionService = null;
public DefaultAtlasFieldActionService(AtlasConversionService conversionService) {
this.conversionService = conversionService;
}
public void init() {
listActionDetails().addAll(loadFieldActions());
// TODO load custom field actions in application bundles
// on hierarchical class loader environment
}
public List loadFieldActions() {
return loadFieldActions(this.getClass().getClassLoader());
}
public List loadFieldActions(ClassLoader classLoader) {
final ServiceLoader fieldActionServiceLoader = ServiceLoader.load(AtlasFieldAction.class, classLoader);
List answer = new ArrayList<>();
for (final AtlasFieldAction atlasFieldAction : fieldActionServiceLoader) {
if (LOG.isDebugEnabled()) {
LOG.debug("Loading FieldAction class: " + atlasFieldAction.getClass().getCanonicalName());
}
Class> clazz = atlasFieldAction.getClass();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
AtlasFieldActionInfo annotation = method.getAnnotation(AtlasFieldActionInfo.class);
if (annotation == null) {
continue;
}
ActionDetail det = new ActionDetail();
det.setClassName(clazz.getName());
det.setMethod(method.getName());
det.setName(annotation.name());
det.setSourceType(annotation.sourceType());
det.setTargetType(annotation.targetType());
det.setSourceCollectionType(annotation.sourceCollectionType());
det.setTargetCollectionType(annotation.targetCollectionType());
Class> actionClazz;
try {
actionClazz = Class.forName("io.atlasmap.v2." + annotation.name());
} catch (Exception e) {
actionClazz = clazz;
det.setCustom(true);
}
try {
// TODO https://github.com/atlasmap/atlasmap/issues/538
if (det.isCustom() == null || !det.isCustom()) {
det.setParameters(detectFieldActionParameters(actionClazz));
}
} catch (ClassNotFoundException e) {
LOG.error(String.format("Error detecting parameters for field action=%s msg=%s", annotation.name(), e.getMessage()), e);
}
if (LOG.isTraceEnabled()) {
LOG.trace("Loaded FieldAction: " + det.getName());
}
answer.add(det);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Loaded %s Field Actions", answer.size()));
}
return answer;
}
private ActionParameters detectFieldActionParameters(Class> actionClazz) throws ClassNotFoundException {
ActionParameters params = null;
for(Method method : actionClazz.getMethods()) {
// Find setters to avoid the get / is confusion
if(method.getParameterCount() == 1 && method.getName().startsWith("set")) {
// We have a parameter
if(params == null) {
params = new ActionParameters();
}
ActionParameter actionParam = null;
for(Parameter methodParam : method.getParameters()) {
actionParam = new ActionParameter();
actionParam.setName(camelize(method.getName().substring("set".length())));
// TODO set displayName/description - https://github.com/atlasmap/atlasmap/issues/96
actionParam.setFieldType(getConversionService().fieldTypeFromClass(methodParam.getType()));
// TODO fix this dirty hack for https://github.com/atlasmap/atlasmap/issues/386
if (methodParam.getType().isEnum()) {
actionParam.setFieldType(FieldType.STRING);
try {
for (Object e : methodParam.getType().getEnumConstants()) {
Method m = e.getClass().getDeclaredMethod("value", new Class[0]);
actionParam.getValues().add(m.invoke(e, new Object[0]).toString());
}
} catch (Exception e) {
LOG.debug("Failed to populate possible enum parameter values, ignoring...", e);
}
}
params.getParameter().add(actionParam);
}
}
}
return params;
}
@Override
public List listActionDetails() {
return actionDetails.getActionDetail();
}
/*
* TODO: getActionDetailByActionName() when all references are updated to use
*
* ActionDetail = findActionDetail(String actionName, FieldType sourceType)
*
* ref: https://github.com/atlasmap/atlasmap-runtime/issues/216
*/
@Deprecated
protected ActionDetail getActionDetailByActionName(String actionName) {
for(ActionDetail actionDetail : listActionDetails()) {
if(actionDetail.getName().equals(actionName)) {
return actionDetail;
}
}
return null;
}
/*
* 1. Find FieldAction by name
* 2. If multiple matches are found, return the best one based on FieldType sourceType
* 3. If there is not an exact match to sourceType, return the first FieldAction
* 4. If no matches found, return null
*
*
* @param actionName The name of the FieldAction
* @param sourceType A hint used to determine which FieldAction to use
* when multiple FieldActions exist with the same name
*
* @return ActionDetail
*/
protected ActionDetail findActionDetail(Action action, FieldType sourceType) throws AtlasException {
String actionName = action.getDisplayName();
CustomAction customAction = null;
if (action instanceof CustomAction) {
customAction = (CustomAction)action;
if (customAction.getClassName() == null || customAction.getMethodName() == null) {
throw new AtlasException("The class name and method name must be specified for custom FieldAction: " + customAction.getName());
}
}
List matches = new ArrayList<>();
for(ActionDetail actionDetail : listActionDetails()) {
if (customAction != null) {
if (customAction.getClassName().equals(actionDetail.getClassName())
&& customAction.getMethodName().equals(actionDetail.getMethod())) {
matches.add(actionDetail);
break;
}
actionDetail.getClassName();
} else if(actionDetail.getName().equals(actionName)) {
matches.add(actionDetail);
}
}
switch(matches.size()) {
case 0: return null;
case 1: return matches.get(0);
default:
if(sourceType != null && !Arrays.asList(FieldType.ANY, FieldType.NONE).contains(sourceType)) {
for(ActionDetail actionDetail : matches) {
if(sourceType.equals(actionDetail.getSourceType())) {
return actionDetail;
}
}
}
return matches.get(0);
}
}
@Override
public Field processActions(AtlasInternalSession session, Field field) throws AtlasException {
Actions actions = field.getActions();
FieldType targetType = field.getFieldType();
if(actions == null || actions.getActions() == null || actions.getActions().isEmpty()) {
return field;
}
if(FieldType.COMPLEX.equals(targetType)) {
return field;
}
Object sourceObject = field.getValue();
FieldType sourceType = (sourceObject != null ? getConversionService().fieldTypeFromClass(sourceObject.getClass()) : FieldType.NONE);
FieldGroup fieldGroup = null;
if (field instanceof FieldGroup) {
fieldGroup = (FieldGroup)field;
List © 2015 - 2025 Weber Informatics LLC | Privacy Policy