JSF 2 cc: pass атрибут в компонент поддержки - PullRequest
1 голос
/ 24 июня 2010

Я создаю пользовательский компонент, который является средством просмотра изображений для данного номера продукта.Я получаю доступ к этим файлам, используя измененную версию ImageServlet от BalusC:

@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;
    }

}

Наконец, я тестирую этот составной компонент, используя URL-адрес http://localhost:8080/project-name/testImages.xhtml?id=213123. Вот код для 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" />.Тем не менее, это утечка абстракция.Управляемому компоненту присваивается номер продукта на основе параметра id запроса.Это очень нежелательно.Это создает гораздо более тесную связь между компонентом и приложением, которое его использует.В идеале я должен использовать тег следующим образом: <sdCom:imageViewer listID="test" productNumber="213123"/>.Однако я не могу придумать, как это сделать, и все еще знаю, сколько изображений мне нужно создать.

Заранее спасибо, Зак

Редактировать: Этобыло бы вполне приемлемо вызвать сервлет, который принимает номер продукта и возвращает количество изображений, которые имеет этот продукт.Однако мне еще предстоит найти способ запустить цикл n раз (для цикла), а не запускать его один раз для каждого объекта в коллекции (цикл foreach).Я в значительной степени доволен любым решением, которое включает удаление этого @ManagedProperty("#{param.id}") из поддерживающего компонента.

1 Ответ

2 голосов
/ 24 июня 2010

Замена @ManagedProperty(value="#{param.id}") в ImageLoaderUtilBean будет

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

в сочетании со следующим в cc:implementation до ui:repeat:

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

Где c: - это (фактически не одобряемая) встроенная библиотека JSTL Facelets, которую нужно объявить следующим образом:

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

У Facelets нет замены для c:set (пока?).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...