Рубрики
Без рубрики

Реализация шаблона хранилища внешних конфигураций с помощью Jakarta EE

Узнайте, как отделить конфигурацию от вашего приложения, в этой статье Руана Роча, старшего консультанта по промежуточному программному обеспечению, имеющего опыт разработки с использованием Java и Java EE.

Автор оригинала: PACKT.

Узнайте, как отделить конфигурацию от вашего приложения, в этой статье Руана Роча, старшего консультанта по промежуточному программному обеспечению, имеющего опыт разработки с использованием Java и Java EE.

Поставка программного обеспечения состоит из множества шагов, которые необходимо выполнить. Одним из основных этапов доставки программного обеспечения является настройка вашего приложения. Конфигурация может быть нескольких типов:

• Конфигурация файловой системы и каталогов • Конфигурация путей к другим службам или ресурсам • Конфигурация базы данных • Другие конфигурации в среде

Среда состоит из разработки, тестирования, постановки и производства. Приложение должно быть настроено в каждой из этих сред, которые имеют одинаковые свойства, но разные значения. Для настройки разработчик обычно использует файл конфигурации (.properties, .xml или другой) внутри приложения. Однако этот метод связывает пакет приложения со средой, и разработчику необходимо создать отдельный пакет для каждой среды. Этот пакет будет знать только детали среды, в которой он работает. Это не лучшая практика и увеличивает сложность доставки статьи как для приложений с монолитной архитектурой, так и для приложений с архитектурой микросервисов.

Решением этого метода является внешний шаблон хранилища конфигурации. Шаблон хранилища внешних конфигураций-это операционный шаблон (в некоторых литературных источниках он определяется как шаблон архитектуры или шаблон облачного проектирования), который отделяет детали конфигурации от приложения. Внешняя конфигурация не знает значения свойств конфигурации и знает только свойства, которые должны быть считаны из хранилища конфигурации. На рисунке ниже показана разница между приложением, использующим шаблон внешнего хранилища конфигурации, и приложением, которое не использует шаблон внешнего хранилища конфигурации:

На рисунке ниже показана разница между приложением, использующим шаблон внешнего хранилища конфигурации, и приложением, которое не использует шаблон внешнего хранилища конфигурации:

Преимущества внешнего шаблона хранилища конфигурации

Шаблон хранилища внешних конфигураций имеет множество преимуществ, но в этой статье подробно рассматриваются лишь некоторые из них. Одно из основных преимуществ включает обновление значений конфигурации без перестройки приложения. После создания пакета он может запускаться в любой среде до тех пор, пока не возникнет проблема (не связанная с конфигурацией) или среда не будет настроена неправильно.

Внешний шаблон хранилища конфигурации также обеспечивает большую свободу. Любая команда (инфраструктура или промежуточное программное обеспечение) может управлять конфигурацией без помощи разработчика, поскольку пакет приложений не нуждается в обновлении. Кроме того, он централизует все конфигурации, и различные приложения могут считывать свойства конфигурации из одного и того же местоположения.

Реализация шаблона внешнего хранилища конфигурации с использованием Jakarta EE

Шаблон хранилища внешних конфигураций может быть реализован следующими способами: • Использование сервера приложений в качестве сервера конфигурации по свойствам системы • Использование внешнего файла или набора внешних файлов • Использование источника данных (реляционная база данных, NoSQL или другие) • Использование сервера пользовательской конфигурации

В этом разделе будет подробно рассмотрена реализация шаблона хранилища внешних конфигураций с использованием сервера приложений в качестве сервера конфигурации по свойствам системы и с использованием внешнего файла или набора внешних файлов.

В этом сценарии у вас будет три ресурса JAX-RS: • Для возврата приветственного сообщения • Для загрузки файлов • Для загрузки файлов

На ресурсе, который возвращает приветственное сообщение, у вас будет один способ получить сообщение из системных свойств сервера приложений и другой способ получить сообщение из внешнего файла. Чтобы реализовать это, вы можете использовать производителя CDI для чтения свойств как с сервера приложений, так и с внешнего файла. Кроме того, создайте свойство квалификатора@, которое будет использоваться производителем в момент инъекции.

