Skip to content

Commit d87d3b8

Browse files
committed
Generate native binary from java code
1 parent 570830e commit d87d3b8

File tree

4 files changed

+77
-5
lines changed

4 files changed

+77
-5
lines changed

azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/AbstractFunctionMojo.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public abstract class AbstractFunctionMojo extends AbstractAppServiceMojo {
6060
private static final String FUNCTION_REGION_KEY = "region";
6161
private static final String FUNCTION_PRICING_KEY = "pricingTier";
6262
private static final String FUNCTION_DEPLOY_TO_SLOT_KEY = "isDeployToFunctionSlot";
63+
private static final String FUNCTION_NATIVE_EXECUTABLE_PATH = "nativeExecutablePath";
6364

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

186+
/**
187+
* Boolean flag to run function with custom runtime instead of java
188+
*
189+
* @since 1.27.0
190+
*/
191+
@Getter
192+
@Parameter(property = "functions.nativeExecutablePath")
193+
protected String nativeExecutablePath;
194+
195+
/**
196+
* Args to be used by custom handler.
197+
*
198+
* @since 1.27.0
199+
*/
200+
@Getter
201+
@Parameter(property = "functions.customHandlerArgs", defaultValue = "")
202+
protected String customHandlerArgs;
203+
204+
185205
@Getter
186206
protected final ConfigParser parser = new ConfigParser(this);
187207

@@ -239,6 +259,10 @@ protected File getArtifact() throws AzureToolkitRuntimeException {
239259
return result;
240260
}
241261

262+
protected boolean isNativeExecutable() {
263+
return !StringUtils.isEmpty(getNativeExecutablePath());
264+
}
265+
242266
protected File getHostJsonFile() {
243267
final Path path = Paths.get(getHostJson());
244268
return path.isAbsolute() ? path.toFile() :
@@ -323,6 +347,7 @@ public Map<String, String> getTelemetryProperties() {
323347
result.put(DISABLE_APP_INSIGHTS_KEY, String.valueOf(isDisableAppInsights()));
324348
final boolean isDeployToFunctionSlot = getDeploymentSlotSetting() != null && StringUtils.isNotEmpty(getDeploymentSlotSetting().getName());
325349
result.put(FUNCTION_DEPLOY_TO_SLOT_KEY, String.valueOf(isDeployToFunctionSlot));
350+
result.put(FUNCTION_NATIVE_EXECUTABLE_PATH, nativeExecutablePath);
326351
return result;
327352
}
328353
//endregion

azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/ConfigParser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public FunctionAppConfig parseConfig() throws AzureExecutionException {
3434
.disableAppInsights(mojo.isDisableAppInsights())
3535
.appInsightsKey(mojo.getAppInsightsKey())
3636
.appInsightsInstance(mojo.getAppInsightsInstance())
37+
.nativeExecutablePath(mojo.getNativeExecutablePath())
38+
.customHandlerArgs(mojo.getCustomHandlerArgs())
3739
.subscriptionId(mojo.getSubscriptionId())
3840
.resourceGroup(mojo.getResourceGroup())
3941
.appName(mojo.getAppName())

azure-functions-maven-plugin/src/main/java/com/microsoft/azure/maven/function/PackageMojo.java

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.microsoft.azure.toolkit.lib.legacy.function.handlers.CommandHandlerImpl;
3030
import com.microsoft.azure.toolkit.lib.legacy.function.handlers.FunctionCoreToolsHandler;
3131
import com.microsoft.azure.toolkit.lib.legacy.function.handlers.FunctionCoreToolsHandlerImpl;
32+
3233
import lombok.extern.slf4j.Slf4j;
3334
import org.apache.commons.collections4.SetUtils;
3435
import org.apache.commons.io.FileUtils;
@@ -61,6 +62,7 @@
6162
import java.net.URL;
6263
import java.nio.charset.Charset;
6364
import java.nio.file.Paths;
65+
import java.text.MessageFormat;
6466
import java.util.ArrayList;
6567
import java.util.Arrays;
6668
import java.util.Collections;
@@ -95,6 +97,7 @@ public class PackageMojo extends AbstractFunctionMojo {
9597
public static final String SAVE_FUNCTION_JSON = "Starting processing function: ";
9698
public static final String SAVE_SUCCESS = "Successfully saved to ";
9799
public static final String COPY_JARS = "Step 7 of 8: Copying JARs to staging directory ";
100+
public static final String COPY_BINARY = "Step 7 of 8: Copying Binary to staging directory ";
98101
public static final String COPY_SUCCESS = "Copied successfully.";
99102
public static final String INSTALL_EXTENSIONS = "Step 8 of 8: Installing function extensions if needed";
100103
public static final String SKIP_INSTALL_EXTENSIONS_HTTP = "Skip install Function extension for HTTP Trigger Functions";
@@ -104,10 +107,12 @@ public class PackageMojo extends AbstractFunctionMojo {
104107
public static final String FUNCTION_JSON = "function.json";
105108
public static final String EXTENSION_BUNDLE = "extensionBundle";
106109
private static final String AZURE_FUNCTIONS_JAVA_CORE_LIBRARY = "azure-functions-java-core-library";
107-
private static final String DEFAULT_LOCAL_SETTINGS_JSON = "{ \"IsEncrypted\": false, \"Values\": " +
108-
"{ \"FUNCTIONS_WORKER_RUNTIME\": \"java\" } }";
110+
private static final String DEFAULT_LOCAL_SETTINGS_JSON = "'{' \"IsEncrypted\": false, \"Values\": " +
111+
"'{' \"FUNCTIONS_WORKER_RUNTIME\": \"{0}\" '}' '}'";
109112
private static final String DEFAULT_HOST_JSON = "{\"version\":\"2.0\",\"extensionBundle\":" +
110113
"{\"id\":\"Microsoft.Azure.Functions.ExtensionBundle\",\"version\":\"[4.*, 5.0.0)\"}}\n";
114+
private static final String DEFAULT_CUSTOM_HOST_JSON = "'{'\"version\":\"2.0\",\"extensionBundle\":" +
115+
"'{'\"id\":\"Microsoft.Azure.Functions.ExtensionBundle\",\"version\":\"[4.*, 5.0.0)\"'}',\"customHandler\": '{'\"description\": '{'\"defaultExecutablePath\": \"{0}\",\"workingDirectory\": \"\",\"arguments\": [\"{1}\"]'}',\"enableForwardingHttpRequest\": true'}}'\n";
111116

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

171176
writeFunctionJsonFiles(objectWriter, configMap);
172177

173-
copyJarsToStageDirectory();
178+
if (isNativeExecutable()) {
179+
copyBinaryToStageDirectory();
180+
} else {
181+
copyJarsToStageDirectory();
182+
}
183+
174184
} catch (IOException | MojoExecutionException e) {
175185
throw new AzureExecutionException("Cannot perform IO operations due to error:" + e.getMessage(), e);
176186
}
@@ -184,6 +194,21 @@ protected void doExecute() throws AzureExecutionException {
184194
log.info(BUILD_SUCCESS);
185195
}
186196

197+
protected void copyBinaryToStageDirectory() throws IOException, MojoExecutionException {
198+
final File stagingDirectory = new File(getDeploymentStagingDirectoryPath());
199+
log.info("");
200+
log.info(COPY_BINARY + stagingDirectory.getAbsolutePath());
201+
final File originalArtifact = new File(getArtifact().getParentFile(), nativeExecutablePath);
202+
203+
File destFile = new File(stagingDirectory, nativeExecutablePath);
204+
log.info("src: {}", originalArtifact.getAbsolutePath());
205+
log.info("dest: {}", destFile.getAbsolutePath());
206+
207+
FileUtils.copyFile(originalArtifact, destFile);
208+
209+
log.info(COPY_SUCCESS);
210+
}
211+
187212
public static void buildArtifactWithDependencies(@Nonnull final File artifactFile, @Nullable final Set<File> dependencies, final File target) {
188213
AzureMessager.getMessager().info("Building artifact with dependencies...");
189214
final Shader shader = new DefaultShader();
@@ -316,6 +341,7 @@ protected void writeFunctionJsonFiles(final ObjectWriter objectWriter,
316341
final Map<String, FunctionConfiguration> configMap) throws IOException {
317342
log.info("");
318343
log.info(SAVE_FUNCTION_JSONS);
344+
log.info("Native: "+isNativeExecutable());
319345
if (configMap.size() == 0) {
320346
log.info(SAVE_SKIP);
321347
} else {
@@ -330,6 +356,11 @@ protected void writeFunctionJsonFile(final ObjectWriter objectWriter, final Stri
330356
log.info(SAVE_FUNCTION_JSON + functionName);
331357
final File functionJsonFile = Paths.get(getDeploymentStagingDirectoryPath(),
332358
functionName, FUNCTION_JSON).toFile();
359+
360+
if (isNativeExecutable()) {
361+
config.setEntryPoint(null);
362+
config.setScriptFile(null);
363+
}
333364
writeObjectToFile(objectWriter, config, functionJsonFile);
334365
log.info(SAVE_SUCCESS + functionJsonFile.getAbsolutePath());
335366
}
@@ -339,7 +370,14 @@ protected void copyHostJson() throws IOException {
339370
log.info(SAVING_HOST_JSON);
340371
final File sourceHostJsonFile = getHostJsonFile();
341372
final File destHostJsonFile = Paths.get(getDeploymentStagingDirectoryPath(), HOST_JSON).toFile();
342-
copyFilesWithDefaultContent(sourceHostJsonFile, destHostJsonFile, DEFAULT_HOST_JSON);
373+
if (isNativeExecutable()) {
374+
File nativeFile = new File(getNativeExecutablePath());
375+
String newDefaultContent = MessageFormat.format(DEFAULT_CUSTOM_HOST_JSON, nativeFile.getName(), getCustomHandlerArgs());
376+
copyFilesWithDefaultContent(sourceHostJsonFile, destHostJsonFile, newDefaultContent);
377+
} else {
378+
copyFilesWithDefaultContent(sourceHostJsonFile, destHostJsonFile, DEFAULT_HOST_JSON);
379+
}
380+
343381
log.info(SAVE_SUCCESS + destHostJsonFile.getAbsolutePath());
344382
}
345383

@@ -348,10 +386,15 @@ protected void copyLocalSettingsJson() throws IOException {
348386
log.info(SAVING_LOCAL_SETTINGS_JSON);
349387
final File sourceLocalSettingsJsonFile = getLocalSettingsJsonFile();
350388
final File destLocalSettingsJsonFile = Paths.get(getDeploymentStagingDirectoryPath(), LOCAL_SETTINGS_JSON).toFile();
351-
copyFilesWithDefaultContent(sourceLocalSettingsJsonFile, destLocalSettingsJsonFile, DEFAULT_LOCAL_SETTINGS_JSON);
389+
String defaultContent = MessageFormat.format(DEFAULT_LOCAL_SETTINGS_JSON, getWorkerRuntime());
390+
copyFilesWithDefaultContent(sourceLocalSettingsJsonFile, destLocalSettingsJsonFile, defaultContent);
352391
log.info(SAVE_SUCCESS + destLocalSettingsJsonFile.getAbsolutePath());
353392
}
354393

394+
private String getWorkerRuntime() {
395+
return isNativeExecutable()? "custom": "java";
396+
}
397+
355398
private static void copyFilesWithDefaultContent(File source, File dest, String defaultContent)
356399
throws IOException {
357400
if (source != null && source.exists()) {

azure-toolkit-libs/azure-toolkit-appservice-lib/src/main/java/com/microsoft/azure/toolkit/lib/appservice/config/FunctionAppConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ public class FunctionAppConfig extends AppServiceConfig {
1616
private String appInsightsInstance;
1717
private String appInsightsKey;
1818
private boolean disableAppInsights;
19+
private String nativeExecutablePath;
20+
private String customHandlerArgs;
1921
private LogAnalyticsWorkspaceConfig workspaceConfig;
2022
}

0 commit comments

Comments
 (0)