【问题标题】:Get a specific service implementation based on a parameter根据参数获取具体的服务实现
【发布时间】:2016-10-23 05:26:31
【问题描述】:

在我的 Sling 应用程序中,我有数据呈现文档、页面和内容节点。我们主要将这些文档作为 HTML 提供,但现在我想要一个 servlet 将这些文档作为 PDF 和 PPT 提供。

基本上,我考虑过实现工厂模式:在我的 servlet 中,取决于请求的扩展(pdf 或 ppt),我将从 DocumentBuilderFactory 中获得正确的 DocumentBuilder 实现,PdfDocumentBuilder 或 PptDocumentBuilder。

所以首先我有这个:

public class PlanExportBuilderFactory {

  public PlanExportBuilder getBuilder(String type) {
    PlanExportBuilder builder = null;
    switch (type) {
      case "pdf":
        builder = new PdfPlanExportBuilder();
        break;
      default: 
        logger.error("Unsupported plan export builder, type: " + type);
    }
    return builder;
  }
}

在 servlet 中:

@Component(metatype = false)
@Service(Servlet.class)
@Properties({ 
  @Property(name = "sling.servlet.resourceTypes", value = "myApp/document"), 
  @Property(name = "sling.servlet.extensions", value = { "ppt", "pdf" }),
  @Property(name = "sling.servlet.methods", value = "GET") 
})
public class PlanExportServlet extends SlingSafeMethodsServlet {

  @Reference
  PlanExportBuilderFactory builderFactory;

  @Override
  protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {

    Resource resource = request.getResource();

    PlanExportBuilder builder = builderFactory.getBuilder(request.getRequestPathInfo().getExtension());
  }
}    

但问题是,在构建器中我想引用其他服务来访问 Sling 资源,并且使用此解决方案,它们没有被绑定。

我查看了带有 OSGi 的服务工厂,但据我了解,您可以使用它们来配置相同的服务实现。

然后我发现你可以通过命名得到一个具体的实现,或者使用一个属性和一个过滤器。

所以我最终得到了这个:

public class PlanExportBuilderFactory {

  @Reference(target = "(builderType=pdf)")
  PlanExportBuilder pdfPlanExportBuilder;

  public PlanExportBuilder getBuilder(String type) {
    PlanExportBuilder builder = null;
    switch (type) {
      case "pdf":
        return pdfPlanExportBuilder;
      default: 
        logger.error("Unsupported plan export builder, type: " + type);
    }
    return builder;
  }

}

定义“builderType”属性的构建器:

// AbstractPlanExportBuilder implements PlanExportBuilder interface
@Component
@Service(value=PlanExportBuilder.class)
public class PdfPlanExportBuilder extends AbstractPlanExportBuilder {

  @Property(name="builderType", value="pdf")

  public PdfPlanExportBuilder() {
    planDocument = new PdfPlanDocument();
  }
}

我想知道这是否是检索我的关于 OSGi 良好实践的 PDF 构建器实现的好方法。

编辑 1

