2016-06-21 51 views
2

在我的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()); 
    } 
}  

但問題是,在建設者,我想引用其他服務來訪問吊帶資源,並與這個解決方案,他們沒有約束。

我用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(); 
    } 
} 

我想知道這是否是要找回我的PDF的好方法關於OSGi良好實踐的建設者實施。

編輯1

從彼得的回答我試圖添加多個引用,但與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 

這裏的Felix文檔http://felix.apache.org/documentation/subprojects/apache-felix-maven-scr-plugin/scr-annotations.html#reference對綁定方法說:

默認值是通過將引用名稱附加到字符串綁定而創建的名稱。該方法必須聲明爲public或保護,並採取其聲明與服務接口類型

所以根據這一點,我的理解是不能與Felix工作,因爲我想傳遞兩個參數一個參數。然而,我發現這裏的例子,似乎與您所建議的是什麼,但我不能讓它工作:https://github.com/Adobe-Consulting-Services/acs-aem-samples/blob/master/bundle/src/main/java/com/adobe/acs/samples/services/impl/SampleMultiReferenceServiceImpl.java

EDIT 2 剛把移動類以上的參考,使其工作:

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

回答

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 { 
    ... 
} 
+0

當你說 「工廠是邪惡的」 你在OSGi上下文中的具體內容是?我們的應用程序很小,因此現在所有代碼都打包在一個包中,但我會嘗試解決方案,謝謝。 –

+0

我不喜歡Java工廠,因爲它們是'全局'變量,傾向於使用類加載'黑客'破壞模塊化原則,並要求需要對象的人主動。 OSGi服務模型允許一方用自己的上下文註冊完全初始化的對象。這是一個經紀人模式,更強大的imho。 –

+0

我試圖實現你的建議,但我不能讓它工作,我編輯了我的問題。它無法找到綁定和解除綁定的方法。另外,我已經意識到,將我的文檔生成器轉換爲OSGi服務將使其成爲一個單例,而這對於構建器模式實現不起作用。是否有可能將服務引用導入不是服務本身的類中? –

相關問題