Skip to content

Commit

Permalink
Generate native binary from java code
Browse files Browse the repository at this point in the history
  • Loading branch information
viniciusfcf committed Jun 20, 2023
1 parent 570830e commit d87d3b8
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public abstract class AbstractFunctionMojo extends AbstractAppServiceMojo {
private static final String FUNCTION_REGION_KEY = "region";
private static final String FUNCTION_PRICING_KEY = "pricingTier";
private static final String FUNCTION_DEPLOY_TO_SLOT_KEY = "isDeployToFunctionSlot";
private static final String FUNCTION_NATIVE_EXECUTABLE_PATH = "nativeExecutablePath";

//region Properties
@Parameter(defaultValue = "${project.build.finalName}", readonly = true, required = true)
Expand Down Expand Up @@ -182,6 +183,25 @@ public abstract class AbstractFunctionMojo extends AbstractAppServiceMojo {
@Parameter(property = "functions.artifact")
protected String artifactPath;

/**
* Boolean flag to run function with custom runtime instead of java
*
* @since 1.27.0
*/
@Getter
@Parameter(property = "functions.nativeExecutablePath")
protected String nativeExecutablePath;

/**
* Args to be used by custom handler.
*
* @since 1.27.0
*/
@Getter
@Parameter(property = "functions.customHandlerArgs", defaultValue = "")
protected String customHandlerArgs;


@Getter
protected final ConfigParser parser = new ConfigParser(this);

Expand Down Expand Up @@ -239,6 +259,10 @@ protected File getArtifact() throws AzureToolkitRuntimeException {
return result;
}

protected boolean isNativeExecutable() {
return !StringUtils.isEmpty(getNativeExecutablePath());
}

protected File getHostJsonFile() {
final Path path = Paths.get(getHostJson());
return path.isAbsolute() ? path.toFile() :
Expand Down Expand Up @@ -323,6 +347,7 @@ public Map<String, String> getTelemetryProperties() {
result.put(DISABLE_APP_INSIGHTS_KEY, String.valueOf(isDisableAppInsights()));
final boolean isDeployToFunctionSlot = getDeploymentSlotSetting() != null && StringUtils.isNotEmpty(getDeploymentSlotSetting().getName());
result.put(FUNCTION_DEPLOY_TO_SLOT_KEY, String.valueOf(isDeployToFunctionSlot));
result.put(FUNCTION_NATIVE_EXECUTABLE_PATH, nativeExecutablePath);
return result;
}
//endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public FunctionAppConfig parseConfig() throws AzureExecutionException {
.disableAppInsights(mojo.isDisableAppInsights())
.appInsightsKey(mojo.getAppInsightsKey())
.appInsightsInstance(mojo.getAppInsightsInstance())
.nativeExecutablePath(mojo.getNativeExecutablePath())
.customHandlerArgs(mojo.getCustomHandlerArgs())
.subscriptionId(mojo.getSubscriptionId())
.resourceGroup(mojo.getResourceGroup())
.appName(mojo.getAppName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.microsoft.azure.toolkit.lib.legacy.function.handlers.CommandHandlerImpl;
import com.microsoft.azure.toolkit.lib.legacy.function.handlers.FunctionCoreToolsHandler;
import com.microsoft.azure.toolkit.lib.legacy.function.handlers.FunctionCoreToolsHandlerImpl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.io.FileUtils;
Expand Down Expand Up @@ -61,6 +62,7 @@
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -95,6 +97,7 @@ public class PackageMojo extends AbstractFunctionMojo {
public static final String SAVE_FUNCTION_JSON = "Starting processing function: ";
public static final String SAVE_SUCCESS = "Successfully saved to ";
public static final String COPY_JARS = "Step 7 of 8: Copying JARs to staging directory ";
public static final String COPY_BINARY = "Step 7 of 8: Copying Binary to staging directory ";
public static final String COPY_SUCCESS = "Copied successfully.";
public static final String INSTALL_EXTENSIONS = "Step 8 of 8: Installing function extensions if needed";
public static final String SKIP_INSTALL_EXTENSIONS_HTTP = "Skip install Function extension for HTTP Trigger Functions";
Expand All @@ -104,10 +107,12 @@ public class PackageMojo extends AbstractFunctionMojo {
public static final String FUNCTION_JSON = "function.json";
public static final String EXTENSION_BUNDLE = "extensionBundle";
private static final String AZURE_FUNCTIONS_JAVA_CORE_LIBRARY = "azure-functions-java-core-library";
private static final String DEFAULT_LOCAL_SETTINGS_JSON = "{ \"IsEncrypted\": false, \"Values\": " +
"{ \"FUNCTIONS_WORKER_RUNTIME\": \"java\" } }";
private static final String DEFAULT_LOCAL_SETTINGS_JSON = "'{' \"IsEncrypted\": false, \"Values\": " +
"'{' \"FUNCTIONS_WORKER_RUNTIME\": \"{0}\" '}' '}'";
private static final String DEFAULT_HOST_JSON = "{\"version\":\"2.0\",\"extensionBundle\":" +
"{\"id\":\"Microsoft.Azure.Functions.ExtensionBundle\",\"version\":\"[4.*, 5.0.0)\"}}\n";
private static final String DEFAULT_CUSTOM_HOST_JSON = "'{'\"version\":\"2.0\",\"extensionBundle\":" +
"'{'\"id\":\"Microsoft.Azure.Functions.ExtensionBundle\",\"version\":\"[4.*, 5.0.0)\"'}',\"customHandler\": '{'\"description\": '{'\"defaultExecutablePath\": \"{0}\",\"workingDirectory\": \"\",\"arguments\": [\"{1}\"]'}',\"enableForwardingHttpRequest\": true'}}'\n";

private static final BindingEnum[] FUNCTION_WITHOUT_FUNCTION_EXTENSION = {BindingEnum.HttpOutput, BindingEnum.HttpTrigger};
private static final String EXTENSION_BUNDLE_ID = "Microsoft.Azure.Functions.ExtensionBundle";
Expand Down Expand Up @@ -170,7 +175,12 @@ protected void doExecute() throws AzureExecutionException {

writeFunctionJsonFiles(objectWriter, configMap);

copyJarsToStageDirectory();
if (isNativeExecutable()) {
copyBinaryToStageDirectory();
} else {
copyJarsToStageDirectory();
}

} catch (IOException | MojoExecutionException e) {
throw new AzureExecutionException("Cannot perform IO operations due to error:" + e.getMessage(), e);
}
Expand All @@ -184,6 +194,21 @@ protected void doExecute() throws AzureExecutionException {
log.info(BUILD_SUCCESS);
}

protected void copyBinaryToStageDirectory() throws IOException, MojoExecutionException {
final File stagingDirectory = new File(getDeploymentStagingDirectoryPath());
log.info("");
log.info(COPY_BINARY + stagingDirectory.getAbsolutePath());
final File originalArtifact = new File(getArtifact().getParentFile(), nativeExecutablePath);

File destFile = new File(stagingDirectory, nativeExecutablePath);
log.info("src: {}", originalArtifact.getAbsolutePath());
log.info("dest: {}", destFile.getAbsolutePath());

FileUtils.copyFile(originalArtifact, destFile);

log.info(COPY_SUCCESS);
}

public static void buildArtifactWithDependencies(@Nonnull final File artifactFile, @Nullable final Set<File> dependencies, final File target) {
AzureMessager.getMessager().info("Building artifact with dependencies...");
final Shader shader = new DefaultShader();
Expand Down Expand Up @@ -316,6 +341,7 @@ protected void writeFunctionJsonFiles(final ObjectWriter objectWriter,
final Map<String, FunctionConfiguration> configMap) throws IOException {
log.info("");
log.info(SAVE_FUNCTION_JSONS);
log.info("Native: "+isNativeExecutable());
if (configMap.size() == 0) {
log.info(SAVE_SKIP);
} else {
Expand All @@ -330,6 +356,11 @@ protected void writeFunctionJsonFile(final ObjectWriter objectWriter, final Stri
log.info(SAVE_FUNCTION_JSON + functionName);
final File functionJsonFile = Paths.get(getDeploymentStagingDirectoryPath(),
functionName, FUNCTION_JSON).toFile();

if (isNativeExecutable()) {
config.setEntryPoint(null);
config.setScriptFile(null);
}
writeObjectToFile(objectWriter, config, functionJsonFile);
log.info(SAVE_SUCCESS + functionJsonFile.getAbsolutePath());
}
Expand All @@ -339,7 +370,14 @@ protected void copyHostJson() throws IOException {
log.info(SAVING_HOST_JSON);
final File sourceHostJsonFile = getHostJsonFile();
final File destHostJsonFile = Paths.get(getDeploymentStagingDirectoryPath(), HOST_JSON).toFile();
copyFilesWithDefaultContent(sourceHostJsonFile, destHostJsonFile, DEFAULT_HOST_JSON);
if (isNativeExecutable()) {
File nativeFile = new File(getNativeExecutablePath());
String newDefaultContent = MessageFormat.format(DEFAULT_CUSTOM_HOST_JSON, nativeFile.getName(), getCustomHandlerArgs());
copyFilesWithDefaultContent(sourceHostJsonFile, destHostJsonFile, newDefaultContent);
} else {
copyFilesWithDefaultContent(sourceHostJsonFile, destHostJsonFile, DEFAULT_HOST_JSON);
}

log.info(SAVE_SUCCESS + destHostJsonFile.getAbsolutePath());
}

Expand All @@ -348,10 +386,15 @@ protected void copyLocalSettingsJson() throws IOException {
log.info(SAVING_LOCAL_SETTINGS_JSON);
final File sourceLocalSettingsJsonFile = getLocalSettingsJsonFile();
final File destLocalSettingsJsonFile = Paths.get(getDeploymentStagingDirectoryPath(), LOCAL_SETTINGS_JSON).toFile();
copyFilesWithDefaultContent(sourceLocalSettingsJsonFile, destLocalSettingsJsonFile, DEFAULT_LOCAL_SETTINGS_JSON);
String defaultContent = MessageFormat.format(DEFAULT_LOCAL_SETTINGS_JSON, getWorkerRuntime());
copyFilesWithDefaultContent(sourceLocalSettingsJsonFile, destLocalSettingsJsonFile, defaultContent);
log.info(SAVE_SUCCESS + destLocalSettingsJsonFile.getAbsolutePath());
}

private String getWorkerRuntime() {
return isNativeExecutable()? "custom": "java";
}

private static void copyFilesWithDefaultContent(File source, File dest, String defaultContent)
throws IOException {
if (source != null && source.exists()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public class FunctionAppConfig extends AppServiceConfig {
private String appInsightsInstance;
private String appInsightsKey;
private boolean disableAppInsights;
private String nativeExecutablePath;
private String customHandlerArgs;
private LogAnalyticsWorkspaceConfig workspaceConfig;
}

0 comments on commit d87d3b8

Please sign in to comment.