2010-06-23 53 views
1

我正在創建一個自定義組件,該組件是給定產品編號的圖像查看器。我訪問使用BalusC的ImageServlet的修改版本這些文件:JSF 2 cc:將屬性傳遞到後臺bean

@WebServlet(name="ImageLoader", urlPatterns={"/ImageLoader"}) 
public class ImageLoader extends HttpServlet { 

    private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB. 

    private static String imagePath = "\\\\xxx.xxx.x.x\\root\\path\\to\\images\\"; 

    /** 
     * This code is a modified version of the ImageServlet found at balusc.blogspot.com. 
    * It expects the parameters id and n. 
    * <ul> 
    * <li><b>id:</b> the product number</li> 
    * <li><b>n:</b> the image number to load.</li> 
    */ 
    public void goGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException { 

     String productNumber = URLDecoder.decode(request.getParameter("id"),"utf-8"); 
     String img = URLDecoder.decode(request.getParameter("n"),"utf-8"); 

     if (productNumber == null || img == null) { 
      response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. 
      return; 
     } 

     String path = generatePath(productNumber); 

     File image = new File(generatePath(productNumber), generateImageName(img)); 

     // Check if file actually exists in filesystem. 
     if (!image.exists()) { 
      response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. 
      return; 
     } 

     String contentType = getServletContext().getMimeType(image.getName()); 

     if (contentType == null || !contentType.startsWith("image")) { 
      response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. 
      return; 
     } 

     // Init servlet response. 
     response.reset(); 
     response.setBufferSize(DEFAULT_BUFFER_SIZE); 
     response.setContentType(contentType); 
     response.setHeader("Content-Length", String.valueOf(image.length())); 
     response.setHeader("Content-Disposition", "inline; filename=\"" + image.getName() + "\""); 

     // Prepare streams. 
     BufferedInputStream input = null; 
     BufferedOutputStream output = null; 

     try { 
      // Open streams. 
      input = new BufferedInputStream(new FileInputStream(image), DEFAULT_BUFFER_SIZE); 
      output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE); 

      // Write file contents to response. 
      byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 
      int length; 
      while ((length = input.read(buffer)) > 0) { 
       output.write(buffer, 0, length); 
      } 
     } finally { 
      close(output); 
      close(input); 
     } 
    } 

    private String generateImageName(String n) { 
     int imageNum = Integer.parseInt(n); 

     StringBuilder ret = new StringBuilder("img-"); 
     if (imageNum < 10) { 
      ret.append("00"); 
     } 
     else if(imageNum < 100) { 
      ret.append("0"); 
     } 
     ret.append(n); 
     ret.append(".jpg"); 
     return ret.toString(); 
    } 


    public static String generatePath(String productNumber) { 
     Long productNumberLng = Long.parseLong(productNumber); 

     StringBuilder ret = new StringBuilder(imagePath); 

     Long thousandPath = productNumberLng - (productNumberLng % 1000); 
     ret.append(thousandPath); 
     ret.append("s\\"); 
     ret.append(productNumber); 
     ret.append("\\"); 
     ret.append(productNumber); 
     ret.append("\\"); 

     return ret.toString(); 
    } 

    private static void close(Closeable resource) { 
     if (resource != null) { 
      try { 
       resource.close(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

接下來,我創建了一個複合組件:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:ui="http://java.sun.com/jsf/facelets" 
     xmlns:cc="http://java.sun.com/jsf/composite" 
     xmlns:h="http://java.sun.com/jsf/html" 
     xmlns:f="http://java.sun.com/jsf/core"> 

    <!-- INTERFACE --> 
    <cc:interface> 
     <cc:attribute name="productNumber" shortDescription="The product number whose images should be displayed." 

        type="java.lang.Long" /> 
     <cc:attribute name="listID" shortDescription="This ID is the html ID of the &lt;ul&gt; element." /> 
    </cc:interface> 

    <!-- IMPLEMENTATION --> 
    <cc:implementation> 
     <div id="#{cc.clientId}"> 
      <ul id="#{cc.attrs.listID}"> 
       <ui:repeat value="#{imageLoaderUtilBean.images}" var="image"> 
        <li> 

         <h:graphicImage value="#{image.url}" alt="#{image.name}" /> 
        </li> 
       </ui:repeat> 
      </ul> 
     </div> 
    </cc:implementation> 
</html> 

正如你所看到的,我只是從管理抓取的圖像列表豆。這是真正必要的唯一原因是因爲我需要知道給定產品有多少圖像。這可能差別很大(從8到100)。下面是代碼:

@ManagedBean 
@RequestScoped 
public class ImageLoaderUtilBean { 

    @ManagedProperty(value = "#{param.id}") 
    private Long productNumber; 

    private List<EvfImage> images; 

    public List<EvfImage> getImages() { 

     if (images == null) { 
      setImages(findImages()); 
     } 
     return images; 
    } 

    public void setImages(List<EvfImage> images) { 
     this.images = images; 
    }  

    public Long getProductNumber() { 
     return productNumber; 
    } 

    public void setProductNumber(Long productNumber) { 
     this.productNumber = productNumber; 
    } 

    private List<EvfImage> findImages() { 


     FilenameFilter jpegFilter = new FilenameFilter() { 

      @Override 
      public boolean accept(File directory, String filename) { 
       return filename.toLowerCase().endsWith(".jpg"); 
      } 
     }; 

     File directory = new File(ImageLoader.generatePath(productNumber.toString())); 
     if (!directory.exists()) { 
      return new ArrayList<EvfImage>(); 
     } 
     File[] files = directory.listFiles(jpegFilter); 

     List<EvfImage> ret = new ArrayList<EvfImage>(); 

     for (int i = 1; i <= files.length; i++) { 
      EvfImage img = new EvfImage(); 
      img.setName("file.getName()"); 
      img.setUrl("/ImageLoader?id=" + productNumber + "&amp;n=" + i); 
      ret.add(img); 
     } 

     return ret; 
    } 

} 

有用於保持我遍歷數據的簡單對象:

public class EvfImage { 

    private String url; 
    private String name; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getUrl() { 
     return url; 
    } 

    public void setUrl(String url) { 
     this.url = url; 
    } 

} 

最後,我使用的http://localhost:8080/project-name/testImages.xhtml?id=213123一個URL測試該複合部件。下面是testImages.xhtml的代碼:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:h="http://java.sun.com/jsf/html" 
     xmlns:sdCom="http://java.sun.com/jsf/composite/components/sd"> 
    <h:head> 
     <title>Facelet Title</title> 
    </h:head> 
    <h:body> 
     <sdCom:imageViewer listID="test" /> 
    </h:body> 
</html> 

這裏的問題是:在應用程序和複合組件之間的交互的唯一的點應該是標籤<sdCom:imageViewer listID="test" />。但是,這是一個抽象漏洞。託管bean根據請求的參數id給出產品編號。這是非常不可取的。它在組件和正在使用它的應用程序之間創建更緊密的耦合。理想情況下,我應該使用如下標籤:<sdCom:imageViewer listID="test" productNumber="213123"/>。但是,我無法想出一個辦法來做到這一點,仍然知道我需要創建多少圖片。

由於提前, 扎克

編輯:這將是完全可以接受的調用一個servlet這需要在產品編號,並返回其該產品具有的圖像的數量。然而,我還沒有找到一種方法來運行一個循環(循環)n,而不是爲一個集合(foreach循環)中的每個對象運行一次。我非常滿意任何涉及從支持bean中刪除@ManagedProperty("#{param.id}")的解決方案。

+1

將複合組件添加到可以傳遞任意'EvfImage'數組的另一個屬性不是更好嗎?這樣你的組件就獨立於託管bean。 – Behrang 2010-06-23 21:12:45

+0

Bytecode忍者:謝謝你的迴應。這聽起來像是一個體面的妥協。在我的特殊情況下,它並不像只傳遞一個產品號(在這種情況下),但它也不會繞過組件的接口。除非有人提出更好的建議,否則我會使用你的建議。謝謝! – 2010-06-23 21:22:38

+0

Bytecode Ninja:我如何限制傳入的列表以確保它只允許列出EvfImages? – 2010-06-24 20:35:15

回答

2

ImageLoaderUtilBean甲更換@ManagedProperty(value="#{param.id}")將是

<sdCom:imageViewer listID="test" productNumber="#{param.id}" /> 

結合在cc:implementation以下之前ui:repeat

<c:set target="#{imageLoaderUtilBean}" property="productNumber" value="#{cc.attrs.productNumber}" /> 

c:是(實際上氣餒)Facelets的內置JSTL庫將被宣佈如下:

xmlns:c="http://java.sun.com/jsp/jstl/core" 

Facelets有沒有替代c:set(還?)。

+0

這仍然需要我打破複合組件的界面。 這會假定每個使用這個組件的應用程序都有一個'id'的請求參數。因此,單個標籤將決定使用它的應用程序的結構。另外,這會導致將這個標籤嵌入到懶惰的加載選項卡面板等問題。 – 2010-06-23 21:19:18

+0

我明白你的意思。我犯了一個錯誤,我已經更新了答案。但是我不確定這是否能正常工作,因爲JSTL運行JSF的「同步」。這都是理論:) – BalusC 2010-06-24 23:44:02