根据参数

时间:2016-06-21 15:11:43

标签: osgi sling

在我的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文档说明了绑定方法:

  

默认值是通过将引用名称附加到字符串bind来创建的名称。该方法必须声明为public或protected,并采用使用服务接口类型

声明的单个参数

因此,根据这一点,我理解它不适用于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 {

1 个答案:

答案 0 :(得分:2)

工厂是邪恶的:-)主要原因当然是通常使用的令人讨厌的类加载黑客,但也因为他们倾向于拥有全球知识。通常,您希望能够使用新的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 {
    ...
}