Создание хранилища конфигураций.свойства

Используйте этот файл, когда приложение использует внешний файл. Это единственный файл, настроенный внутри приложения, позволяющий приложению знать, где находится хранилище конфигурации.

path=${path_to_configuration_store}

Квалификатор реализации

Приведенный ниже код показывает реализацию классификатора, используемого для настройки инъекции и разрешения производителю продукта вводимого значения:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Property {
 
    @Nonbinding String key() default "";
 
    @Nonbinding boolean container() default true;
 
    @Nonbinding String fileName() default "";
 
}

Обратите внимание, что квалификатор имеет три атрибута:

• Ключ – Этот атрибут используется для передачи ключа свойства. • Контейнер – Этот атрибут используется для определения атрибута, который будет считываться из контейнера Jakarta EE. Если контейнер атрибутов имеет значение true, приложение выполнит поиск свойства на сервере приложений. • Имя файла – Этот атрибут используется для передачи пути к файлу при использовании внешнего файла. Когда имя файла передано и контейнер атрибутов имеет значение true, приложение сначала выполнит поиск в контейнере Jakarta EE. Если свойство там не найдено, то его можно найти во внешнем файле.

Производитель-исполнитель

Приведенный ниже код показывает реализацию производителя свойств, производителя, используемого для введения свойств:

public class PropertyProducer {
 
    @Property
    @Produces
    public String readProperty(InjectionPoint point){
 
        String key = point
                .getAnnotated()
                .getAnnotation(Property.class)
                .key();
 
        if( point
                .getAnnotated()
                .getAnnotation(Property.class)
                .container() ){
 
           String value = System.getProperty(key);
 
           if( Objects.nonNull(value) ){
               return value;
           }
 
        }
 
        return readFromPath(point
                .getAnnotated()
                .getAnnotation(Property.class)
                .fileName(), key);
 
    }
 
    private String readFromPath(String fileName, String key){
 
        try(InputStream in = new FileInputStream( readPathConfigurationStore() + fileName)){
 
            Properties properties = new Properties();
            properties.load( in );
 
            return properties.getProperty( key );
 
        } catch ( Exception e ) {
            e.printStackTrace();
            throw new PropertyException("Error to read property.");
        }
 
    }
 
    private String readPathConfigurationStore(){
 
        Properties configStore = new Properties();
 
        try( InputStream stream = PropertyProducer.class
                .getResourceAsStream("/configurationStore.properties") ) {
 
            configStore.load(stream);
        }
        catch ( Exception e ) {
            e.printStackTrace();
            throw new PropertyException("Error to read property.");
        }
 
        return configStore.getProperty("path");
    }
 
}

Реализация конфигурации

Этот класс составляет ядро этой статьи, поскольку он содержит конфигурации приложения, которое было считано как с сервера приложений, так и с внешнего файла. Этот класс является одноэлементным, и все введенные свойства конфигурации централизованы в этом классе.

@Singleton
public class Config {
 
    @Inject
    @Property(key="message.welcome")
    public String WELCOME;
 
    @Inject
    @Property(key="message.welcome", container = false, fileName = "config.properties")
    public String WELCOME_EXTERNAL_FILE;
 
    @Inject
    @Property(key="path.download")
    public String PATH_DOWNLOAD;
 
    @Inject
    @Property(key="path.upload")
    public String PATH_UPLOAD;
 
}

Внедрение приветственного ресурса

Этот код включает реализацию ресурса JAX-RS, которая имеет два метода:

• Для возврата приветственного сообщения, определенного в системных свойствах сервера приложений • Для возврата приветственного сообщения, определенного во внешний файл.

@Path("/welcome")
public class WelcomeResource {
 
    @Inject
    private Config config;
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response message(){
 
        Map map = new HashMap();
        map.put("message", config.WELCOME);
 
        return Response
                .status( Response.Status.OK )
                .entity( map )
                .build();
 
    }
 
