English 中文(简体)
JSF2 Static Resource Management——Copressed
原标题:JSF2 Static Resource Management -- Combined, Compressed

是否有任何人了解一种方法,能够动态地把所有方面结合起来/集中起来:产出标准资源,然后将所有方面结合起来/集中起来:在成品阶段投入成像资源? 混合资源/专用资源可能需要与根据合并资源实力或避免过度处理的办法确定一个关键要素。

如果这一特征不存在,我就希望就此开展工作。 是否有人对执行类似内容的最佳方法有看法。 一台服务器过滤器将运行Ippose,但过滤器必须做比必要的更多的工作——基本上检查全部完成的产出并替换配对。 在原产地阶段实施某些措施似乎会更好,因为所有静态资源都可供使用,而无需全部产出。

感谢任何建议!

<>strong>Edit: > 显示Im not lazy,并将在某种指导下切实开展这项工作,这里是一个 st,抓住了本典资源名称/图书馆,然后从中删除。 你们可以看到,我对下一步应做些什么有疑问,如果我提出“要求”,获得资源,将资源合并起来,然后将这些资源用于资源中心?

package com.davemaple.jsf.listener;

import java.util.ArrayList;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;

import org.apache.log4j.Logger;

/**
 * A Listener that combines CSS/Javascript Resources
 * 
 * @author David Maple<d@davemaple.com>
 *
 */
public class ResourceComboListener implements PhaseListener, SystemEventListener {

