/*
* Copyright 2008-2009 the original author or authors.
*
* 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 net.hasor.web.render;
import net.hasor.core.AppContext;
import net.hasor.core.Settings;
import net.hasor.core.utils.StringUtils;
import net.hasor.web.*;
import net.hasor.web.annotation.Produces;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 渲染器插件。
* @version : 2017-01-10
* @author 赵永春 (zyc@hasor.net)
*/
public class RenderWebPlugin extends WebModule implements WebPlugin, InvokerFilter {
protected Logger logger = LoggerFactory.getLogger(getClass());
private AtomicBoolean inited = new AtomicBoolean(false);
private String layoutPath = null; // 布局模版位置
private boolean useLayout = true;
private String templatePath = null; // 页面模版位置
private Map<String, RenderEngine> engineMap;
//
@Override
public void loadModule(WebApiBinder apiBinder) throws Throwable {
apiBinder.addPlugin(this);
apiBinder.filter("/*").through(Integer.MAX_VALUE, this);
}
//
@Override
public void init(InvokerConfig config) throws Throwable {
if (!this.inited.compareAndSet(false, true)) {
return;
}
//
AppContext appContext = config.getAppContext();
Map<String, RenderEngine> engineMap = new HashMap<String, RenderEngine>();
Map<String, String> renderMapping = new HashMap<String, String>();
List<RenderDefinition> renderInfoList = appContext.findBindingBean(RenderDefinition.class);
for (RenderDefinition renderInfo : renderInfoList) {
if (renderInfo == null) {
continue;
}
logger.info("web -> renderType {} mappingTo {}.", StringUtils.join(renderInfo.getRenderSet().toArray(), ","), renderInfo.toString());
String renderInfoID = renderInfo.getID();
engineMap.put(renderInfoID, renderInfo.newEngine(appContext));
//
List<String> renderSet = renderInfo.getRenderSet();
for (String renderName : renderSet) {
renderMapping.put(renderName.toUpperCase(), renderInfoID);
}
}
//
this.engineMap = new HashMap<String, RenderEngine>();
for (String key : renderMapping.keySet()) {
//
String keyMapping = renderMapping.get(key);
RenderEngine engine = engineMap.get(keyMapping);
this.engineMap.put(key, engine);
}
//
Settings settings = appContext.getEnvironment().getSettings();
this.useLayout = settings.getBoolean("hasor.layout.enable", true);
this.layoutPath = settings.getString("hasor.layout.layoutPath", "/layout");
this.templatePath = settings.getString("hasor.layout.templatePath", "/templates");
}
//
@Override
public void destroy() {
}
//
@Override
public void beforeFilter(Invoker invoker, InvokerData info) {
if (!(invoker instanceof RenderInvoker)) {
return;
}
//
RenderInvoker render = (RenderInvoker) invoker;
HttpServletResponse htttResponse = render.getHttpResponse();
Method targetMethod = info.targetMethod();
if (targetMethod.isAnnotationPresent(Produces.class)) {
Produces pro = targetMethod.getAnnotation(Produces.class);
String proValue = pro.value();
if (!StringUtils.isBlank(proValue)) {
render.viewType(proValue);
String mimeType = invoker.getMimeType(proValue);
if (StringUtils.isBlank(mimeType)) {
htttResponse.setContentType(proValue);//用原始配置
} else {
htttResponse.setContentType(mimeType);//用定义的配置
}
}
}
}
@Override
public void doInvoke(Invoker invoker, InvokerChain chain) throws Throwable {
// .执行过滤器
chain.doNext(invoker);
//
// .处理渲染
if (invoker instanceof RenderInvoker) {
boolean process = this.process((RenderInvoker) invoker);
if (process) {
return;
}
RenderInvoker renderInvoker = (RenderInvoker) invoker;
HttpServletRequest httpRequest = renderInvoker.getHttpRequest();
HttpServletResponse httpResponse = renderInvoker.getHttpResponse();
httpRequest.getRequestDispatcher(renderInvoker.renderTo()).forward(httpRequest, httpResponse);
}
}
@Override
public void afterFilter(Invoker invoker, InvokerData info) {
}
//
public boolean process(RenderInvoker render) throws Throwable {
if (render == null) {
return false;
}
String type = render.viewType();
RenderEngine engine = this.engineMap.get(type);
if (engine == null) {
return false;
}
if (render.getHttpResponse().isCommitted()) {
return false;
}
//
String oriViewName = render.renderTo();
if (this.useLayout) {
render.renderTo(fixTempName(this.templatePath, oriViewName));
}
//
String layoutFile = null;
if (this.useLayout && render.layout()) {
layoutFile = findLayout(engine, oriViewName);
}
//
if (layoutFile != null) {
//先执行目标页面,然后在渲染layout
StringWriter tmpWriter = new StringWriter();
if (engine.exist(render.renderTo())) {
engine.process(render, tmpWriter);
} else {
return false;
}
//渲染layout
render.put("content_placeholder", tmpWriter.toString());
render.renderTo(layoutFile);
if (engine.exist(render.renderTo())) {
engine.process(render, render.getHttpResponse().getWriter());
return true;
} else {
throw new IOException("layout '" + layoutFile + "' file is missing.");//不可能发生这个错误。
}
} else {
if (engine.exist(render.renderTo())) {
engine.process(render, render.getHttpResponse().getWriter());
return true;
} else {
return false;//没有执行模版
}
}
//
}
//
protected String findLayout(RenderEngine engine, String tempFile) throws IOException {
if (engine == null) {
return null;
}
File layoutFile = new File(this.layoutPath, tempFile);
if (engine.exist(layoutFile.getPath())) {
return layoutFile.getPath();
} else {
layoutFile = new File(layoutFile.getParent(), "default.htm");
if (engine.exist(layoutFile.getPath())) {
return layoutFile.getPath();
} else {
while (layoutFile.getPath().startsWith(this.layoutPath)) {
layoutFile = new File(layoutFile.getParentFile().getParent(), "default.htm");
if (engine.exist(layoutFile.getPath())) {
return layoutFile.getPath();
}
}
}
}
return null;
}
private String fixTempName(String templatePath, String tempName) {
if (tempName.charAt(0) != '/') {
return templatePath + "/" + tempName;
} else {
return templatePath + tempName;
}
}
}