    @GET
    @Path("/external")
    @Produces(MediaType.APPLICATION_JSON)
    public Response messageExternalFile(){
 
        Map map = new HashMap();
        map.put("message", config.WELCOME_EXTERNAL_FILE);
 
        return Response
                .status( Response.Status.OK )
                .entity( map )
                .build();
 
    }
}

Реализация FileDao

Этот код имеет реализацию FileDao, класс для чтения и записи файла. FileDao используется UploadResource и ресурсом загрузки.

@Stateless
public class FileDao {
 
    @Inject
    private Config config;
 
    public boolean save( File file ){
 
        File fileToSave = new File(config.PATH_UPLOAD + "/" + file.getName());
 
        try (InputStream input = new FileInputStream( file )) {
 
            Files.copy( input, fileToSave.toPath() );
 
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
 
        return true;
    }
 
    public File find( String fileName ){
 
        File file = new File(config.PATH_DOWNLOAD + "/" + fileName);
 
        if( file.exists() && file.isFile() ) {
 
            return file;
        }
 
        return null;
    }
 
}

Реализация UploadResource

Этот код включает в себя реализацию процесса загрузки файла с использованием ресурсов JAX-RS. Обратите внимание, что класс FileDao используется для чтения и записи файла.

@Path("/upload")
public class UploadResource {
 
    @Inject
    private FileDao fileDao;
 
    @POST
    public Response upload(@NotNull File file){
 
        if( fileDao.save( file ) ){
            return Response
                    .created(URI.create("/download?fileName="+ file.getName()))
                    .build();
        }
 
        return Response.serverError().build();
 
    }
}

Реализация Ресурса Загрузки

Этот код включает в себя реализацию процесса загрузки файла с использованием ресурсов JAX-RS. Обратите внимание, что класс FileDao используется для чтения и записи файла.

@Path("/download")
public class DownloadResource {
 
    @Inject
    private FileDao fileDao;
 
    @GET
    public Response download(@NotNull @QueryParam("fileName") String fileName){
 
        File file = fileDao.find( fileName );
 
        if( Objects.isNull( file ) ){
 
            return Response.status(Response.Status.NOT_FOUND).build();
 
        }
 
        return Response.ok(file)
                .header("Content-Disposition",
                "attachment; filename=\"" + fileName + "\"")
                .build();
    }
 
}

Конфигурация Микропрофиля Eclipse

Jakarta EE-это новый проект, основанный на Java EE 8, но многие люди говорят о возможном слиянии между MicroProfile и Jakarta EE. Проект МикроПрофиля включает решение под названием Eclipse Micro Profile Config версии 1.3, которое позволяет реализовать шаблон хранилища внешних конфигураций. Если вы хотите узнать больше о конфигурации микропрофиля Eclipse, перейдите по ссылке https://microprofile.io/project/eclipse/microprofile-config

Вывод

Используя шаблон хранилища внешних конфигураций, вы можете отделить конфигурации от приложения. Таким образом, вы можете обновить некоторые конфигурации без перестройки приложения. Кроме того, другие команды могут управлять конфигурацией без вмешательства разработчика, и вы можете использовать один и тот же набор конфигураций с любыми приложениями.

Это хорошая практика, в первую очередь, если приложение было создано с использованием архитектуры микросервисов, поскольку это способствует лучшей доставке и упрощению обслуживания. Чтобы увидеть полный код этого примера, посетите https://github.com/rhuan080/jakartaee-example-external-conf

Если вы нашли эту статью интересной, вы можете изучить Шаблоны проектирования Java EE 8 и лучшие практики для эффективного решения распространенных проблем, с которыми сталкиваются при разработке приложений, и вам будет удобно работать над масштабируемыми и поддерживаемыми проектами любого размера. Шаблоны проектирования Java EE 8 и лучшие практики помогают разработчикам улучшить качество кода и продвинуться на более высокие уровни архитектурного творчества, изучая назначение каждого доступного шаблона и демонстрируя его реализацию на различных примерах кода.

Оригинал: “https://www.codementor.io/@packt/implementing-external-configuration-store-pattern-with-jakarta-ee-pfv6w07v4”