    private static final long serialVersionUID = -8430945481069344353L;
    private static final Logger LOGGER = Logger.getLogger(ResourceComboListener.class);

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
     */
    public void afterPhase(PhaseEvent event) {
        FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
     */
    public void beforePhase(PhaseEvent event) {
        //nothing here
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.SystemEventListener#isListenerForSource(java.lang.Object)
     */
    public boolean isListenerForSource(Object source) {
        return (source instanceof UIViewRoot);
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.SystemEventListener#processEvent(javax.faces.event.SystemEvent)
     */
    public void processEvent(SystemEvent event) throws AbortProcessingException {
        FacesContext context = FacesContext.getCurrentInstance();
        UIViewRoot viewRoot = context.getViewRoot();
        List<UIComponent> scriptsToRemove = new ArrayList<UIComponent>();

        if (!context.isPostback()) {

            for (UIComponent component : viewRoot.getComponentResources(context, "head")) {
                if (component.getClass().equals(UIOutput.class)) {
                    UIOutput uiOutput = (UIOutput) component;

                    if (uiOutput.getRendererType().equals("javax.faces.resource.Script")) {
                        String library = uiOutput.getAttributes().get("library").toString();
                        String name = uiOutput.getAttributes().get("name").toString();

                        // make https requests to get the resources?
                        // combine then and save to resource cache?
                        // insert new UIOutput script?

                        scriptsToRemove.add(component);
                    }


                }
            }

            for (UIComponent component : scriptsToRemove) {
                viewRoot.getComponentResources(context, "head").remove(component);
            }

        }
    }

}
最佳回答

答案没有涉及排雷和压缩。 更好地下放CSS/JS个别资源,以建立诸如compression='on>属以下事项:<Connector> /code/conf/server.xml。


http://docs.oracle.com/javaee/6/api/javax/faces/event/SystemEventListener.html” rel=“nofollow”SystemEventListener 已经有了良好的第一步(除某些<代码>外)。 阶段Listener unnecessity。 其次,您需要实施以下习惯:rel=“nofollow”>ResourceHandler rel=“nofollow”<>>Resource/code>。 这一部分并非完全是微不足道的。 如果你希望独立执行共同基金,那么你需要重新发明。

http://docs.oracle.com/javaee/6/api/javax/faces/event/SystemEventListener.html 您需要制定其<条码>图书馆,具体内容为unique,由您的习俗资源手来理解。 您需要根据资源组合(MD5 hash maybe?)将综合资源储存在“unique上的广泛应用中,然后将这一关键内容定为。 作为一种广泛应用的变量,服务器和客户都具有切合优势。

与此类似:

String combinedResourceName = CombinedResourceInfo.createAndPutInCacheIfAbsent(resourceNames);
UIOutput component = new UIOutput();
component.setRendererType(rendererType);
component.getAttributes().put(ATTRIBUTE_RESOURCE_LIBRARY, CombinedResourceHandler.RESOURCE_LIBRARY);
component.getAttributes().put(ATTRIBUTE_RESOURCE_NAME, combinedResourceName + extension);
context.getViewRoot().addComponentResource(context, component, TARGET_HEAD);

Then, in your custom ResourceHandler implementation, you d need to implement the createResource() method accordingly to create a custom Resource implementation whenever the library matches the desired value:

@Override
public Resource createResource(String resourceName, String libraryName) {
    if (RESOURCE_LIBRARY.equals(libraryName)) {
        return new CombinedResource(resourceName);
    } else {
        return super.createResource(resourceName, libraryName);
    }
}

http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html

public CombinedResource(String name) {
    setResourceName(name);
    setLibraryName(CombinedResourceHandler.RESOURCE_LIBRARY);
    setContentType(FacesContext.getCurrentInstance().getExternalContext().getMimeType(name));
    this.info = CombinedResourceInfo.getFromCache(name.split("\.", 2)[0]);
}

这一习俗Resource/code> 执行必须提供适当的RequestPath(PH) 回收URI的方法,随后将列入以下要素:<代码><script><link>:

@Override
public String getRequestPath() {
    FacesContext context = FacesContext.getCurrentInstance();
    String path = ResourceHandler.RESOURCE_IDENTIFIER + "/" + getResourceName();
    String mapping = getFacesMapping();
    path = isPrefixMapping(mapping) ? (mapping + path) : (path + mapping);
    return context.getExternalContext().getRequestContextPath()
        + path + "?ln=" + CombinedResourceHandler.RESOURCE_LIBRARY;
}

Now, the HTML rendering part should be fine. It ll look something like this:

<link type="text/css" rel="stylesheet" href="/playground/javax.faces.resource/dd08b105bf94e3a2b6dbbdd3ac7fc3f5.css.xhtml?ln=combined.resource" />
<script type="text/javascript" src="/playground/javax.faces.resource/2886165007ccd8fb65771b75d865f720.js.xhtml?ln=combined.resource"></script>

其次,你不得不根据浏览器提出的合并资源要求拦截。 这是最困难的部分。 首先,在您的习惯< rel=“nofollow”>ResourceHandler implementation>, 您需要执行

@Override
public void handleResourceRequest(FacesContext context) throws IOException {
    if (RESOURCE_LIBRARY.equals(context.getExternalContext().getRequestParameterMap().get("ln"))) {
        streamResource(context, new CombinedResource(getCombinedResourceName(context)));
    } else {
        super.handleResourceRequest(context);
    }
}

然后,你必须完成实施习惯其他方法的全部工作:Resource 据此执行,例如getInputStream( 填表 InputStream s of the joint resources in a one InputStream and userAgenteds(Update)/a>,其中应适当回应有关要求。

@Override
public Map<String, String> getResponseHeaders() {
    Map<String, String> responseHeaders = new HashMap<String, String>(3);
    SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);
    sdf.setTimeZone(TIMEZONE_GMT);
    responseHeaders.put(HEADER_LAST_MODIFIED, sdf.format(new Date(info.getLastModified())));
    responseHeaders.put(HEADER_EXPIRES, sdf.format(new Date(System.currentTimeMillis() + info.getMaxAge())));
    responseHeaders.put(HEADER_ETAG, String.format(FORMAT_ETAG, info.getContentLength(), info.getLastModified()));
    return responseHeaders;
}

@Override
public InputStream getInputStream() throws IOException {
    return new CombinedResourceInputStream(info.getResources());
}

@Override
public boolean userAgentNeedsUpdate(FacesContext context) {
    String ifModifiedSince = context.getExternalContext().getRequestHeaderMap().get(HEADER_IF_MODIFIED_SINCE);

    if (ifModifiedSince != null) {
        SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);

        try {
            info.reload();
            return info.getLastModified() > sdf.parse(ifModifiedSince).getTime();
        } catch (ParseException ignore) {
            return true;
        }
    }

    return true;
}

我在此举出一个完整的概念工作证据,但是,它太过分地把守则列为SO的答案。 以上只是帮助你朝着正确方向努力的一部分。 我假定,缺失的方法/可变/经常声明足以说明你自己,否则让我知道。


<>Update: as per comments, here s how You can take resources in 综合资源信息:

private synchronized void loadResources(boolean forceReload) {
    if (!forceReload && resources != null) {
        return;
    }

    FacesContext context = FacesContext.getCurrentInstance();
    ResourceHandler handler = context.getApplication().getResourceHandler();
    resources = new LinkedHashSet<Resource>();
    contentLength = 0;
    lastModified = 0;

    for (Entry<String, Set<String>> entry : resourceNames.entrySet()) {
        String libraryName = entry.getKey();

        for (String resourceName : entry.getValue()) {
            Resource resource = handler.createResource(resourceName, libraryName);
            resources.add(resource);

            try {
                URLConnection connection = resource.getURL().openConnection();
                contentLength += connection.getContentLength();
                long lastModified = connection.getLastModified();

                if (lastModified > this.lastModified) {
                    this.lastModified = lastModified;
                }
            } catch (IOException ignore) {
                // Can t and shouldn t handle it here anyway.
            }
        }
    }
}

(上述方法通过reload(>)法和根据拟设定的特性之一而定的接收器打上<>。

这里是<代码>。 综合资源信息数据库 参看:

final class CombinedResourceInputStream extends InputStream {

    private List<InputStream> streams;
    private Iterator<InputStream> streamIterator;
    private InputStream currentStream;

    public CombinedResourceInputStream(Set<Resource> resources) throws IOException {
        streams = new ArrayList<InputStream>();

        for (Resource resource : resources) {
            streams.add(resource.getInputStream());
        }

        streamIterator = streams.iterator();
        streamIterator.hasNext(); // We assume it to be always true; CombinedResourceInfo won t be created anyway if it s empty.
        currentStream = streamIterator.next();
    }

    @Override
    public int read() throws IOException {
        int read = -1;

        while ((read = currentStream.read()) == -1) {
            if (streamIterator.hasNext()) {
                currentStream = streamIterator.next();
            } else {
                break;
            }
        }

        return read;
    }

    @Override
    public void close() throws IOException {
        IOException caught = null;

        for (InputStream stream : streams) {
            try {
                stream.close();
            } catch (IOException e) {
                if (caught == null) {
                    caught = e; // Don t throw it yet. We have to continue closing all other streams.
                }
            }
        }

        if (caught != null) {
            throw caught;
        }
    }

}

<>Update 2:一个具体和可再利用的解决方案可在总括项目中找到。 另见CombinedResourceHandler 显示case page and rel=“nofollow”API document,详情。

问题回答

在执行你自己的解决办法之前,你不妨评价JAWR。 我在两个项目中都使用了这一系统,这是一个巨大的成功。 它用于JSF1.2项目,但我认为很容易将其扩大到与JF2.0合作。 只是给它一个尝试。

http://showcase.omnifaces.org/resourcehandlers/CombinedResourceHandler”rel=“nofollow”CombinedResourceHandler 这是一种极佳的用途,但我也热心分享这一极佳的幻影:>><<<<。 我认为,这可用于清理/清理js/cs案卷和营地;/或将其合并为建筑时间和营地中较少的资源;在经营期间并非动态的,这使得它成为一种更有成效的解决办法。

还对这个出色的图书馆进行了研究:webutilities

I have an other solution for JSF 2. Might also rok with JSF 1, but i do not know JSF 1 so i can not say. The Idea works mainly with components from h:head and works also for stylesheets. The result is always one JavaScript (or Stylesheet) file for a page! It is hard for me to describe but i try.

I overload the standard JSF ScriptRenderer (or StylesheetRenderer) and configure the renderer for the h:outputScript component in the faces-config.xml. The new Renderer will now not write anymore the script-Tag but it will collect all resources in a list. So first resource to be rendered will be first item in the list, the next follows and so on. After last h:outputScript component ist rendered, you have to render 1 script-Tag for the JavaScript file on this page. I make this by overloading the h:head renderer.

Now comes the idea: I register an filter! The filter will look for this 1 script-Tag request. When this request comes, i will get the list of resources for this page. Now i can fill the response from the list of resources. The order will be correct, because the JSF rendering put the resources in correct order into the list. After response is filled, the list should be cleared. Also you can do more optimizations because you have the code in the filter....

I have code that works superb. My code also can handle browser caching and dynamic script rendering. If anybody is interested i can share the code.





相关问题
JSF a4j:support with h:selectManyCheckbox

I m having trouble with a JSF selectManyCheckbox and A4J support. The purpose is to run some action when a checkbox is selected. This works perfectly in Firefox. Yet, when testing in any IE (ie6 / ie7 ...

Mojarra for JSF Encoding

Can anyone teach me how to use mojarra to encode my JSF files. I downloaded mojarra and expected some kind of jar but what i had downloaded was a folder of files i don t know what to do with

如何拦截要求终止?

在共同基金中,如果用户要求终止,就需要采取一些行动。 我需要某种拦截器,但我不知道如何这样做。 我需要帮助。 增 编

ICEFaces inputFile getting the file content without upload

Is there any way of just getting the content of the browsed file without any upload/file transfer operations? I currently use ICEFaces inputFile component but I do not need the default uploading ...

Weird behaviour of h:commandLink action (MethodExpression)

I have two JSPs where I am displaying some info from database in a h:dataTable. One of them is showing all the info, and one of them user specifically. I have showXML.jsp that shows the "XML" column ...

How to correctly use ResultSet with h:dataTable

The problem is, that after displaying the ResultSet with <h:dataTable>, the connection is left open. If I close it, it closes the ResultSet too. I m thinking about copying the ResultSet data ...