diff --git a/apps/faces-example2/pom.xml b/apps/faces-example2/pom.xml index 54b35398e..b5521b67f 100644 --- a/apps/faces-example2/pom.xml +++ b/apps/faces-example2/pom.xml @@ -174,6 +174,20 @@ 3.2.2 + + + org.springframework.boot + spring-boot-maven-plugin + 3.2.0 + + + + repackage + + + + + org.jacoco diff --git a/apps/faces-example2/src/test/java/org/apache/struts/webapp/example2/TestApplication.java b/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/Example2Application.java similarity index 57% rename from apps/faces-example2/src/test/java/org/apache/struts/webapp/example2/TestApplication.java rename to apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/Example2Application.java index f2a9f5073..b73b347fd 100644 --- a/apps/faces-example2/src/test/java/org/apache/struts/webapp/example2/TestApplication.java +++ b/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/Example2Application.java @@ -16,11 +16,25 @@ * specific language governing permissions and limitations * under the License. */ - package org.apache.struts.webapp.example2; +import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * Spring Boot main application class for the Struts Faces Example 2 migration. + * This class serves as the entry point for the Spring Boot application, + * replacing the traditional web.xml and servlet configuration. + * + * The @SpringBootApplication annotation enables: + * - @Configuration: Tags the class as a source of bean definitions + * - @EnableAutoConfiguration: Enables Spring Boot's auto-configuration + * - @ComponentScan: Enables component scanning in the current package and sub-packages + */ @SpringBootApplication -public class TestApplication { +public class Example2Application { + + public static void main(String[] args) { + SpringApplication.run(Example2Application.class, args); + } } diff --git a/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/config/DatabaseConfiguration.java b/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/config/DatabaseConfiguration.java index c04fc30e7..f7a7047e1 100644 --- a/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/config/DatabaseConfiguration.java +++ b/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/config/DatabaseConfiguration.java @@ -18,14 +18,136 @@ */ package org.apache.struts.webapp.example2.config; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.util.List; +import jakarta.annotation.PreDestroy; + +import org.apache.struts.webapp.example2.UserDatabase; +import org.apache.struts.webapp.example2.memory.MemoryUserDatabase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +/** + * Spring configuration class that replaces the MemoryDatabasePlugIn from Struts. + * This class initializes the in-memory user database from an XML file and provides + * it as a Spring bean for dependency injection. + */ @Configuration public class DatabaseConfiguration { + private static final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); + + @Value("${app.database.path:classpath:database.xml}") + private Resource databaseResource; + + private MemoryUserDatabase database; + + /** + * Creates and initializes the UserDatabase bean. + * The database is loaded from the configured XML file path. + * + * @return The initialized UserDatabase instance + * @throws Exception if the database cannot be loaded + */ + @Bean + public UserDatabase userDatabase() throws Exception { + log.info("Initializing memory database from '{}'", databaseResource); + + database = new MemoryUserDatabase(); + + try { + if (databaseResource.exists() && databaseResource.isFile()) { + File file = databaseResource.getFile(); + database.setPathname(file.getAbsolutePath()); + database.open(); + } else if (databaseResource.exists()) { + File tempFile = createTempDatabaseFile(); + database.setPathname(tempFile.getAbsolutePath()); + try (InputStream is = databaseResource.getInputStream()) { + copyInputStreamToFile(is, tempFile); + } + database.open(); + } else { + log.warn("Database resource '{}' does not exist, creating empty database", databaseResource); + File tempFile = createTempDatabaseFile(); + database.setPathname(tempFile.getAbsolutePath()); + createEmptyDatabaseFile(tempFile); + database.open(); + } + } catch (Exception e) { + log.error("Failed to initialize database from '{}': {}", databaseResource, e.getMessage()); + throw e; + } + + log.info("Memory database initialized successfully"); + return database; + } + + /** + * Creates a temporary file for the database when running from a JAR. + * This allows the database to be saved during runtime. + * + * @return The temporary file + * @throws IOException if the file cannot be created + */ + private File createTempDatabaseFile() throws IOException { + File tempDir = Files.createTempDirectory("struts-example2").toFile(); + tempDir.deleteOnExit(); + File tempFile = new File(tempDir, "database.xml"); + tempFile.deleteOnExit(); + return tempFile; + } + + /** + * Copies an input stream to a file. + * + * @param is The input stream + * @param file The target file + * @throws IOException if copying fails + */ + private void copyInputStreamToFile(InputStream is, File file) throws IOException { + Files.copy(is, file.toPath()); + } + + /** + * Creates a database XML file with sample data. + * This provides initial test data for the application. + * + * @param file The file to create + * @throws IOException if file creation fails + */ + private void createEmptyDatabaseFile(File file) throws IOException { + String sampleDb = """ + + + + + + + + """; + Files.writeString(file.toPath(), sampleDb); + } + + /** + * Provides a list of server types for subscription forms. + * This replaces the setupCache method from MemoryDatabasePlugIn. + * + * @return List of server type options + */ @Bean public List serverTypes() { return List.of( @@ -34,6 +156,26 @@ public List serverTypes() { ); } + /** + * Cleanup method called when the application shuts down. + * Saves and closes the database. + */ + @PreDestroy + public void cleanup() { + log.info("Finalizing memory database"); + if (database != null) { + try { + database.close(); + } catch (Exception e) { + log.error("Error closing memory database: {}", e.getMessage()); + } + } + } + + /** + * Simple bean to hold label-value pairs for dropdown options. + * This replaces the Struts LabelValueBean. + */ public static class LabelValueBean { private final String label; private final String value; diff --git a/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/controller/WelcomeController.java b/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/controller/WelcomeController.java new file mode 100644 index 000000000..9bbfc3eaf --- /dev/null +++ b/apps/faces-example2/src/main/java/org/apache/struts/webapp/example2/controller/WelcomeController.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts.webapp.example2.controller; + +import jakarta.servlet.http.HttpSession; + +import org.apache.struts.webapp.example2.Constants; +import org.apache.struts.webapp.example2.User; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class WelcomeController { + + @GetMapping({"/", "/welcome"}) + public String welcome(HttpSession session, Model model) { + User user = (User) session.getAttribute(Constants.USER_KEY); + if (user != null) { + model.addAttribute("user", user); + } + return "welcome"; + } + + @GetMapping("/mainMenu") + public String mainMenu(HttpSession session, Model model) { + User user = (User) session.getAttribute(Constants.USER_KEY); + if (user == null) { + return "redirect:/editLogon"; + } + model.addAttribute("user", user); + return "mainMenu"; + } +} diff --git a/apps/faces-example2/src/main/resources/application.properties b/apps/faces-example2/src/main/resources/application.properties index ce3bf3461..98f11aaf4 100644 --- a/apps/faces-example2/src/main/resources/application.properties +++ b/apps/faces-example2/src/main/resources/application.properties @@ -11,3 +11,11 @@ spring.thymeleaf.suffix=.html # Message source configuration spring.messages.basename=messages spring.messages.encoding=UTF-8 + +# Database configuration +# Uses the existing database.xml from webapp/WEB-INF for sample data +app.database.path=classpath:database.xml + +# Logging +logging.level.org.apache.struts.webapp.example2=DEBUG +logging.level.org.springframework.web=INFO