From 76b33100278bc4cf338e50f81257c3cd6a52a556 Mon Sep 17 00:00:00 2001 From: johnniang Date: Thu, 18 Apr 2019 23:32:44 +0800 Subject: [PATCH 1/2] Add a common theme addition service --- .../run/halo/app/service/ThemeService.java | 11 ++ .../app/service/impl/ThemeServiceImpl.java | 104 ++++++++++++------ .../java/run/halo/app/utils/FileUtils.java | 16 +++ 3 files changed, 95 insertions(+), 36 deletions(-) diff --git a/src/main/java/run/halo/app/service/ThemeService.java b/src/main/java/run/halo/app/service/ThemeService.java index 302149080..303059c2a 100644 --- a/src/main/java/run/halo/app/service/ThemeService.java +++ b/src/main/java/run/halo/app/service/ThemeService.java @@ -7,6 +7,7 @@ import run.halo.app.handler.theme.support.ThemeProperty; import run.halo.app.model.support.ThemeFile; import java.io.File; +import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Optional; @@ -204,9 +205,19 @@ public interface ThemeService { /** * Upload theme. + * * @param file multipart file must not be null * @return theme info */ @NonNull ThemeProperty upload(@NonNull MultipartFile file); + + /** + * Adds a new theme. + * + * @param themeTmpPath theme temporary path must not be null + * @return theme property + */ + @NonNull + ThemeProperty add(@NonNull Path themeTmpPath) throws IOException; } diff --git a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java index 02b7955a3..88c7e8932 100644 --- a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java @@ -10,7 +10,6 @@ import freemarker.template.Configuration; import freemarker.template.TemplateModelException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.http.MediaType; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; @@ -20,7 +19,6 @@ import org.springframework.web.multipart.MultipartFile; import run.halo.app.cache.StringCacheStore; import run.halo.app.config.properties.HaloProperties; import run.halo.app.exception.*; -import run.halo.app.handler.file.FileHandler; import run.halo.app.handler.theme.ThemeConfigResolver; import run.halo.app.handler.theme.ThemePropertyResolver; import run.halo.app.handler.theme.support.Group; @@ -28,14 +26,12 @@ import run.halo.app.handler.theme.support.ThemeProperty; import run.halo.app.model.properties.PrimaryProperties; import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.ThemeFile; -import run.halo.app.model.support.UploadResult; import run.halo.app.service.OptionService; import run.halo.app.service.ThemeService; +import run.halo.app.utils.FileUtils; import run.halo.app.utils.FilenameUtils; import run.halo.app.utils.JsonUtils; -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -287,15 +283,6 @@ public class ThemeServiceImpl implements ThemeService { return activatedThemeId; } - /** - * Set activated theme id. - * - * @param themeId theme id - */ - private void setActivatedThemeId(@Nullable String themeId) { - this.activatedThemeId = themeId; - } - @Override public ThemeProperty activeTheme(String themeId) { // Check existence of the theme @@ -322,12 +309,6 @@ public class ThemeServiceImpl implements ThemeService { return themeProperty; } - /** - * Upload theme. - * - * @param file multipart file must not be null - * @return theme info - */ @Override public ThemeProperty upload(MultipartFile file) { Assert.notNull(file, "Multipart file must not be null"); @@ -346,7 +327,7 @@ public class ThemeServiceImpl implements ThemeService { file.transferTo(uploadPath); // Unzip theme package - ZipUtil.unzip(uploadPath.toFile(),uploadPath.getParent().toFile()); + ZipUtil.unzip(uploadPath.toFile(), uploadPath.getParent().toFile()); // Delete theme package FileUtil.del(uploadPath.toFile()); @@ -360,6 +341,36 @@ public class ThemeServiceImpl implements ThemeService { } } + @Override + public ThemeProperty add(Path themeTmpPath) throws IOException { + Assert.notNull(themeTmpPath, "Theme temporary path must not be null"); + Assert.isTrue(Files.isDirectory(themeTmpPath), "Theme temporary path must be a directory"); + + // Check property config + Path configPath = getThemePropertyPathOfNullable(themeTmpPath).orElseThrow(() -> new ThemePropertyMissingException("Theme property file is dismiss").setErrorData(themeTmpPath)); + + ThemeProperty tmpThemeProperty = getProperty(configPath); + + // Copy the temporary path to current theme folder + Path targetThemePath = workDir.resolve(tmpThemeProperty.getId()); + FileUtils.copyFolder(themeTmpPath, targetThemePath); + + // Delete temp theme folder + FileUtils.deleteFolder(themeTmpPath); + + // Get property again + return getProperty(targetThemePath); + } + + /** + * Set activated theme id. + * + * @param themeId theme id + */ + private void setActivatedThemeId(@Nullable String themeId) { + this.activatedThemeId = themeId; + } + /** * Lists theme files as tree view. * @@ -446,8 +457,14 @@ public class ThemeServiceImpl implements ThemeService { } } - @Nullable - private Path getPropertyPath(@NonNull Path themePath) { + /** + * Gets property path of nullable. + * + * @param themePath theme path. + * @return an optional property path + */ + @NonNull + private Optional getThemePropertyPathOfNullable(@NonNull Path themePath) { Assert.notNull(themePath, "Theme path must not be null"); for (String propertyPathName : THEME_PROPERTY_FILE_NAMES) { @@ -456,28 +473,28 @@ public class ThemeServiceImpl implements ThemeService { log.debug("Attempting to find property file: [{}]", propertyPath); if (Files.exists(propertyPath) && Files.isReadable(propertyPath)) { log.debug("Found property file: [{}]", propertyPath); - return propertyPath; + return Optional.of(propertyPath); } } - return null; + return Optional.empty(); } /** - * Gets theme property. + * Gets property path of non null. * - * @param themePath must not be null - * @return theme property + * @param themePath theme path. + * @return property path won't be null */ @NonNull - private ThemeProperty getProperty(@NonNull Path themePath) { + private Path getThemePropertyPath(@NonNull Path themePath) { + return getThemePropertyPathOfNullable(themePath).orElseThrow(() -> new ThemePropertyMissingException(themePath + " dose not exist any theme property file").setErrorData(themePath)); + } + + private Optional getPropertyOfNullable(Path themePath) { Assert.notNull(themePath, "Theme path must not be null"); - Path propertyPath = getPropertyPath(themePath); - - if (propertyPath == null) { - throw new ThemePropertyMissingException(themePath + " dose not exist any theme property file").setErrorData(themePath); - } + Path propertyPath = getThemePropertyPath(themePath); try { // Get property content @@ -504,10 +521,24 @@ public class ThemeServiceImpl implements ThemeService { themeProperty.setActivated(true); } - return themeProperty; + return Optional.of(themeProperty); + } catch (IOException e) { - throw new ThemePropertyMissingException("Cannot resolve theme property", e).setErrorData(propertyPath.toString()); + log.error("Failed to load theme property file", e); } + + return Optional.empty(); + } + + /** + * Gets theme property. + * + * @param themePath must not be null + * @return theme property + */ + @NonNull + private ThemeProperty getProperty(@NonNull Path themePath) { + return getPropertyOfNullable(themePath).orElseThrow(() -> new ThemePropertyMissingException("Cannot resolve theme property").setErrorData(themePath)); } /** @@ -517,6 +548,7 @@ public class ThemeServiceImpl implements ThemeService { * @return screenshots file name or null if the given theme path has not screenshots * @throws IOException throws when listing files */ + @NonNull private Optional getScreenshotsFileName(@NonNull Path themePath) throws IOException { Assert.notNull(themePath, "Theme path must not be null"); diff --git a/src/main/java/run/halo/app/utils/FileUtils.java b/src/main/java/run/halo/app/utils/FileUtils.java index 5f2d93800..302d84413 100644 --- a/src/main/java/run/halo/app/utils/FileUtils.java +++ b/src/main/java/run/halo/app/utils/FileUtils.java @@ -3,9 +3,11 @@ package run.halo.app.utils; import org.springframework.lang.NonNull; import org.springframework.util.Assert; +import java.io.File; import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Comparator; /** * File utilities. @@ -46,4 +48,18 @@ public class FileUtils { } }); } + + /** + * Deletes folder recursively. + * + * @param deletingPath deleting path must not be null + */ + public static void deleteFolder(Path deletingPath) throws IOException { + Assert.notNull(deletingPath, "Deleting path must not be null"); + + Files.walk(deletingPath) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } } From 5ebf5653f33c93301d807677eede0543f13d7a80 Mon Sep 17 00:00:00 2001 From: johnniang Date: Thu, 18 Apr 2019 23:42:51 +0800 Subject: [PATCH 2/2] Move theme config handler to new package --- .../theme/{ => config}/ThemeConfigResolver.java | 4 ++-- .../theme/{ => config}/ThemeConfigResolvers.java | 6 +++--- .../theme/{ => config}/ThemePropertyResolver.java | 4 ++-- .../{ => config}/impl/YamlThemeConfigResolverImpl.java | 10 +++++----- .../{ => config}/impl/YamlThemePropertyResolver.java | 6 +++--- .../config}/impl/YamlThemePropertyResolverTest.java | 4 ++-- .../app/handler/theme/{ => config}/support/Group.java | 2 +- .../app/handler/theme/{ => config}/support/Item.java | 2 +- .../app/handler/theme/{ => config}/support/Option.java | 2 +- .../theme/{ => config}/support/ThemeProperty.java | 2 +- src/main/java/run/halo/app/service/ThemeService.java | 4 ++-- .../run/halo/app/service/impl/ThemeServiceImpl.java | 8 ++++---- .../app/web/controller/admin/api/ThemeController.java | 4 ++-- src/test/java/run/halo/app/utils/YamlTest.java | 4 ++-- 14 files changed, 31 insertions(+), 31 deletions(-) rename src/main/java/run/halo/app/handler/theme/{ => config}/ThemeConfigResolver.java (83%) rename src/main/java/run/halo/app/handler/theme/{ => config}/ThemeConfigResolvers.java (91%) rename src/main/java/run/halo/app/handler/theme/{ => config}/ThemePropertyResolver.java (79%) rename src/main/java/run/halo/app/handler/theme/{ => config}/impl/YamlThemeConfigResolverImpl.java (95%) rename src/main/java/run/halo/app/handler/theme/{ => config}/impl/YamlThemePropertyResolver.java (80%) rename src/{test/java/run/halo/app/handler/theme => main/java/run/halo/app/handler/theme/config}/impl/YamlThemePropertyResolverTest.java (89%) rename src/main/java/run/halo/app/handler/theme/{ => config}/support/Group.java (87%) rename src/main/java/run/halo/app/handler/theme/{ => config}/support/Item.java (93%) rename src/main/java/run/halo/app/handler/theme/{ => config}/support/Option.java (84%) rename src/main/java/run/halo/app/handler/theme/{ => config}/support/ThemeProperty.java (95%) diff --git a/src/main/java/run/halo/app/handler/theme/ThemeConfigResolver.java b/src/main/java/run/halo/app/handler/theme/config/ThemeConfigResolver.java similarity index 83% rename from src/main/java/run/halo/app/handler/theme/ThemeConfigResolver.java rename to src/main/java/run/halo/app/handler/theme/config/ThemeConfigResolver.java index 179f4f612..5800e8dcc 100644 --- a/src/main/java/run/halo/app/handler/theme/ThemeConfigResolver.java +++ b/src/main/java/run/halo/app/handler/theme/config/ThemeConfigResolver.java @@ -1,7 +1,7 @@ -package run.halo.app.handler.theme; +package run.halo.app.handler.theme.config; import org.springframework.lang.NonNull; -import run.halo.app.handler.theme.support.Group; +import run.halo.app.handler.theme.config.support.Group; import java.io.IOException; import java.util.List; diff --git a/src/main/java/run/halo/app/handler/theme/ThemeConfigResolvers.java b/src/main/java/run/halo/app/handler/theme/config/ThemeConfigResolvers.java similarity index 91% rename from src/main/java/run/halo/app/handler/theme/ThemeConfigResolvers.java rename to src/main/java/run/halo/app/handler/theme/config/ThemeConfigResolvers.java index 9d8b2baa8..4d4ab094d 100644 --- a/src/main/java/run/halo/app/handler/theme/ThemeConfigResolvers.java +++ b/src/main/java/run/halo/app/handler/theme/config/ThemeConfigResolvers.java @@ -1,10 +1,10 @@ -package run.halo.app.handler.theme; +package run.halo.app.handler.theme.config; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; -import run.halo.app.handler.theme.impl.YamlThemeConfigResolverImpl; -import run.halo.app.handler.theme.support.Group; +import run.halo.app.handler.theme.config.impl.YamlThemeConfigResolverImpl; +import run.halo.app.handler.theme.config.support.Group; import java.io.IOException; import java.util.List; diff --git a/src/main/java/run/halo/app/handler/theme/ThemePropertyResolver.java b/src/main/java/run/halo/app/handler/theme/config/ThemePropertyResolver.java similarity index 79% rename from src/main/java/run/halo/app/handler/theme/ThemePropertyResolver.java rename to src/main/java/run/halo/app/handler/theme/config/ThemePropertyResolver.java index 160b6f53b..bfdf261fc 100644 --- a/src/main/java/run/halo/app/handler/theme/ThemePropertyResolver.java +++ b/src/main/java/run/halo/app/handler/theme/config/ThemePropertyResolver.java @@ -1,7 +1,7 @@ -package run.halo.app.handler.theme; +package run.halo.app.handler.theme.config; import org.springframework.lang.NonNull; -import run.halo.app.handler.theme.support.ThemeProperty; +import run.halo.app.handler.theme.config.support.ThemeProperty; import java.io.IOException; diff --git a/src/main/java/run/halo/app/handler/theme/impl/YamlThemeConfigResolverImpl.java b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java similarity index 95% rename from src/main/java/run/halo/app/handler/theme/impl/YamlThemeConfigResolverImpl.java rename to src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java index 8c489b933..56547dc91 100644 --- a/src/main/java/run/halo/app/handler/theme/impl/YamlThemeConfigResolverImpl.java +++ b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java @@ -1,13 +1,13 @@ -package run.halo.app.handler.theme.impl; +package run.halo.app.handler.theme.config.impl; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; -import run.halo.app.handler.theme.ThemeConfigResolver; -import run.halo.app.handler.theme.support.Group; -import run.halo.app.handler.theme.support.Item; -import run.halo.app.handler.theme.support.Option; +import run.halo.app.handler.theme.config.ThemeConfigResolver; +import run.halo.app.handler.theme.config.support.Group; +import run.halo.app.handler.theme.config.support.Item; +import run.halo.app.handler.theme.config.support.Option; import run.halo.app.model.enums.DataType; import run.halo.app.model.enums.InputType; diff --git a/src/main/java/run/halo/app/handler/theme/impl/YamlThemePropertyResolver.java b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemePropertyResolver.java similarity index 80% rename from src/main/java/run/halo/app/handler/theme/impl/YamlThemePropertyResolver.java rename to src/main/java/run/halo/app/handler/theme/config/impl/YamlThemePropertyResolver.java index 189f9569a..8b3ef5f0b 100644 --- a/src/main/java/run/halo/app/handler/theme/impl/YamlThemePropertyResolver.java +++ b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemePropertyResolver.java @@ -1,11 +1,11 @@ -package run.halo.app.handler.theme.impl; +package run.halo.app.handler.theme.config.impl; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.springframework.stereotype.Service; import org.springframework.util.Assert; -import run.halo.app.handler.theme.ThemePropertyResolver; -import run.halo.app.handler.theme.support.ThemeProperty; +import run.halo.app.handler.theme.config.ThemePropertyResolver; +import run.halo.app.handler.theme.config.support.ThemeProperty; import java.io.IOException; diff --git a/src/test/java/run/halo/app/handler/theme/impl/YamlThemePropertyResolverTest.java b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemePropertyResolverTest.java similarity index 89% rename from src/test/java/run/halo/app/handler/theme/impl/YamlThemePropertyResolverTest.java rename to src/main/java/run/halo/app/handler/theme/config/impl/YamlThemePropertyResolverTest.java index 658f0340c..521b47511 100644 --- a/src/test/java/run/halo/app/handler/theme/impl/YamlThemePropertyResolverTest.java +++ b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemePropertyResolverTest.java @@ -1,9 +1,9 @@ -package run.halo.app.handler.theme.impl; +package run.halo.app.handler.theme.config.impl; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.junit.Test; -import run.halo.app.handler.theme.support.ThemeProperty; +import run.halo.app.handler.theme.config.support.ThemeProperty; import java.io.IOException; diff --git a/src/main/java/run/halo/app/handler/theme/support/Group.java b/src/main/java/run/halo/app/handler/theme/config/support/Group.java similarity index 87% rename from src/main/java/run/halo/app/handler/theme/support/Group.java rename to src/main/java/run/halo/app/handler/theme/config/support/Group.java index 90d66851d..e8f78037c 100644 --- a/src/main/java/run/halo/app/handler/theme/support/Group.java +++ b/src/main/java/run/halo/app/handler/theme/config/support/Group.java @@ -1,4 +1,4 @@ -package run.halo.app.handler.theme.support; +package run.halo.app.handler.theme.config.support; import lombok.Data; diff --git a/src/main/java/run/halo/app/handler/theme/support/Item.java b/src/main/java/run/halo/app/handler/theme/config/support/Item.java similarity index 93% rename from src/main/java/run/halo/app/handler/theme/support/Item.java rename to src/main/java/run/halo/app/handler/theme/config/support/Item.java index f63f7dc66..61bdd593e 100644 --- a/src/main/java/run/halo/app/handler/theme/support/Item.java +++ b/src/main/java/run/halo/app/handler/theme/config/support/Item.java @@ -1,4 +1,4 @@ -package run.halo.app.handler.theme.support; +package run.halo.app.handler.theme.config.support; import lombok.Data; import run.halo.app.model.enums.DataType; diff --git a/src/main/java/run/halo/app/handler/theme/support/Option.java b/src/main/java/run/halo/app/handler/theme/config/support/Option.java similarity index 84% rename from src/main/java/run/halo/app/handler/theme/support/Option.java rename to src/main/java/run/halo/app/handler/theme/config/support/Option.java index c8b901cc7..9db1ad1c5 100644 --- a/src/main/java/run/halo/app/handler/theme/support/Option.java +++ b/src/main/java/run/halo/app/handler/theme/config/support/Option.java @@ -1,4 +1,4 @@ -package run.halo.app.handler.theme.support; +package run.halo.app.handler.theme.config.support; import lombok.Data; diff --git a/src/main/java/run/halo/app/handler/theme/support/ThemeProperty.java b/src/main/java/run/halo/app/handler/theme/config/support/ThemeProperty.java similarity index 95% rename from src/main/java/run/halo/app/handler/theme/support/ThemeProperty.java rename to src/main/java/run/halo/app/handler/theme/config/support/ThemeProperty.java index 2d456bb88..69b2d0557 100644 --- a/src/main/java/run/halo/app/handler/theme/support/ThemeProperty.java +++ b/src/main/java/run/halo/app/handler/theme/config/support/ThemeProperty.java @@ -1,4 +1,4 @@ -package run.halo.app.handler.theme.support; +package run.halo.app.handler.theme.config.support; import lombok.Data; diff --git a/src/main/java/run/halo/app/service/ThemeService.java b/src/main/java/run/halo/app/service/ThemeService.java index 303059c2a..aae1486c8 100644 --- a/src/main/java/run/halo/app/service/ThemeService.java +++ b/src/main/java/run/halo/app/service/ThemeService.java @@ -2,8 +2,8 @@ package run.halo.app.service; import org.springframework.lang.NonNull; import org.springframework.web.multipart.MultipartFile; -import run.halo.app.handler.theme.support.Group; -import run.halo.app.handler.theme.support.ThemeProperty; +import run.halo.app.handler.theme.config.support.Group; +import run.halo.app.handler.theme.config.support.ThemeProperty; import run.halo.app.model.support.ThemeFile; import java.io.File; diff --git a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java index 88c7e8932..4246dfdc1 100644 --- a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java @@ -19,10 +19,10 @@ import org.springframework.web.multipart.MultipartFile; import run.halo.app.cache.StringCacheStore; import run.halo.app.config.properties.HaloProperties; import run.halo.app.exception.*; -import run.halo.app.handler.theme.ThemeConfigResolver; -import run.halo.app.handler.theme.ThemePropertyResolver; -import run.halo.app.handler.theme.support.Group; -import run.halo.app.handler.theme.support.ThemeProperty; +import run.halo.app.handler.theme.config.ThemeConfigResolver; +import run.halo.app.handler.theme.config.ThemePropertyResolver; +import run.halo.app.handler.theme.config.support.Group; +import run.halo.app.handler.theme.config.support.ThemeProperty; import run.halo.app.model.properties.PrimaryProperties; import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.ThemeFile; diff --git a/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java b/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java index 9c060e055..b326c1773 100644 --- a/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java +++ b/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java @@ -3,10 +3,10 @@ package run.halo.app.web.controller.admin.api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import run.halo.app.handler.theme.support.Group; +import run.halo.app.handler.theme.config.support.Group; import run.halo.app.model.support.BaseResponse; import run.halo.app.model.support.ThemeFile; -import run.halo.app.handler.theme.support.ThemeProperty; +import run.halo.app.handler.theme.config.support.ThemeProperty; import run.halo.app.service.ThemeService; import run.halo.app.service.ThemeSettingService; diff --git a/src/test/java/run/halo/app/utils/YamlTest.java b/src/test/java/run/halo/app/utils/YamlTest.java index dc86ba4ae..2993c4c10 100644 --- a/src/test/java/run/halo/app/utils/YamlTest.java +++ b/src/test/java/run/halo/app/utils/YamlTest.java @@ -1,8 +1,8 @@ package run.halo.app.utils; import org.junit.Test; -import run.halo.app.handler.theme.support.Group; -import run.halo.app.handler.theme.impl.YamlThemeConfigResolverImpl; +import run.halo.app.handler.theme.config.support.Group; +import run.halo.app.handler.theme.config.impl.YamlThemeConfigResolverImpl; import java.io.IOException; import java.util.List;