com.boozallen.aiops.mda.metamodel.element.java.JavaStep Maven / Gradle / Ivy
package com.boozallen.aiops.mda.metamodel.element.java;
/*-
* #%L
* AIOps Foundation::AIOps MDA
* %%
* Copyright (C) 2021 Booz Allen
* %%
* This software package is licensed under the Booz Allen Public License. All Rights Reserved.
* #L%
*/
import com.boozallen.aiops.mda.generator.util.PipelineUtils;
import com.boozallen.aiops.mda.metamodel.AIOpsModelInstanceRepostory;
import com.boozallen.aiops.mda.metamodel.element.*;
import com.boozallen.aiops.mda.metamodel.element.Record;
import com.boozallen.aiops.mda.metamodel.element.util.JavaElementUtils;
import org.apache.commons.lang3.StringUtils;
import org.technologybrewery.fermenter.mda.TypeManager;
import org.technologybrewery.fermenter.mda.generator.GenerationException;
import org.technologybrewery.fermenter.mda.metamodel.ModelInstanceRepositoryManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* Step decorator to ease generation of Java files.
*/
public class JavaStep extends BaseStepDecorator {
private static final String VOID = "void";
private static final String MESSAGE = "message";
private static final String MULTI_MESSAGE = "multimessage";
private static final String DATASET = "dataset";
private static final String ROW = "row";
private static final String STRING = "string";
/**
* Uni is hard wired into our templates for a special messaging case, so it is not abstracted via the
* {@link TypeManager}.
*/
private static final String IO_SMALLRYE_MUTINY_UNI = "io.smallrye.mutiny.Uni";
private Set imports = new TreeSet<>();
private AIOpsModelInstanceRepostory metamodelRepository = ModelInstanceRepositoryManager.getMetamodelRepository(AIOpsModelInstanceRepostory.class);
/**
* {@inheritDoc}
*/
public JavaStep(Step stepToDecorate) {
super(stepToDecorate);
}
/**
* {@inheritDoc}
*/
@Override
public Persist getPersist() {
return super.getPersist() != null ? new JavaPersist(super.getPersist()) : null;
}
@Override
public JavaStepDataBinding getInbound() {
return super.getInbound() != null? new JavaStepDataBinding(super.getInbound()) : null;
}
@Override
public JavaStepDataBinding getOutbound() {
return super.getOutbound() != null? new JavaStepDataBinding(super.getOutbound()) : null;
}
/**
* Calculates the plain Java signature for this operation. It's labor intensive and messy in velocity, so much more
* clean to do it in the Java decorator.
*
* @return signature
*/
public String getBaseSignature() {
String inputType = getBaseInboundType();
String outboundType = getBaseOutboundType();
return createSignature("executeStep", "public", inputType, outboundType, false);
}
/**
* Calculates the plain Java signature for this operation. It's labor intensive and messy in velocity, so much more
* clean to do it in the Java decorator.
*
* @return signature
*/
public String getAbstractImplSignature() {
return getImplSignature(true);
}
/**
* Calculates the plain Java signature for this operation. It's labor intensive and messy in velocity, so much more
* clean to do it in the Java decorator.
*
* @return signature
*/
public String getConcreteImplSignature() {
return getImplSignature(false);
}
/**
* Calculates the signature for the encryption.
*
* @return encryption method signature
*/
public String getEncryptionSignature() {
String inputType = getBaseInboundType();
return createSignature("checkAndApplyEncryptionPolicy", "protected", inputType, inputType, false);
}
private String getImplSignature(boolean isAbstract) {
String inputType;
String outboundType;
if ("asynchronous".equalsIgnoreCase(getType())) {
inputType = getBaseInboundType();
outboundType = getBaseOutboundType();
} else {
inputType = getImplInboundType();
outboundType = getImplOutboundType();
}
return createSignature("executeStepImpl", "protected", inputType, outboundType, isAbstract);
}
private String createSignature(String methodName, String visibility, String inputType, String outputType,
boolean isAbstract) {
StringBuilder builder = new StringBuilder();
builder.append(visibility).append(" ");
if (isAbstract) {
builder.append("abstract ");
}
builder.append(outputType).append(" ");
builder.append(methodName).append("(");
if (StringUtils.isNotBlank(inputType)) {
builder.append(inputType);
builder.append(" inbound");
}
builder.append(")");
return builder.toString();
}
public boolean hasVoidInbound() {
return isVoid(getInbound());
}
public boolean hasVoidOutbound() {
return isVoid(getOutbound());
}
public boolean useEmitter() {
return hasNativeInbound() || hasVoidInbound();
}
public Set getBaseImports() {
getBaseSignature();
addPersistImports();
return imports;
}
public Set getImplImports() {
getImplSignature(false);
return imports;
}
@Override
public boolean isMessaging(StepDataBinding stepDataBinding) {
return stepDataBinding != null
&& ("messaging".equalsIgnoreCase(stepDataBinding.getType()));
}
public boolean isMultiMessaging(StepDataBinding stepDataBinding) {
return stepDataBinding != null && "multimessaging".equalsIgnoreCase(stepDataBinding.getType());
}
/**
* If the outbound type of this step is messaging, this method will return the Reactive Messaging outgoing channel
* name. Note this is not the same as {@link StepDataBinding#getChannelName()} and is simply an externality of our
* utilization of Reactive Messaging to proxy the backing messaging provider (i.e. Kafka).
*
* @return the outgoing channel name for Reactive Messaging
* @throws GenerationException if this step does not have a messaging outbound
*/
public String getOutgoingChannel() {
if(!hasMessagingOutbound()) {
throw new GenerationException("Cannot retrieve outgoing channel name for non-messaging outbound: " + getName());
}
return getHyphenatedName() + "-out" ;
}
/**
* If the inbound type of this step is messaging, this method will return the Reactive Messaging incoming channel
* name. Note this is not the same as {@link StepDataBinding#getChannelName()} and is simply an externality of our
* utilization of Reactive Messaging to proxy the backing messaging provider (i.e. Kafka).
*
* @return the incoming channel name for Reactive Messaging
* @throws GenerationException if this step does not have a messaging inbound
*/
public String getIncomingChannel() {
if(!hasMessagingInbound()) {
throw new GenerationException("Cannot retrieve incoming channel name for non-messaging inbound: " + getName());
}
return getHyphenatedName() + "-in" ;
}
/**
* Converts the camel-case step name to a lowercase hyphenated version. E.g. `IngestAWSInfo` => `ingest-aws-info`
*
* @return hyphenated version of the step name
*/
private String getHyphenatedName() {
String[] nameParts = StringUtils.splitByCharacterTypeCamelCase(getName());
return StringUtils.join(nameParts, '-').toLowerCase();
}
public boolean isVoid(StepDataBinding stepDataBinding) {
return stepDataBinding == null || VOID.equalsIgnoreCase(stepDataBinding.getType());
}
public String getBaseInboundType() {
String inboundType = null;
String implInboundType = getImplInboundType();
if (hasMessagingInbound()) {
String messageType = TypeManager.getShortType(MESSAGE);
inboundType = messageType + "<" + implInboundType + ">";
if (isMultiMessaging(getInbound())) {
inboundType = TypeManager.getShortType(MULTI_MESSAGE) + "<" + inboundType + ">";
}
addImport(TypeManager.getFullyQualifiedType("incoming"));
addImport(TypeManager.getFullyQualifiedType(MESSAGE));
} else if (!hasVoidInbound()) {
inboundType = implInboundType;
}
return inboundType;
}
public String getBaseOutboundType() {
if ("asynchronous".equalsIgnoreCase(getType())) {
return getAsyncBaseOutboundType();
} else {
return getSyncBaseOutboundType();
}
}
public String getAsyncBaseOutboundType() {
String outboundType = "CompletionStage";
StepDataBinding outbound = getOutbound();
String implOutboundType = getImplOutboundType();
if (outbound == null) {
outboundType += "";
} else {
List types = new ArrayList<>();
if (isMessaging(outbound)) {
types.add(" builder.append(">"));
outboundType += builder.toString();
}
return outboundType;
}
public String getSyncBaseOutboundType() {
String outboundType = "";
String implOutboundType = getImplOutboundType();
boolean hasMessagingInbound = hasMessagingInbound();
StepDataBinding outbound = getOutbound();
if (outbound == null && hasMessagingInbound) {
outboundType = "Uni";
addImport(IO_SMALLRYE_MUTINY_UNI);
} else if (hasVoidOutbound() || (hasMessagingOutbound() && hasNativeInbound())) {
outboundType = VOID;
} else if (hasMessagingOutbound()) {
if (hasMultiMessagingOutbound()) {
outboundType = TypeManager.getShortType(MULTI_MESSAGE) + "<" + TypeManager.getShortType(MESSAGE) + ">";
addImport(TypeManager.getFullyQualifiedType(MULTI_MESSAGE));
} else {
String messageType = TypeManager.getShortType(MESSAGE);
outboundType = messageType + "<" + implOutboundType + ">";
}
addImport(TypeManager.getFullyQualifiedType("outgoing"));
addImport(TypeManager.getFullyQualifiedType(MESSAGE));
} else {
if (hasMessagingInbound) {
outboundType = "Uni<" + implOutboundType + ">";
addImport(IO_SMALLRYE_MUTINY_UNI);
} else {
outboundType = implOutboundType;
}
}
return outboundType;
}
public String getImplInboundType() {
StepDataBinding inbound = getInbound();
return deriveImplType(inbound);
}
public String getImplOutboundType() {
String outboundType;
StepDataBinding outbound = getOutbound();
if (hasVoidOutbound()) {
outboundType = VOID;
} else {
outboundType = deriveImplType(outbound);
}
return outboundType;
}
private String deriveImplType(StepDataBinding dataBinding) {
String implementationType = null;
String javaNativeCollectionTypeName = null;
String javaNativeCollectionImportName = null;
String javaRecordTypeName = null;
String javaRecordImportName = null;
if (hasRecordType(dataBinding)) {
JavaStepDataRecordType javaRecordType = new JavaStepDataRecordType(dataBinding.getRecordType());
javaRecordTypeName = javaRecordType.getName();
javaRecordImportName = javaRecordType.getFullyQualifiedType();
} else {
// default record types
if (isMessaging(dataBinding) || isMultiMessaging(dataBinding)) {
javaRecordTypeName = TypeManager.getShortType(STRING);
javaRecordImportName = TypeManager.getFullyQualifiedType(STRING);
} else if (isNative(dataBinding)) {
javaRecordTypeName = TypeManager.getShortType(ROW);
javaRecordImportName = TypeManager.getFullyQualifiedType(ROW);
}
}
if (isNative(dataBinding)) {
if (dataBinding.getNativeCollectionType() != null) {
JavaStepDataCollectionType javaNativeCollectionType = new JavaStepDataCollectionType(
dataBinding.getNativeCollectionType());
javaNativeCollectionTypeName = javaNativeCollectionType.getShortType();
javaNativeCollectionImportName = javaNativeCollectionType.getFullyQualifiedType();
} else {
// default native collection type
javaNativeCollectionTypeName = TypeManager.getShortType(DATASET);
javaNativeCollectionImportName = TypeManager.getFullyQualifiedType(DATASET);
}
addImport(javaNativeCollectionImportName);
}
if (StringUtils.isNotBlank(javaNativeCollectionTypeName)) {
implementationType = javaNativeCollectionTypeName + "<" + javaRecordTypeName + ">";
addRecordImports(javaRecordImportName);
} else if (!(isVoid(dataBinding))) {
implementationType = javaRecordTypeName;
addRecordImports(javaRecordImportName);
}
return implementationType;
}
/**
* Adds imports, but pulls out items that don't need to be specified for a more clean result.
*/
private void addImport(String importValue) {
if (JavaElementUtils.isImportNeeded(importValue)) {
imports.add(importValue);
}
}
private void addPersistImports() {
JavaPersist javaPersist = (JavaPersist) getPersist();
if (javaPersist != null) {
for (String importValue : javaPersist.getImports()) {
addImport(importValue);
}
}
}
@Override
public void validate() {
super.validate();
if (hasMessagingInbound() && hasNativeOutbound()) {
throw new GenerationException("Step '" + getName() + "' uses messaging inbound and native outbound. "
+ "This combination cannot be used together as it is not possible for a synchronous consumer to be "
+ "listening for an asynchonrous event without also messaging being used as the outbound type.");
}
}
/**
* Returns the instantiation type of a step's native collection type.
*
* @return native collection instantiation type
*/
public String getNativeCollectionInstantiationType() {
String instantationType = null;
if (getInbound() != null && getInbound().getNativeCollectionType() != null) {
DictionaryType dictionaryType = getInbound().getNativeCollectionType().getDictionaryType();
String simpleType = dictionaryType.getSimpleType();
String fullyQualifiedType = TypeManager.getFullyQualifiedType(simpleType);
if (JavaElementUtils.LIST_IMPORT.equals(fullyQualifiedType)) {
instantationType = "ArrayList";
} else if (JavaElementUtils.SET_IMPORT.equals(fullyQualifiedType)) {
instantationType = "HashSet";
} else {
instantationType = TypeManager.getShortType(simpleType);
}
}
return instantationType;
}
/**
* Returns the record name in lower camel case format.
*
* @return lower camel case name
*/
public String getLowerCamelCaseName() {
return PipelineUtils.deriveLowerCamelNameFromUpperCamelName(getName());
}
private void addRecordImports(String importName) {
String packageName = importName.substring(0, importName.lastIndexOf('.'));
String className = importName.substring(importName.lastIndexOf('.')+1);
Record record = metamodelRepository.getRecord(packageName, className);
imports.add(importName);
if(record != null) {
imports.add(record.getPackage() + "." + record.getName() + "Schema");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy