/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.citrus.service.uribroker.uri; import static com.alibaba.citrus.springext.util.SpringExtUtil.*; import static com.alibaba.citrus.util.Assert.ExceptionType.*; import static com.alibaba.citrus.util.Assert.*; import static com.alibaba.citrus.util.CollectionUtil.*; import static com.alibaba.citrus.util.StringUtil.*; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import com.alibaba.citrus.service.template.Renderable; import com.alibaba.citrus.service.uribroker.interceptor.URIBrokerInterceptor; import com.alibaba.citrus.util.StringEscapeUtil; /** * 这个类将URIBroker中,和URL的结构没有直接关系的一些基础特性分离出来,使代码更清晰。 * * @author Michael Zhou */ public abstract class URIBrokerFeatures implements Renderable { protected final Renderer renderer = new Renderer(); private Boolean requestAware; private HttpServletRequest request; private URIBroker parent; private boolean initialized; private String charset; private boolean autoReset; private Map<URIBrokerInterceptor, Integer> interceptors; /** 设置运行时信息。由Spring自动装入。 */ public void setRequest(HttpServletRequest request) { this.request = request; } /** 是否自动从request中填充值。 */ public boolean isRequestAware() { return requestAware == null ? false : requestAware; } /** 设置是否自动从request中填充值。 */ public void setRequestAware(boolean requestAware) { this.requestAware = requestAware; } public void setRequestAwareDefault(boolean requestAware) { if (this.requestAware == null) { this.requestAware = requestAware; } } /** 取得parent URI broker。 */ public URIBroker getParent() { return parent; } /** * 设置parent URI broker。 * <p> * 每次reset,当前URIBroker的状态将会恢复成和它的parent相同。 * </p> */ public void setParent(URIBroker parent) { assertTrue(!initialized, ILLEGAL_STATE, "already initialized"); if (parent != null) { this.parent = parent; } } /** * 取得URL encoding的编码字符集。 * <p> * 假如值为<code>null</code>,将从<code>LocaleUtil</code>中取得。 * </p> */ public String getCharset() { return charset; } /** 设置URL encoding的编码字符集。 */ public void setCharset(String charset) { this.charset = trimToNull(charset); } /** * 是否自动reset。 * <p> * URI broker是有状态的。假如自动reset设为<code>true</code>,则其状态在执行<code>render()</code> * 方法之后将恢复原状。 从配置文件中生成的uri broker,其autoReset为<code>false</code>,说明其状态不能被改变。 * </p> */ public boolean isAutoReset() { return autoReset; } /** 是否有interceptor? */ public boolean hasInterceptors() { return interceptors != null && !interceptors.isEmpty(); } /** 取得所有的interceptors及其状态。 */ protected Map<URIBrokerInterceptor, Integer> getInterceptorStates() { if (interceptors == null) { interceptors = createLinkedHashMap(); } return interceptors; } /** 取得所有的interceptors。 */ public Collection<URIBrokerInterceptor> getInterceptors() { return getInterceptorStates().keySet(); } /** 取得所有的interceptors。 */ public void setInterceptors(Collection<URIBrokerInterceptor> interceptors) { clearInterceptors(); for (URIBrokerInterceptor interceptor : interceptors) { addInterceptor(interceptor); } } /** 添加一个interceptor。 */ public void addInterceptor(URIBrokerInterceptor interceptor) { getInterceptorStates().put(assertNotNull(interceptor, "interceptor"), null); } /** 清除interceptors。 */ public void clearInterceptors() { if (interceptors != null) { interceptors.clear(); } } /** 初始化URI broker,将当前broker与parent合并。 */ public final void init() { if (initialized) { return; } initialized = true; if (parent != null) { // 确保parent已经初始化 parent.init(); // charset if (charset == null) { charset = parent.getCharset(); } // request aware if (requestAware == null) { requestAware = parent.isRequestAware(); } // init interceptors,将parent interceptors加在前面 if (parent.hasInterceptors()) { mergeLinkedHashMap(parent.getInterceptorStates(), getInterceptorStates()); } // init others initDefaults(parent); } renderer.prerender(); } /** 复制parent broker中的值作为默认值,但不覆盖当前broker中已有的值。 */ protected abstract void initDefaults(URIBroker parent); /** 复位到parent的状态。 */ public final void reset() { URIBroker parent = this.parent; if (parent == null) { parent = newInstanceInternal(); } // reset charset charset = parent.getCharset(); // reset interceptors clearInterceptors(); if (parent.hasInterceptors()) { for (Entry<URIBrokerInterceptor, Integer> entry : parent.getInterceptorStates().entrySet()) { getInterceptorStates().put(entry.getKey(), entry.getValue()); } } // reset others copyFrom(parent); // 复制renderer。 // 确保同一类型的broker,才复制预渲染buffer,因为不同的broker有可能渲染结果不同。 if (parent.getClass().equals(getClass())) { renderer.copyFrom(parent.renderer); } // read request HttpServletRequest realRequest = getRealRequest(); if (realRequest != null) { populateWithRequest(realRequest); } } /** * 复制parent的状态。 * <p> * 子类应该覆盖此方法,以实现特定的reset功能。 * </p> */ protected abstract void copyFrom(URIBroker parent); /** 将request中的运行时信息填充到uri broker中。 */ protected abstract void populateWithRequest(HttpServletRequest request); /** * 当以下条件成立时,返回真实的request对象。 * <ul> * <li>isRequestAware == true</li> * <li>request != null</li> * <li>处于web环境,request proxy可取得真实的request对象。</li> * </ul> */ protected final HttpServletRequest getRealRequest() { if (isRequestAware()) { return getProxyTarget(request); } return null; } /** * 以当前URI broker为模板, 创建一个新的URI broker。新的broker在执行<code>render()</code> * 方法后会自动复位。 * <p> * 此方法和<code>render()</code>具有相同的副作用,假如<code>autoReset == true</code> * ,那么所有状态自动复位。可应用于以下场景(velocity): * </p> * <p/> * <pre> * #set ($sub_uri = $uri.addPath("xxx/yyy").fork()) * * #foreach (...) * <a href="$sub_uri.addQueryData(...)">...</a> * #end * </pre> */ public final URIBroker fork() { return fork(true); } /** 以当前URI broker为模板, 创建一个新的URI broker。 */ public final URIBroker fork(boolean autoReset) { URIBroker parentBroker; URIBroker broker = null; // 确保当前broker不是autoReset的, 否则当前broker的reset会影响新生成的broker if (autoReset && isAutoReset()) { parentBroker = fork(false); parentBroker.renderer.prerender(); // 复位当前的broker, 就象执行过render一样 reset(); } else { parentBroker = (URIBroker) this; } // 生成新的基于parentBroker的broker // 确保新建的broker是非空,且为同一类型。 broker = newInstanceInternal(); ((URIBrokerFeatures) broker).autoReset = autoReset; broker.setRequestAware(parentBroker.isRequestAware()); broker.setRequest(((URIBrokerFeatures) parentBroker).request); broker.setParent(parentBroker); broker.reset(); return broker; } /** 创建一个新的空白broker。 */ protected final URIBroker newInstanceInternal() { URIBroker instance = assertNotNull(newInstance(), "%s.newInstance() returns null", getClass().getName()); assertTrue(instance != this, "%s.newInstance() returns itself", getClass().getName()); assertTrue(instance.getClass().equals(getClass()), "%s.newInstance() returns wrong type: %s", getClass() .getName(), instance.getClass().getName()); return instance; } /** 创建新的实例。 */ protected abstract URIBroker newInstance(); /** 按顺序执行所有interceptors。 */ protected void processInterceptors() { if (hasInterceptors()) { for (Map.Entry<URIBrokerInterceptor, Integer> entry : getInterceptorStates().entrySet()) { if (entry.getValue() == null) { entry.setValue(1); entry.getKey().perform((URIBroker) this); } } } } /** 渲染uri。 */ public final String render() { return render(autoReset); } private String render(boolean reset) { // 仅当reset时才执行interceptors,toString不执行。 if (reset) { processInterceptors(); } StringBuilder buf = new StringBuilder(); render(buf); if (reset) { reset(); } return buf.toString(); } protected abstract void render(StringBuilder buf); protected abstract void renderServer(StringBuilder buf); protected abstract void renderPath(StringBuilder buf); protected abstract void renderQuery(StringBuilder buf); /** 工具方法:进行URL编码,使用uribroker中指定的charset。 */ protected final String escapeURL(String str) { String charset = trimToNull(getCharset()); try { return StringEscapeUtil.escapeURL(str, charset); } catch (UnsupportedEncodingException e) { return StringEscapeUtil.escapeURL(str); } } /** 将parent map中的值插入到当前map的前面。 */ protected final <K, V> void mergeLinkedHashMap(Map<K, V> parentMap, Map<K, V> thisMap) { assertNotNull(thisMap, "thisMap"); Map<K, V> thisMapCopy = createLinkedHashMap(); thisMapCopy.putAll(thisMap); thisMap.clear(); if (parentMap != null) { thisMap.putAll(parentMap); } thisMap.putAll(thisMapCopy); } /** 取得URI字符串,但不reset。 */ @Override public String toString() { return render(false); } /** * 用来渲染URL、管理buffer的辅助类。 * <p> * 为了加快渲染URL的速度,在init的时候会进行预渲染,以填充<code>serverBuffer</code>、 * <code>pathBuffer</code>、<code>queryBuffer</code> * 等三个buffer。当fork的时候,直接将buffer复制给新的对象 * ,假如新的broker没有经过很大的修改就render的话,渲染速度会大大加快。 * </p> */ public final class Renderer { protected final StringBuilder serverBuffer = new StringBuilder(); protected final StringBuilder pathBuffer = new StringBuilder(); protected final StringBuilder queryBuffer = new StringBuilder(); public boolean isServerRendered() { return serverBuffer.length() > 0; } public boolean isPathRendered() { return pathBuffer.length() > 0; } public boolean isQueryRendered() { return queryBuffer.length() > 0; } public void clearServerBuffer() { serverBuffer.setLength(0); } public void clearPathBuffer() { pathBuffer.setLength(0); } public void updatePathBuffer(String path) { if (isPathRendered()) { pathBuffer.append("/").append(escapeURL(path)); } } public void truncatePathBuffer(int removedCount) { if (isPathRendered()) { int index = pathBuffer.length(); for (int i = 0; i < removedCount && index >= 0; i++) { index = pathBuffer.lastIndexOf("/", index - 1); } if (index >= 0) { pathBuffer.setLength(index); } else { pathBuffer.setLength(0); } } } public void clearQueryBuffer() { queryBuffer.setLength(0); } public void updateQueryBuffer(String id, String value) { if (isQueryRendered()) { queryBuffer.append("&").append(escapeURL(id)).append("=").append(escapeURL(value)); } } private void prerender() { if (!isServerRendered()) { renderServer(serverBuffer); } if (!isPathRendered()) { renderPath(pathBuffer); } if (!isQueryRendered()) { renderQuery(queryBuffer); } } private void copyFrom(Renderer parent) { // server info clearServerBuffer(); if (parent.isServerRendered()) { serverBuffer.append(parent.serverBuffer); } // path info clearPathBuffer(); if (parent.isPathRendered()) { pathBuffer.append(parent.pathBuffer); } // query info clearQueryBuffer(); if (parent.isQueryRendered()) { queryBuffer.append(parent.queryBuffer); } } } }