Azure Functions Java Worker enables running Java functions in Azure Functions runtime. This guide covers creating, deploying, and managing Java functions with Azure Functions.
Setup and Dependencies
1. Maven Configuration
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.company.azure.functions</groupId> <artifactId>azure-java-functions</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <functionAppName>java-functions-app</functionAppName> <functionAppRegion>eastus</functionAppRegion> <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory> <azure.functions.maven.plugin.version>1.26.0</azure.functions.maven.plugin.version> <azure.functions.java.library.version>3.0.0</azure.functions.java.library.version> </properties> <dependencies> <!-- Azure Functions Java Library --> <dependency> <groupId>com.microsoft.azure.functions</groupId> <artifactId>azure-functions-java-library</artifactId> <version>${azure.functions.java.library.version}</version> </dependency> <!-- Azure Storage SDK for Blob, Queue, Table bindings --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-storage-blob</artifactId> <version>12.20.0</version> </dependency> <dependency> <groupId>com.azure</groupId> <artifactId>azure-storage-queue</artifactId> <version>12.16.0</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- HTTP Client --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.11.0</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.7</version> </dependency> <!-- Testing --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> <dependency> <groupId>com.microsoft.azure.functions</groupId> <artifactId>azure-functions-java-library-test</artifactId> <version>1.0.0</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>com.microsoft.azure</groupId> <artifactId>azure-functions-maven-plugin</artifactId> <version>${azure.functions.maven.plugin.version}</version> <configuration> <appName>${functionAppName}</appName> <resourceGroup>java-functions-rg</resourceGroup> <appServicePlanName>java-functions-asp</appServicePlanName> <region>${functionAppRegion}</region> <runtime> <os>windows</os> <javaVersion>11</javaVersion> </runtime> <appSettings> <property> <name>FUNCTIONS_EXTENSION_VERSION</name> <value>~4</value> </property> <property> <name>FUNCTIONS_WORKER_RUNTIME</name> <value>java</value> </property> </appSettings> </configuration> <executions> <execution> <id>package-functions</id> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>11</source> <target>11</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </build> </project> 2. Host Configuration
{ "version": "2.0", "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[2.*, 3.0.0)" }, "extensions": { "http": { "routePrefix": "api", "maxOutstandingRequests": 200, "maxConcurrentRequests": 100, "dynamicThrottlesEnabled": true }, "queues": { "batchSize": 16, "newBatchThreshold": 8, "maxDequeueCount": 5, "maxPollingInterval": "00:00:02" }, "serviceBus": { "prefetchCount": 100, "messageHandlerOptions": { "autoComplete": true, "maxConcurrentCalls": 32, "maxAutoRenewDuration": "00:05:00" } } }, "logging": { "fileLoggingMode": "debugOnly", "logLevel": { "default": "Information", "Host.Results": "Error", "Function": "Information", "Host.Aggregator": "Trace" }, "applicationInsights": { "samplingSettings": { "isEnabled": true, "maxTelemetryItemsPerSecond": 5 } } }, "functionTimeout": "00:10:00", "healthMonitor": { "enabled": true, "healthCheckInterval": "00:00:10", "healthCheckWindow": "00:02:00", "healthCheckThreshold": 6, "counterThreshold": 0.80 } } Core Function Implementations
1. HTTP Trigger Functions
package com.company.azure.functions; import com.microsoft.azure.functions.*; import com.microsoft.azure.functions.annotation.*; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * HTTP Trigger Functions for User Management */ public class UserFunctions { private static final Logger LOGGER = Logger.getLogger(UserFunctions.class.getName()); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Map<String, User> USER_STORE = new ConcurrentHashMap<>(); private static final Random RANDOM = new Random(); /** * HTTP POST - Create User */ @FunctionName("createUser") public HttpResponseMessage createUser( @HttpTrigger( name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION, route = "users" ) HttpRequestMessage<Optional<String>> request, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger processed a request to create user."); try { // Parse request body String requestBody = request.getBody().orElseThrow(() -> new IllegalArgumentException("Request body is required")); UserCreateRequest createRequest = OBJECT_MAPPER.readValue(requestBody, UserCreateRequest.class); // Validate input if (createRequest.getEmail() == null || createRequest.getEmail().trim().isEmpty()) { return request.createResponseBuilder(HttpStatus.BAD_REQUEST) .body("{\"error\": \"Email is required\"}") .header("Content-Type", "application/json") .build(); } // Check for duplicate email boolean emailExists = USER_STORE.values().stream() .anyMatch(user -> user.getEmail().equalsIgnoreCase(createRequest.getEmail())); if (emailExists) { return request.createResponseBuilder(HttpStatus.CONFLICT) .body("{\"error\": \"User with this email already exists\"}") .header("Content-Type", "application/json") .build(); } // Create user String userId = "user-" + System.currentTimeMillis() + "-" + RANDOM.nextInt(1000); User user = new User( userId, createRequest.getEmail(), createRequest.getFirstName(), createRequest.getLastName(), System.currentTimeMillis() ); USER_STORE.put(userId, user); // Log success context.getLogger().info("User created successfully: " + userId); // Return response UserResponse response = new UserResponse( "User created successfully", user ); return request.createResponseBuilder(HttpStatus.CREATED) .body(OBJECT_MAPPER.writeValueAsString(response)) .header("Content-Type", "application/json") .build(); } catch (IOException e) { context.getLogger().severe("Error parsing request body: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.BAD_REQUEST) .body("{\"error\": \"Invalid JSON format\"}") .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error creating user: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Internal server error\"}") .header("Content-Type", "application/json") .build(); } } /** * HTTP GET - Get User by ID */ @FunctionName("getUser") public HttpResponseMessage getUser( @HttpTrigger( name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.FUNCTION, route = "users/{id}" ) HttpRequestMessage<Optional<String>> request, @BindingName("id") String userId, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger processed a request to get user: " + userId); try { User user = USER_STORE.get(userId); if (user == null) { return request.createResponseBuilder(HttpStatus.NOT_FOUND) .body("{\"error\": \"User not found\"}") .header("Content-Type", "application/json") .build(); } UserResponse response = new UserResponse( "User retrieved successfully", user ); return request.createResponseBuilder(HttpStatus.OK) .body(OBJECT_MAPPER.writeValueAsString(response)) .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error retrieving user: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Internal server error\"}") .header("Content-Type", "application/json") .build(); } } /** * HTTP GET - List All Users */ @FunctionName("listUsers") public HttpResponseMessage listUsers( @HttpTrigger( name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.FUNCTION, route = "users" ) HttpRequestMessage<Optional<String>> request, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger processed a request to list users."); try { List<User> users = new ArrayList<>(USER_STORE.values()); UserListResponse response = new UserListResponse( "Users retrieved successfully", users.size(), users ); return request.createResponseBuilder(HttpStatus.OK) .body(OBJECT_MAPPER.writeValueAsString(response)) .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error listing users: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Internal server error\"}") .header("Content-Type", "application/json") .build(); } } /** * HTTP PUT - Update User */ @FunctionName("updateUser") public HttpResponseMessage updateUser( @HttpTrigger( name = "req", methods = {HttpMethod.PUT}, authLevel = AuthorizationLevel.FUNCTION, route = "users/{id}" ) HttpRequestMessage<Optional<String>> request, @BindingName("id") String userId, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger processed a request to update user: " + userId); try { User existingUser = USER_STORE.get(userId); if (existingUser == null) { return request.createResponseBuilder(HttpStatus.NOT_FOUND) .body("{\"error\": \"User not found\"}") .header("Content-Type", "application/json") .build(); } String requestBody = request.getBody().orElseThrow(() -> new IllegalArgumentException("Request body is required")); UserUpdateRequest updateRequest = OBJECT_MAPPER.readValue(requestBody, UserUpdateRequest.class); // Update user User updatedUser = new User( existingUser.getId(), existingUser.getEmail(), // Email cannot be changed updateRequest.getFirstName() != null ? updateRequest.getFirstName() : existingUser.getFirstName(), updateRequest.getLastName() != null ? updateRequest.getLastName() : existingUser.getLastName(), existingUser.getCreatedAt() ); USER_STORE.put(userId, updatedUser); UserResponse response = new UserResponse( "User updated successfully", updatedUser ); return request.createResponseBuilder(HttpStatus.OK) .body(OBJECT_MAPPER.writeValueAsString(response)) .header("Content-Type", "application/json") .build(); } catch (IOException e) { context.getLogger().severe("Error parsing request body: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.BAD_REQUEST) .body("{\"error\": \"Invalid JSON format\"}") .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error updating user: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Internal server error\"}") .header("Content-Type", "application/json") .build(); } } /** * HTTP DELETE - Delete User */ @FunctionName("deleteUser") public HttpResponseMessage deleteUser( @HttpTrigger( name = "req", methods = {HttpMethod.DELETE}, authLevel = AuthorizationLevel.FUNCTION, route = "users/{id}" ) HttpRequestMessage<Optional<String>> request, @BindingName("id") String userId, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger processed a request to delete user: " + userId); try { User removedUser = USER_STORE.remove(userId); if (removedUser == null) { return request.createResponseBuilder(HttpStatus.NOT_FOUND) .body("{\"error\": \"User not found\"}") .header("Content-Type", "application/json") .build(); } DeleteResponse response = new DeleteResponse( "User deleted successfully", userId, System.currentTimeMillis() ); return request.createResponseBuilder(HttpStatus.OK) .body(OBJECT_MAPPER.writeValueAsString(response)) .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error deleting user: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Internal server error\"}") .header("Content-Type", "application/json") .build(); } } // Data Models public static class UserCreateRequest { private String email; private String firstName; private String lastName; // Getters and setters public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } public static class UserUpdateRequest { private String firstName; private String lastName; // Getters and setters public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } public static class User { private final String id; private final String email; private final String firstName; private final String lastName; private final long createdAt; public User(String id, String email, String firstName, String lastName, long createdAt) { this.id = id; this.email = email; this.firstName = firstName; this.lastName = lastName; this.createdAt = createdAt; } // Getters public String getId() { return id; } public String getEmail() { return email; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public long getCreatedAt() { return createdAt; } } public static class UserResponse { private final String message; private final User user; public UserResponse(String message, User user) { this.message = message; this.user = user; } // Getters public String getMessage() { return message; } public User getUser() { return user; } } public static class UserListResponse { private final String message; private final int total; private final List<User> users; public UserListResponse(String message, int total, List<User> users) { this.message = message; this.total = total; this.users = users; } // Getters public String getMessage() { return message; } public int getTotal() { return total; } public List<User> getUsers() { return users; } } public static class DeleteResponse { private final String message; private final String userId; private final long deletedAt; public DeleteResponse(String message, String userId, long deletedAt) { this.message = message; this.userId = userId; this.deletedAt = deletedAt; } // Getters public String getMessage() { return message; } public String getUserId() { return userId; } public long getDeletedAt() { return deletedAt; } } } 2. Blob Storage Trigger Functions
package com.company.azure.functions; import com.microsoft.azure.functions.*; import com.microsoft.azure.functions.annotation.*; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.BlobServiceClientBuilder; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.logging.Logger; /** * Blob Storage Trigger Functions */ public class BlobStorageFunctions { private static final Logger LOGGER = Logger.getLogger(BlobStorageFunctions.class.getName()); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static BlobServiceClient blobServiceClient; static { // Initialize Blob Service Client String connectionString = System.getenv("AzureWebJobsStorage"); blobServiceClient = new BlobServiceClientBuilder() .connectionString(connectionString) .buildClient(); } /** * Blob Trigger - Process new user data files */ @FunctionName("processUserDataBlob") public void processUserDataBlob( @BlobTrigger( name = "content", path = "user-data/{name}", dataType = "binary", connection = "AzureWebJobsStorage" ) byte[] content, @BindingName("name") String filename, final ExecutionContext context) { context.getLogger().info("Java Blob trigger function processed a blob: " + filename); try { String fileContent = new String(content, StandardCharsets.UTF_8); context.getLogger().info("Blob content: " + fileContent); // Process the blob content UserBatchData batchData = OBJECT_MAPPER.readValue(fileContent, UserBatchData.class); context.getLogger().info(String.format( "Processing batch data with %d users", batchData.getUsers().size())); // Process each user in the batch for (UserBatchData.UserData userData : batchData.getUsers()) { processUserData(userData, context); } // Move processed blob to archive container moveBlobToArchive(filename, content, context); context.getLogger().info("Successfully processed blob: " + filename); } catch (Exception e) { context.getLogger().severe("Error processing blob " + filename + ": " + e.getMessage()); // Move failed blob to error container moveBlobToError(filename, content, context); } } /** * Blob Output - Generate user report */ @FunctionName("generateUserReport") public HttpResponseMessage generateUserReport( @HttpTrigger( name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.FUNCTION, route = "reports/users" ) HttpRequestMessage<Optional<String>> request, @BlobOutput( name = "report", path = "reports/user-report-{datetime}.json", connection = "AzureWebJobsStorage" ) OutputBinding<String> reportOutput, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger function processed a request to generate user report."); try { // Generate report data UserReport report = generateUserReportData(); String reportJson = OBJECT_MAPPER.writeValueAsString(report); // Set output blob content reportOutput.setValue(reportJson); return request.createResponseBuilder(HttpStatus.OK) .body("{\"message\": \"User report generated successfully\", \"reportPath\": \"reports/user-report-\" + System.currentTimeMillis() + \".json\"}") .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error generating user report: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Failed to generate report\"}") .header("Content-Type", "application/json") .build(); } } private void processUserData(UserBatchData.UserData userData, ExecutionContext context) { context.getLogger().info("Processing user: " + userData.getEmail()); // Implement user processing logic // This could include validation, transformation, storage, etc. try { Thread.sleep(100); // Simulate processing time context.getLogger().info("Successfully processed user: " + userData.getEmail()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); context.getLogger().warning("User processing interrupted: " + userData.getEmail()); } } private void moveBlobToArchive(String filename, byte[] content, ExecutionContext context) { try { BlobContainerClient archiveContainer = blobServiceClient.getBlobContainerClient("user-data-archive"); if (!archiveContainer.exists()) { archiveContainer.create(); } BlobClient archiveBlob = archiveContainer.getBlobClient(filename); archiveBlob.upload(new ByteArrayInputStream(content), content.length, true); // Delete from original container BlobContainerClient sourceContainer = blobServiceClient.getBlobContainerClient("user-data"); BlobClient sourceBlob = sourceContainer.getBlobClient(filename); sourceBlob.delete(); context.getLogger().info("Moved blob to archive: " + filename); } catch (Exception e) { context.getLogger().severe("Error moving blob to archive: " + e.getMessage()); } } private void moveBlobToError(String filename, byte[] content, ExecutionContext context) { try { BlobContainerClient errorContainer = blobServiceClient.getBlobContainerClient("user-data-errors"); if (!errorContainer.exists()) { errorContainer.create(); } BlobClient errorBlob = errorContainer.getBlobClient(filename); errorBlob.upload(new ByteArrayInputStream(content), content.length, true); // Delete from original container BlobContainerClient sourceContainer = blobServiceClient.getBlobContainerClient("user-data"); BlobClient sourceBlob = sourceContainer.getBlobClient(filename); sourceBlob.delete(); context.getLogger().info("Moved blob to error container: " + filename); } catch (Exception e) { context.getLogger().severe("Error moving blob to error container: " + e.getMessage()); } } private UserReport generateUserReportData() { UserReport report = new UserReport(); report.setGeneratedAt(System.currentTimeMillis()); report.setTotalUsers(UserFunctions.USER_STORE.size()); report.setActiveUsers(UserFunctions.USER_STORE.size()); // Simplified UserReport.ReportSummary summary = new UserReport.ReportSummary(); summary.setUserCount(UserFunctions.USER_STORE.size()); summary.setGenerationTime(System.currentTimeMillis()); report.setSummary(summary); return report; } // Data Models for Blob Processing public static class UserBatchData { private String batchId; private long processedAt; private List<UserData> users; // Getters and setters public String getBatchId() { return batchId; } public void setBatchId(String batchId) { this.batchId = batchId; } public long getProcessedAt() { return processedAt; } public void setProcessedAt(long processedAt) { this.processedAt = processedAt; } public List<UserData> getUsers() { return users; } public void setUsers(List<UserData> users) { this.users = users; } public static class UserData { private String email; private String firstName; private String lastName; private String department; // Getters and setters public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } } } public static class UserReport { private long generatedAt; private int totalUsers; private int activeUsers; private ReportSummary summary; // Getters and setters public long getGeneratedAt() { return generatedAt; } public void setGeneratedAt(long generatedAt) { this.generatedAt = generatedAt; } public int getTotalUsers() { return totalUsers; } public void setTotalUsers(int totalUsers) { this.totalUsers = totalUsers; } public int getActiveUsers() { return activeUsers; } public void setActiveUsers(int activeUsers) { this.activeUsers = activeUsers; } public ReportSummary getSummary() { return summary; } public void setSummary(ReportSummary summary) { this.summary = summary; } public static class ReportSummary { private int userCount; private long generationTime; // Getters and setters public int getUserCount() { return userCount; } public void setUserCount(int userCount) { this.userCount = userCount; } public long getGenerationTime() { return generationTime; } public void setGenerationTime(long generationTime) { this.generationTime = generationTime; } } } } 3. Queue Trigger Functions
package com.company.azure.functions; import com.microsoft.azure.functions.*; import com.microsoft.azure.functions.annotation.*; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.*; import java.util.logging.Logger; /** * Queue Storage Trigger Functions */ public class QueueFunctions { private static final Logger LOGGER = Logger.getLogger(QueueFunctions.class.getName()); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** * Queue Trigger - Process user registration messages */ @FunctionName("processUserRegistrationQueue") public void processUserRegistrationQueue( @QueueTrigger( name = "message", queueName = "user-registrations", connection = "AzureWebJobsStorage" ) String message, @QueueOutput( name = "output", queueName = "user-registrations-processed", connection = "AzureWebJobsStorage" ) OutputBinding<String> output, @CosmosDBOutput( name = "databaseOutput", databaseName = "UserDatabase", collectionName = "UserCollection", connectionStringSetting = "CosmosDBConnection" ) OutputBinding<String> cosmosOutput, final ExecutionContext context) { context.getLogger().info("Java Queue trigger function processed a message: " + message); try { // Parse queue message UserRegistrationMessage registrationMessage = OBJECT_MAPPER.readValue(message, UserRegistrationMessage.class); context.getLogger().info("Processing user registration: " + registrationMessage.getEmail()); // Process registration processRegistration(registrationMessage, context); // Create processed message ProcessedRegistrationMessage processedMessage = new ProcessedRegistrationMessage( registrationMessage.getEmail(), registrationMessage.getUserId(), "SUCCESS", System.currentTimeMillis() ); String processedMessageJson = OBJECT_MAPPER.writeValueAsString(processedMessage); output.setValue(processedMessageJson); // Store in CosmosDB cosmosOutput.setValue(OBJECT_MAPPER.writeValueAsString(registrationMessage)); context.getLogger().info("Successfully processed user registration: " + registrationMessage.getEmail()); } catch (Exception e) { context.getLogger().severe("Error processing queue message: " + e.getMessage()); // Send to poison queue handleFailedMessage(message, e.getMessage(), context); } } /** * Queue Trigger - Process user notification messages */ @FunctionName("processUserNotificationQueue") @StorageAccount("AzureWebJobsStorage") public void processUserNotificationQueue( @QueueTrigger( name = "message", queueName = "user-notifications" ) String message, @SendGridOutput( name = "emailOutput", apiKey = "SendGridApiKey", to = "{email}", from = "[email protected]", subject = "Notification from User Service", text = "{message}" ) OutputBinding<SendGridMessage> emailOutput, final ExecutionContext context) { context.getLogger().info("Java Queue trigger function processed a notification: " + message); try { UserNotificationMessage notification = OBJECT_MAPPER.readValue(message, UserNotificationMessage.class); // Create email message SendGridMessage emailMessage = new SendGridMessage(); emailMessage.setTo(notification.getEmail()); emailMessage.setSubject(notification.getSubject()); emailMessage.setContent(notification.getMessage()); emailOutput.setValue(emailMessage); context.getLogger().info("Sent notification email to: " + notification.getEmail()); } catch (Exception e) { context.getLogger().severe("Error processing notification: " + e.getMessage()); } } /** * HTTP Trigger - Add message to queue */ @FunctionName("addUserToQueue") public HttpResponseMessage addUserToQueue( @HttpTrigger( name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION, route = "queue/users" ) HttpRequestMessage<Optional<String>> request, @QueueOutput( name = "output", queueName = "user-registrations", connection = "AzureWebJobsStorage" ) OutputBinding<String> output, final ExecutionContext context) { context.getLogger().info("Java HTTP trigger function processed a request to add user to queue."); try { String requestBody = request.getBody().orElseThrow(() -> new IllegalArgumentException("Request body is required")); UserRegistrationMessage registrationMessage = OBJECT_MAPPER.readValue(requestBody, UserRegistrationMessage.class); // Generate user ID if not provided if (registrationMessage.getUserId() == null) { registrationMessage.setUserId("user-" + System.currentTimeMillis()); } registrationMessage.setQueuedAt(System.currentTimeMillis()); String queueMessage = OBJECT_MAPPER.writeValueAsString(registrationMessage); output.setValue(queueMessage); return request.createResponseBuilder(HttpStatus.ACCEPTED) .body("{\"message\": \"User added to queue for processing\", \"userId\": \"" + registrationMessage.getUserId() + "\"}") .header("Content-Type", "application/json") .build(); } catch (Exception e) { context.getLogger().severe("Error adding user to queue: " + e.getMessage()); return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) .body("{\"error\": \"Failed to add user to queue\"}") .header("Content-Type", "application/json") .build(); } } private void processRegistration(UserRegistrationMessage message, ExecutionContext context) { context.getLogger().info("Processing registration for: " + message.getEmail()); // Simulate processing time try { Thread.sleep(500); // Simulate random failures for testing if (Math.random() < 0.1) { // 10% failure rate for testing throw new RuntimeException("Simulated processing failure"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Processing interrupted", e); } } private void handleFailedMessage(String originalMessage, String error, ExecutionContext context) { try { FailedMessage failedMessage = new FailedMessage( originalMessage, error, System.currentTimeMillis() ); String failedMessageJson = OBJECT_MAPPER.writeValueAsString(failedMessage); // In a real scenario, you would send this to a poison queue context.getLogger().severe("Message failed processing: " + failedMessageJson); } catch (Exception e) { context.getLogger().severe("Error handling failed message: " + e.getMessage()); } } // Data Models for Queue Processing public static class UserRegistrationMessage { private String userId; private String email; private String firstName; private String lastName; private long queuedAt; // Getters and setters public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public long getQueuedAt() { return queuedAt; } public void setQueuedAt(long queuedAt) { this.queuedAt = queuedAt; } } public static class ProcessedRegistrationMessage { private final String email; private final String userId; private final String status; private final long processedAt; public ProcessedRegistrationMessage(String email, String userId, String status, long processedAt) { this.email = email; this.userId = userId; this.status = status; this.processedAt = processedAt; } // Getters public String getEmail() { return email; } public String getUserId() { return userId; } public String getStatus() { return status; } public long getProcessedAt() { return processedAt; } } public static class UserNotificationMessage { private String email; private String subject; private String message; // Getters and setters public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } public static class FailedMessage { private final String originalMessage; private final String error; private final long failedAt; public FailedMessage(String originalMessage, String error, long failedAt) { this.originalMessage = originalMessage; this.error = error; this.failedAt = failedAt; } // Getters public String getOriginalMessage() { return originalMessage; } public String getError() { return error; } public long getFailedAt() { return failedAt; } } public static class SendGridMessage { private String to; private String subject; private String content; // Getters and setters public String getTo() { return to; } public void setTo(String to) { this.to = to; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } } 4. Timer Trigger Functions
package com.company.azure.functions; import com.microsoft.azure.functions.*; import com.microsoft.azure.functions.annotation.*; import com.fasterxml.jackson.databind.ObjectMapper; import java.time.Instant; import java.util.*; import java.util.logging.Logger; /** * Timer Trigger Functions for Scheduled Tasks */ public class TimerFunctions { private static final Logger LOGGER = Logger.getLogger(TimerFunctions.class.getName()); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** * Timer Trigger - Daily user cleanup */ @FunctionName("dailyUserCleanup") public void dailyUserCleanup( @TimerTrigger( name = "timerInfo", schedule = "0 0 2 * * *" // 2 AM daily ) String timerInfo, @CosmosDBOutput( name = "databaseOutput", databaseName = "UserDatabase", collectionName = "UserAudit", connectionStringSetting = "CosmosDBConnection" ) OutputBinding<String> auditOutput, final ExecutionContext context) { context.getLogger().info("Java Timer trigger function executed at: " + Instant.now()); try { CleanupResult result = performUserCleanup(context); // Create audit record AuditRecord auditRecord = new AuditRecord( "dailyUserCleanup", result.getDeletedCount(), result.getProcessedCount(), System.currentTimeMillis() ); auditOutput.setValue(OBJECT_MAPPER.writeValueAsString(auditRecord)); context.getLogger().info(String.format( "Daily cleanup completed. Deleted: %d, Processed: %d", result.getDeletedCount(), result.getProcessedCount())); } catch (Exception e) { context.getLogger().severe("Error in daily user cleanup: " + e.getMessage()); } } /** * Timer Trigger - Hourly user statistics */ @FunctionName("hourlyUserStats") public void hourlyUserStats( @TimerTrigger( name = "timerInfo", schedule = "0 0 * * * *" // Every hour ) String timerInfo, @BlobOutput( name = "statsOutput", path = "stats/user-stats-{datetime}.json", connection = "AzureWebJobsStorage" ) OutputBinding<String> statsOutput, final ExecutionContext context) { context.getLogger().info("Java Timer trigger function executed for hourly stats at: " + Instant.now()); try { UserStats stats = collectUserStats(); String statsJson = OBJECT_MAPPER.writeValueAsString(stats); statsOutput.setValue(statsJson); context.getLogger().info("Hourly user stats generated: " + stats.getTotalUsers()); } catch (Exception e) { context.getLogger().severe("Error generating hourly stats: " + e.getMessage()); } } /** * Timer Trigger - Every 5 minutes health check */ @FunctionName("healthCheck") public void healthCheck( @TimerTrigger( name = "timerInfo", schedule = "0 */5 * * * *" // Every 5 minutes ) String timerInfo, final ExecutionContext context) { context.getLogger().info("Java Timer trigger function executed for health check at: " + Instant.now()); try { HealthCheckResult result = performHealthCheck(context); if (result.isHealthy()) { context.getLogger().info("Health check passed: " + result.getMessage()); } else { context.getLogger().warning("Health check failed: " + result.getMessage()); // Could send alert here } } catch (Exception e) { context.getLogger().severe("Error performing health check: " + e.getMessage()); } } private CleanupResult performUserCleanup(ExecutionContext context) { context.getLogger().info("Performing user cleanup..."); // Simulate cleanup process int deletedCount = 0; int processedCount = UserFunctions.USER_STORE.size(); // In a real scenario, you would: // 1. Find inactive users // 2. Archive their data // 3. Remove from active storage // 4. Update audit records try { Thread.sleep(2000); // Simulate cleanup time deletedCount = (int) (Math.random() * 10); // Simulate random deletions } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return new CleanupResult(deletedCount, processedCount); } private UserStats collectUserStats() { UserStats stats = new UserStats(); stats.setTimestamp(System.currentTimeMillis()); stats.setTotalUsers(UserFunctions.USER_STORE.size()); stats.setActiveUsers(UserFunctions.USER_STORE.size()); // Simplified // Collect additional statistics Map<String, Integer> statsByHour = new HashMap<>(); for (int i = 0; i < 24; i++) { statsByHour.put(String.valueOf(i), (int) (Math.random() * 100)); } stats.setRegistrationsByHour(statsByHour); return stats; } private HealthCheckResult performHealthCheck(ExecutionContext context) { context.getLogger().info("Performing health check..."); // Check various system health indicators boolean storageHealthy = checkStorageHealth(); boolean databaseHealthy = checkDatabaseHealth(); boolean memoryHealthy = checkMemoryHealth(); boolean overallHealthy = storageHealthy && databaseHealthy && memoryHealthy; String message = String.format( "Storage: %s, Database: %s, Memory: %s", storageHealthy ? "OK" : "FAIL", databaseHealthy ? "OK" : "FAIL", memoryHealthy ? "OK" : "FAIL" ); return new HealthCheckResult(overallHealthy, message); } private boolean checkStorageHealth() { // Implement storage health check return Math.random() > 0.1; // 90% success rate for simulation } private boolean checkDatabaseHealth() { // Implement database health check return Math.random() > 0.1; // 90% success rate for simulation } private boolean checkMemoryHealth() { // Implement memory health check Runtime runtime = Runtime.getRuntime(); long usedMemory = runtime.totalMemory() - runtime.freeMemory(); long maxMemory = runtime.maxMemory(); double memoryUsage = (double) usedMemory / maxMemory; return memoryUsage < 0.8; // Healthy if using less than 80% memory } // Data Models for Timer Functions public static class CleanupResult { private final int deletedCount; private final int processedCount; public CleanupResult(int deletedCount, int processedCount) { this.deletedCount = deletedCount; this.processedCount = processedCount; } // Getters public int getDeletedCount() { return deletedCount; } public int getProcessedCount() { return processedCount; } } public static class AuditRecord { private final String operation; private final int affectedRecords; private final int totalRecords; private final long timestamp; public AuditRecord(String operation, int affectedRecords, int totalRecords, long timestamp) { this.operation = operation; this.affectedRecords = affectedRecords; this.totalRecords = totalRecords; this.timestamp = timestamp; } // Getters public String getOperation() { return operation; } public int getAffectedRecords() { return affectedRecords; } public int getTotalRecords() { return totalRecords; } public long getTimestamp() { return timestamp; } } public static class UserStats { private long timestamp; private int totalUsers; private int activeUsers; private Map<String, Integer> registrationsByHour; // Getters and setters public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public int getTotalUsers() { return totalUsers; } public void setTotalUsers(int totalUsers) { this.totalUsers = totalUsers; } public int getActiveUsers() { return activeUsers; } public void setActiveUsers(int activeUsers) { this.activeUsers = activeUsers; } public Map<String, Integer> getRegistrationsByHour() { return registrationsByHour; } public void setRegistrationsByHour(Map<String, Integer> registrationsByHour) { this.registrationsByHour = registrationsByHour; } } public static class HealthCheckResult { private final boolean healthy; private final String message; public HealthCheckResult(boolean healthy, String message) { this.healthy = healthy; this.message = message; } // Getters public boolean isHealthy() { return healthy; } public String getMessage() { return message; } } } Configuration and Deployment
1. Application Settings
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=yourstorageaccount;AccountKey=yourkey;EndpointSuffix=core.windows.net", "FUNCTIONS_WORKER_RUNTIME": "java", "FUNCTIONS_EXTENSION_VERSION": "~4", "APPINSIGHTS_INSTRUMENTATIONKEY": "your-app-insights-key", "CosmosDBConnection": "AccountEndpoint=https://your-cosmosdb.documents.azure.com:443/;AccountKey=yourkey;", "SendGridApiKey": "your-sendgrid-api-key", "ServiceBusConnection": "Endpoint=sb://yourservicebus.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=yourkey" } } 2. Deployment Script
#!/bin/bash # deploy-azure-functions.sh set -e # Configuration FUNCTION_APP_NAME="java-functions-app" RESOURCE_GROUP="java-functions-rg" LOCATION="eastus" APP_SERVICE_PLAN="java-functions-asp" STORAGE_ACCOUNT="javafuncstorage$RANDOM" COSMOS_DB_ACCOUNT="java-func-cosmos-$RANDOM" echo "Deploying Azure Functions Java Application..." # Create Resource Group echo "Creating resource group..." az group create --name $RESOURCE_GROUP --location $LOCATION # Create Storage Account echo "Creating storage account..." az storage account create \ --name $STORAGE_ACCOUNT \ --resource-group $RESOURCE_GROUP \ --location $LOCATION \ --sku Standard_LRS # Create App Service Plan echo "Creating app service plan..." az appservice plan create \ --name $APP_SERVICE_PLAN \ --resource-group $RESOURCE_GROUP \ --sku B1 \ --is-linux # Create Function App echo "Creating function app..." az functionapp create \ --name $FUNCTION_APP_NAME \ --resource-group $RESOURCE_GROUP \ --plan $APP_SERVICE_PLAN \ --runtime java \ --runtime-version 11 \ --functions-version 4 \ --storage-account $STORAGE_ACCOUNT \ --os-type Linux # Build and package the application echo "Building application..." mvn clean package # Deploy the function app echo "Deploying application..." mvn azure-functions:deploy # Configure application settings echo "Configuring application settings..." az functionapp config appsettings set \ --name $FUNCTION_APP_NAME \ --resource-group $RESOURCE_GROUP \ --settings \ AzureWebJobsStorage=$(az storage account show-connection-string --name $STORAGE_ACCOUNT --resource-group $RESOURCE_GROUP --query connectionString --output tsv) \ FUNCTIONS_WORKER_RUNTIME=java \ FUNCTIONS_EXTENSION_VERSION=~4 echo "Deployment completed successfully!" echo "Function App URL: https://$FUNCTION_APP_NAME.azurewebsites.net"
3. Function App Structure
src/main/java/com/company/azure/functions/ ├── UserFunctions.java # HTTP Trigger Functions ├── BlobStorageFunctions.java # Blob Trigger Functions ├── QueueFunctions.java # Queue Trigger Functions └── TimerFunctions.java # Timer Trigger Functions src/main/resources/ ├── host.json # Host configuration └── local.settings.json # Local development settings target/azure-functions/ └── java-functions-app/ ├── host.json ├── local.settings.json ├── com.company.azure.functions-1.0.0.jar └── function-app-name/ ├── function-name/ │ ├── function.json │ └── sample.dat └── extensions.csproj
Testing
1. Unit Tests
package com.company.azure.functions.test; import com.microsoft.azure.functions.*; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import java.util.*; import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; /** * Unit tests for Azure Functions */ class UserFunctionsTest { private static final Logger LOG = Logger.getLogger(UserFunctionsTest.class.getName()); @Test void testCreateUser() { // Setup UserFunctions function = new UserFunctions(); String requestBody = """ { "email": "[email protected]", "firstName": "John", "lastName": "Doe" } """; @SuppressWarnings("unchecked") HttpRequestMessage<Optional<String>> request = mock(HttpRequestMessage.class); when(request.getBody()).thenReturn(Optional.of(requestBody)); HttpResponseMessage.Builder responseBuilder = mock(HttpResponseMessage.Builder.class); when(responseBuilder.body(any())).thenReturn(responseBuilder); when(responseBuilder.header(anyString(), anyString())).thenReturn(responseBuilder); when(responseBuilder.build()).thenReturn(mock(HttpResponseMessage.class)); when(request.createResponseBuilder(any(HttpStatus.class))).thenReturn(responseBuilder); ExecutionContext context = mock(ExecutionContext.class); when(context.getLogger()).thenReturn(LOG); // Invoke HttpResponseMessage response = function.createUser(request, context); // Verify assertNotNull(response); verify(request).createResponseBuilder(HttpStatus.CREATED); } @Test void testGetUser() { // Setup UserFunctions function = new UserFunctions(); // First create a user String createRequestBody = """ { "email": "[email protected]", "firstName": "John", "lastName": "Doe" } """; @SuppressWarnings("unchecked") HttpRequestMessage<Optional<String>> createRequest = mock(HttpRequestMessage.class); when(createRequest.getBody()).thenReturn(Optional.of(createRequestBody)); HttpResponseMessage.Builder createResponseBuilder = mock(HttpResponseMessage.Builder.class); when(createResponseBuilder.body(any())).thenReturn(createResponseBuilder); when(createResponseBuilder.header(anyString(), anyString())).thenReturn(createResponseBuilder); ArgumentCaptor<String> responseBodyCaptor = ArgumentCaptor.forClass(String.class); when(createResponseBuilder.body(responseBodyCaptor.capture())).thenReturn(createResponseBuilder); when(createResponseBuilder.build()).thenReturn(mock(HttpResponseMessage.class)); when(createRequest.createResponseBuilder(any(HttpStatus.class))).thenReturn(createResponseBuilder); ExecutionContext context = mock(ExecutionContext.class); when(context.getLogger()).thenReturn(LOG); // Create user function.createUser(createRequest, context); // Extract user ID from response String responseBody = responseBodyCaptor.getValue(); String userId = extractUserIdFromResponse(responseBody); // Now test getUser @SuppressWarnings("unchecked") HttpRequestMessage<Optional<String>> getRequest = mock(HttpRequestMessage.class); when(getRequest.getBody()).thenReturn(Optional.empty()); HttpResponseMessage.Builder getResponseBuilder = mock(HttpResponseMessage.Builder.class); when(getResponseBuilder.body(any())).thenReturn(getResponseBuilder); when(getResponseBuilder.header(anyString(), anyString())).thenReturn(getResponseBuilder); when(getResponseBuilder.build()).thenReturn(mock(HttpResponseMessage.class)); when(getRequest.createResponseBuilder(any(HttpStatus.class))).thenReturn(getResponseBuilder); // Invoke getUser HttpResponseMessage response = function.getUser(getRequest, userId, context); // Verify assertNotNull(response); verify(getRequest).createResponseBuilder(HttpStatus.OK); } @Test void testCreateUserWithInvalidEmail() { // Setup UserFunctions function = new UserFunctions(); String requestBody = """ { "email": "", "firstName": "John", "lastName": "Doe" } """; @SuppressWarnings("unchecked") HttpRequestMessage<Optional<String>> request = mock(HttpRequestMessage.class); when(request.getBody()).thenReturn(Optional.of(requestBody)); HttpResponseMessage.Builder responseBuilder = mock(HttpResponseMessage.Builder.class); when(responseBuilder.body(any())).thenReturn(responseBuilder); when(responseBuilder.header(anyString(), anyString())).thenReturn(responseBuilder); when(responseBuilder.build()).thenReturn(mock(HttpResponseMessage.class)); when(request.createResponseBuilder(any(HttpStatus.class))).thenReturn(responseBuilder); ExecutionContext context = mock(ExecutionContext.class); when(context.getLogger()).thenReturn(LOG); // Invoke HttpResponseMessage response = function.createUser(request, context); // Verify assertNotNull(response); verify(request).createResponseBuilder(HttpStatus.BAD_REQUEST); } private String extractUserIdFromResponse(String responseBody) { // Simple extraction for test purposes // In real scenario, parse JSON properly if (responseBody.contains("\"id\"")) { int start = responseBody.indexOf("\"id\":\"") + 6; int end = responseBody.indexOf("\"", start); return responseBody.substring(start, end); } return null; } } class QueueFunctionsTest { @Test void testProcessUserRegistrationQueue() { // Setup QueueFunctions function = new QueueFunctions(); String queueMessage = """ { "userId": "test-user-123", "email": "[email protected]", "firstName": "John", "lastName": "Doe", "queuedAt": 1234567890 } """; @SuppressWarnings("unchecked") OutputBinding<String> output = mock(OutputBinding.class); @SuppressWarnings("unchecked") OutputBinding<String> cosmosOutput = mock(OutputBinding.class); ExecutionContext context = mock(ExecutionContext.class); when(context.getLogger()).thenReturn(Logger.getGlobal()); // Invoke function.processUserRegistrationQueue(queueMessage, output, cosmosOutput, context); // Verify verify(output).setValue(anyString()); verify(cosmosOutput).setValue(anyString()); } } 2. Integration Tests
package com.company.azure.functions.test; import com.microsoft.azure.functions.ExecutionContext; import com.microsoft.azure.functions.HttpResponseMessage; import com.microsoft.azure.functions.HttpStatus; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Optional; import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.*; /** * Integration tests for Azure Functions */ class UserFunctionsIntegrationTest { private UserFunctions userFunctions; private ExecutionContext context; @BeforeEach void setUp() { userFunctions = new UserFunctions(); context = new ExecutionContext() { @Override public Logger getLogger() { return Logger.getGlobal(); } @Override public String getInvocationId() { return "test-invocation-id"; } @Override public String getFunctionName() { return "test-function"; } }; } @Test void testCreateAndGetUserIntegration() { // Create user String createRequestBody = """ { "email": "[email protected]", "firstName": "Integration", "lastName": "Test" } """; TestHttpRequestMessage createRequest = new TestHttpRequestMessage( Optional.of(createRequestBody), "POST", "/api/users" ); HttpResponseMessage createResponse = userFunctions.createUser(createRequest, context); assertEquals(HttpStatus.CREATED, createResponse.getStatus()); // Extract user ID from response (simplified) String responseBody = createResponse.getBody().toString(); assertTrue(responseBody.contains("[email protected]")); } } // Test implementation for HTTP request class TestHttpRequestMessage implements com.microsoft.azure.functions.HttpRequestMessage<Optional<String>> { private final Optional<String> body; private final String method; private final String uri; public TestHttpRequestMessage(Optional<String> body, String method, String uri) { this.body = body; this.method = method; this.uri = uri; } @Override public Optional<String> getBody() { return body; } @Override public String getUri() { return uri; } @Override public String getMethod() { return method; } // Implement other methods with default values or empty implementations @Override public Map<String, String> getHeaders() { return new HashMap<>(); } @Override public Map<String, String> getQueryParameters() { return new HashMap<>(); } @Override public HttpResponseMessage.Builder createResponseBuilder(HttpStatus status) { return new TestHttpResponseMessage.Builder(status); } // Other methods can throw UnsupportedOperationException or return default values @Override public Object getAttribute(String name) { throw new UnsupportedOperationException(); } @Override public Set<String> getAttributeNames() { throw new UnsupportedOperationException(); } } class TestHttpResponseMessage implements HttpResponseMessage { private final HttpStatus status; private final Object body; private final Map<String, String> headers; public TestHttpResponseMessage(HttpStatus status, Object body, Map<String, String> headers) { this.status = status; this.body = body; this.headers = headers; } @Override public HttpStatus getStatus() { return status; } @Override public String getHeader(String key) { return headers.get(key); } @Override public Object getBody() { return body; } static class Builder implements HttpResponseMessage.Builder { private HttpStatus status; private Object body; private final Map<String, String> headers = new HashMap<>(); public Builder(HttpStatus status) { this.status = status; } @Override public Builder status(HttpStatus status) { this.status = status; return this; } @Override public Builder header(String key, String value) { headers.put(key, value); return this; } @Override public Builder body(Object body) { this.body = body; return this; } @Override public HttpResponseMessage build() { return new TestHttpResponseMessage(status, body, headers); } } } Summary
This Azure Functions Java implementation provides:
- Multiple Trigger Types: HTTP, Blob Storage, Queue Storage, Timer
- Input/Output Bindings: Seamless integration with Azure services
- Scalable Architecture: Automatic scaling based on workload
- Comprehensive Testing: Unit and integration tests
- Production Ready: Error handling, logging, monitoring
Key Benefits:
- Serverless: No infrastructure management
- Cost Effective: Pay only for execution time
- Integrated: Native integration with Azure ecosystem
- Java Ecosystem: Leverage existing Java libraries and tools
- Enterprise Ready: Security, monitoring, and compliance features
This implementation demonstrates how to build robust, scalable serverless applications using Azure Functions with Java, covering common patterns and best practices for production deployments.