根据 Peter 的回答,我尝试添加多个引用,但对于 Felix,它似乎不起作用:

 @Reference(name = "planExportBuilder", cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
private Map<String, PlanExportBuilder> builders = new ConcurrentHashMap<String, PlanExportBuilder>();

protected final void bindPlanExportBuilder(PlanExportBuilder b, Map<String, Object> props) {
  final String type = PropertiesUtil.toString(props.get("type"), null);
  if (type != null) {
    this.builders.put((String) props.get("type"), b);
  }
}

protected final void unbindPlanExportBuilder(final PlanExportBuilder b, Map<String, Object> props) {
  final String type = PropertiesUtil.toString(props.get("type"), null);
  if (type != null) {
    this.builders.remove(type);
  }
}

我收到这些错误:

@Reference(builders) : Missing method bind for reference planExportBuilder
@Reference(builders) : Something went wrong: false - true - MANDATORY_MULTIPLE
@Reference(builders) : Missing method unbind for reference planExportBuilder

http://felix.apache.org/documentation/subprojects/apache-felix-maven-scr-plugin/scr-annotations.html#reference 此处的 Felix 文档说明了绑定方法:

默认值是通过将引用名称附加到字符串绑定而创建的名称。该方法必须声明为公共或受保护的,并采用使用服务接口类型声明的单个参数

因此,据此,我知道它不能与 Felix 一起使用,因为我正在尝试传递两个参数。但是,我在这里找到了一个示例,该示例似乎与您的建议相匹配,但我无法使其工作:https://github.com/Adobe-Consulting-Services/acs-aem-samples/blob/master/bundle/src/main/java/com/adobe/acs/samples/services/impl/SampleMultiReferenceServiceImpl.java

编辑 2 只需将引用移到类上方即可使其工作:

@References({
  @Reference(
    name = "planExportBuilder",
    referenceInterface = PlanExportBuilder.class,
    policy = ReferencePolicy.DYNAMIC,
    cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE)
})
public class PlanExportServlet extends SlingSafeMethodsServlet {

【问题讨论】:

    标签: osgi sling


    【解决方案1】:

    工厂是邪恶的 :-) 主要原因当然是通常使用的令人讨厌的类加载技巧,但也因为它们往往具有全局知识。通常,您希望能够使用新的 DocumentBuilder 添加捆绑包,然后该类型应该可用。

    因此,更面向 OSGi 的解决方案是使用服务属性。这可能看起来像:

    @Component( property=HTTP_WHITEBOARD_FILTER_REGEX+"=/as")
    public class DocumentServlet {
    
      final Map<String,DocBuilder>        builders = new ConcurrentHashMap<>();
    
      public void doGet( HttpServletRequest rq, HttpServletResponse rsp ) 
                               throws IOException, ServletException {
    
        InputStream in = getInputStream( rq.getPathInfo() );
        if ( in == null ) 
          ....
    
        String type = toType( rq.getPathInfo(), rq.getParameter("type") );
    
        DocBuilder docbuilder = builders.get( type );
        if ( docbuilder == null)
           ....
    
        docbuilder.convert( type, in, rsp.getOutputStream() );
     }
    
     @Reference( cardinality=MULTIPLE, policy=DYNAMIC )
     void addDocBuilder( DocBuilder db, Map<String,Object> props ) {
        docbuilders.put(props.get("type"), db );
     }
    
     void removeDocBuilder(Map<String,Object> props ) {
        docbuilders.remove(props.get("type"));
     }
    

    }

    DocBuilder 可能如下所示:

    @Component( property = "type=ppt-pdf" )
    public class PowerPointToPdf implements DocBuilder {
        ...
    }
    

    【讨论】:

    • 当你说“工厂是邪恶的”时,你是指在 OSGi 上下文中吗?我们的应用程序很小,因此现在所有代码都打包在一个包中,但我会试试你的解决方案,谢谢。
    • 我不喜欢 Java 工厂,因为它们是“全局”变量,倾向于使用对模块化原则造成严重破坏的类加载“黑客”,并要求需要对象的人采取主动。 OSGi 服务模型允许一方使用自己的上下文注册一个完全初始化的对象。这是一个代理模型,而且功能更强大。
    • 我已尝试实施您的建议,但无法实现,我已经编辑了我的问题。它找不到绑定和取消绑定方法。此外,我意识到将我的文档构建器转换为 OSGi 服务将使其成为单例,这将无法与构建器模式实现一起使用。是否有可能将服务引用获取到本身不是服务的类中?
    • 只要让你的 DocBuilder 成为一个 DocBuilder 服务。我不知道为什么你不能让伪代码工作,它很简单 DS?
    • 我只需将引用移动到类声明上方的@References 中,这实际上是在 Adob​​e 示例中所做的。我不确定您所说的“只是让您的 DocBuilder 成为 DocBuilderBuilder 服务”是什么意思?是否可以为我的 PdfDocBuilder 生成不同的服务实例,并能够通过摆脱单例问题获得其他服务引用?
    猜你喜欢
    • 2021-09-16
    • 1970-01-01
    • 1970-01-01
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    相关资源
    最近更新 更多