Реализация шаблона внешнего хранилища конфигурации с помощью Jakarta EE
Узнайте, как отделить конфигурацию от вашего приложения, из этой статьи Руана Роча, старшего консультанта по промежуточному программному обеспечению с опытом разработки с использованием Java и Java EE.
Доставка программного обеспечения состоит из множества шагов, которые необходимо выполнить. Одним из основных шагов в доставке программного обеспечения является настройка вашего приложения. Конфигурация может быть нескольких видов:
• Конфигурация файловой системы и каталогов
• Конфигурация путей к другим службам или ресурсам
• Конфигурация базы данных
• Другие конфигурации в среде
Среда состоит из разработки, тестирования, постановки и производства. Приложение необходимо настроить в каждой из этих сред, которые имеют одинаковые свойства, но разные значения. Для настройки разработчик обычно использует файл конфигурации (.properties, .xml или другой) внутри приложения.
Однако этот метод связывает пакет приложения со средой, и разработчику необходимо создать отдельный пакет для каждой среды. Этот пакет будет знать только детали среды, в которой он работает. Это не лучшая практика, которая усложняет доставку статьи как в приложение с монолитной архитектурой, так и в приложение с микросервисной архитектурой.
Решением этого метода является внешний шаблон хранилища конфигурации. Шаблон внешнего хранилища конфигурации — это операционный шаблон (в некоторых источниках он определяется как архитектурный шаблон или шаблон облачного проектирования), который отделяет детали конфигурации от приложения. Внешняя конфигурация не знает значения свойств конфигурации и знает только те свойства, которые должны быть прочитаны из хранилища конфигурации.
На приведенном ниже рисунке показана разница между приложением, использующим шаблон внешнего хранилища конфигураций, и приложением, которое не использует шаблон внешнего хранилища конфигураций:
Преимущества шаблона внешнего хранилища конфигурации
Шаблон внешнего хранилища конфигурации имеет много преимуществ, но в этой статье подробно описаны лишь некоторые из них. Одним из основных преимуществ является обновление значений конфигурации без перестроения приложения. После создания пакета он может работать в любой среде до тех пор, пока не возникнет проблема (не связанная с конфигурацией) или пока среда не будет настроена неправильно.
Шаблон внешнего хранилища конфигурации также предоставляет больше свободы. Любая команда (инфраструктура или промежуточное ПО) может управлять конфигурацией без помощи разработчика, так как пакет приложения не нужно обновлять. Кроме того, он централизует все конфигурации, и различные приложения могут считывать свойства конфигурации из одного и того же места.
Реализация шаблона внешнего хранилища конфигурации с помощью Jakarta EE
Шаблон внешнего хранилища конфигурации может быть реализован следующими способами:
• Использование сервера приложений в качестве сервера конфигурации по свойствам системы
• Использование внешнего файла или набора внешних файлов
• Использование источника данных (реляционная база данных, NoSQL и др.)
• Использование пользовательского сервера конфигурации
В этом разделе будет рассмотрена реализация шаблона внешнего хранилища конфигурации с использованием сервера приложений в качестве сервера конфигурации с помощью системных свойств и с использованием внешнего файла или набора внешних файлов.
В этом сценарии у вас будет три ресурса JAX-RS:
• Чтобы вернуть приветственное сообщение
• Для загрузки файлов
• Для загрузки файлов
Для ресурса, который возвращает приветственное сообщение, у вас будет один метод для получения сообщения из системных свойств сервера приложений и другой метод для получения сообщения из внешнего файла. Чтобы реализовать это, вы можете использовать производителя CDI для чтения свойств как с сервера приложений, так и из внешнего файла. Кроме того, создайте квалификатор @Property, который будет использоваться производителем в момент внедрения.
Создание ConfigurationStore.properties
Используйте этот файл, когда приложение использует внешний файл. Это единственный файл, сконфигурированный внутри приложения, который позволяет приложению узнать, где находится хранилище конфигурации.
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, приложение будет искать свойство на сервере приложений.
• FileName — этот атрибут используется для передачи пути к файлу при использовании внешнего файла. Если передано имя файла и атрибут container имеет значение true, приложение сначала будет выполнять поиск в контейнере Jakarta EE. Если свойства там нет, то его можно найти во внешнем файле.
Реализующий продюсер
В приведенном ниже коде показана реализация PropertyProducer, производителя, используемого для внедрения свойств:
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 и DownloadResource.
@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();
}
}
Реализация DownloadResource
Этот код включает в себя реализацию процесса ресурсов 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. Проект MicroProfile включает решение под названием Eclipse MicroProfile Config версии 1.3, которое позволяет реализовать шаблон внешнего хранилища конфигурации. Если вы хотите узнать больше о Eclipse MicroProfile Config, перейдите по ссылке
Вывод
Используя шаблон внешнего хранилища конфигураций, вы можете отделить конфигурации от приложения. Таким образом, вы можете обновить некоторые конфигурации без пересборки вашего приложения. Кроме того, другие команды могут управлять конфигурацией без вмешательства разработчика, и вы можете совместно использовать один и тот же набор конфигураций с любыми приложениями.
Это хорошая практика, особенно если приложение было создано с использованием микросервисной архитектуры, поскольку это способствует лучшей доставке и упрощению обслуживания. Чтобы увидеть полный код этого примера, посетите
Если вы нашли эту статью интересной, вы можете изучить Шаблоны проектирования Java EE 8 и лучшие практики для эффективного решения общих проблем, возникающих при разработке приложений, и будет комфортно работать над масштабируемыми и поддерживаемыми проектами любого размера. Шаблоны проектирования Java EE 8 и лучшие практики помогает разработчикам добиться лучшего качества кода и перейти на более высокий уровень архитектурного творчества, исследуя назначение каждого доступного шаблона и демонстрируя его реализацию на различных примерах кода.