避免全局状态的REST服务器java存储和加载配置(即单例与上下文、依赖注入)
我正在使用tomcat开发Java体系结构,我遇到了一种我认为非常普遍的情况,但在阅读StackOverflow中的几个问题/答案后,我找不到一个明确的答案。我的体系结构有一个REST API(在tomcat上运行),它接收一个或多个文件及其相关元数据,并将它们写入存储器。存储层的配置与REST API服务器有1-1关系,因此直观的方法是编写一个单例来保存该配置
显然,我知道,由于全球状况和嘲笑单身汉的困难,单身汉带来了可测试性问题。我也想过使用上下文模式,但我不相信上下文模式适用于这种情况,我担心我最终会使用“上下文反模式”进行编码
让我给你们介绍一下我写作的背景。该体系结构由以下组件组成:
向REST API发送请求的客户端上载或检索“保存对象”,或者简单地说,是JSON或XML格式的POs(文件+元数据)
高级REST API,用于接收来自客户端的请求并将数据存储在存储层中
存储层,可能包含OpenStack Swift容器、磁带库和文件系统的组合。在我的体系结构中,每一个“存储容器”(为了简单起见,我称之为文件系统容器)都被称为端点。存储层显然与REST API所在的服务器不同
端点的配置通过REST API(例如POST/configEndpoint)完成,以便管理用户可以通过HTTP调用注册新端点、编辑或删除现有端点。虽然我只使用OpenStack Swift端点实现了该架构,但我预计每个端点的信息至少包含一个IP地址、某种形式的身份验证信息和一个驱动程序名,例如“Swift驱动程序”、“LTFS驱动程序”,等等(这样,当新的存储技术出现时,只要有人为我的体系结构编写驱动程序,它们就可以轻松集成到我的体系结构中)
我的问题是:如何以可测试、可重用和优雅的方式存储和加载配置?我甚至不考虑将配置对象传递给实现REST API调用的所有各种方法。
REST API调用的几个示例以及配置的作用:
// Retrieve a preservation object metadata (PO)
@GET
@Path("container/{containername}/{po}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public PreservationObjectInformation getPOMetadata(@PathParam("containername") String containerName, @PathParam("po") String poUUID) {
// STEP 1 - LOAD THE CONFIGURATION
// One of the following options:
// StorageContext.loadContext(containerName);
// Configuration.getInstance(containerName);
// Pass a configuration object as an argument of the getPOMetadata() method?
// Some sort of dependency injection
// STEP 2 - RETRIEVE THE METADATA FROM THE STORAGE
// Call the driver depending on the endpoint (JClouds if Swift, Java IO stream if file system, etc.)
// Pass poUUID as parameter
// STEP 3 - CONVERT JSON/XML TO OBJECT
// Unmarshall the file in JSON format
PreservationObjectInformation poi = unmarshall(data);
return poi;
}
// Delete a PO
@DELETE
@Path("container/{containername}/{po}")
public Response deletePO(@PathParam("containername") String containerName, @PathParam("po") String poName) throws IOException, URISyntaxException {
// STEP 1 - LOAD THE CONFIGURATION
// One of the following options:
// StorageContext.loadContext(containerName); // Context
// Configuration.getInstance(containerName); // Singleton
// Pass a configuration object as an argument of the getPOMetadata() method?
// Some sort of dependency injection
// STEP 2 - CONNECT TO THE STORAGE ENDPOINT
// Call the driver depending on the endpoint (JClouds if Swift, Java IO stream if file system, etc.)
// STEP 3 - DELETE THE FILE
return Response.ok().build();
}
// Submit a PO and its metadata
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("container/{containername}/{po}")
public Response submitPO(@PathParam("containername") String container, @PathParam("po") String poName, @FormDataParam("objectName") String objectName,
@FormDataParam("inputstream") InputStream inputStream) throws IOException, URISyntaxException {
// STEP 1 - LOAD THE CONFIGURATION
// One of the following options:
// StorageContext.loadContext(containerName);
// Configuration.getInstance(containerName);
// Pass a configuration object as an argument of the getPOMetadata() method?
// Some sort of dependency injection
// STEP 2 - WRITE THE DATA AND METADATA TO STORAGE
// Call the driver depending on the endpoint (JClouds if Swift, Java IO stream if file system, etc.)
return Response.created(new URI("container/" + container + "/" + poName))
.build();
}
**更新#1-我的实现基于@mawalker的评论**
下面是我使用建议答案的实现。工厂创建具体的策略对象,以实现较低级别的存储操作。上下文对象(由中间件来回传递)包含抽象类型的对象(在本例中为接口)StorageContainerStrategy(其实现将取决于运行时每个特定情况下的存储类型)
public interface StorageContainerStrategy {
public void write();
public void read();
// other methods here
}
public class Context {
public StorageContainerStrategy strategy;
// other context information here...
}
public class StrategyFactory {
public static StorageContainerStrategy createStorageContainerStrategy(Container c) {
if(c.getEndpoint().isSwift())
return new SwiftStrategy();
else if(c.getEndpoint().isLtfs())
return new LtfsStrategy();
// etc.
return null;
}
}
public class SwiftStrategy implements StorageContainerStrategy {
@Override
public void write() {
// OpenStack Swift specific code
}
@Override
public void read() {
// OpenStack Swift specific code
}
}
public class LtfsStrategy implements StorageContainerStrategy {
@Override
public void write() {
// LTFS specific code
}
@Override
public void read() {
// LTFS specific code
}
}
共 (0) 个答案