Вот необходимые шаги:
- Результат создания пользовательских плиток, который динамически создает строку «местоположение» (строка местоположения — это значение, передаваемое плиткам), которое учитывает пространство имен, actionName.
- Создайте пакет, который использует этот результат (с именем «плитки»), и используйте соглашения в качестве родительского пакета.
- Внедрите и зарегистрируйте «com.opensymphony.xwork2.UnknownHandler», этот шаг является наиболее важным, поскольку этот обработчик вызывается, когда результат не может быть разрешен.
- Определения плиток, в которых используется «местоположение», переданное с первого шага.
Вышеуказанные шаги требуют следующего в struts.xml
<struts>
<constant name="struts.convention.default.parent.package" value="tiles-package"/>
<bean type="com.opensymphony.xwork2.UnknownHandler" name="tilesUnknownHandler" class="com.kenmcwilliams.tiles.result.TilesUnknownHandler"/>
<package name="tiles-package" extends="convention-default">
<result-types>
<result-type default="true" name="tiles" class="com.kenmcwilliams.tiles.result.TilesResult"/>
</result-types>
</package>
</struts>
Реализация пользовательского типа результата:
package com.kenmcwilliams.tiles.result;
import com.opensymphony.xwork2.ActionInvocation;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.servlet.ServletRequest;
import org.apache.tiles.request.servlet.ServletUtil;
public class TilesResult extends ServletDispatcherResult {
private static final Logger log = Logger.getLogger(TilesResult.class.getName());
public TilesResult() {
super();
}
public TilesResult(String location) {
super(location);
}
@Override
public void doExecute(String location, ActionInvocation invocation) throws Exception {
//location = "test.definition"; //for test
log.log(Level.INFO, "TilesResult doExecute() location: {0}", location);
//Start simple conventions
//
if (/** tiles && **/location == null) {
String namespace = invocation.getProxy().getNamespace();
String actionName = invocation.getProxy().getActionName();
location = namespace + "#" + actionName + ".jsp"; //Warning forcing extension
log.log(Level.INFO, "TilesResult namespace: {0}", namespace);
log.log(Level.INFO, "TilesResult actionName: {0}", actionName);
log.log(Level.INFO, "TilesResult location: {0}", location);
}
//End simple conventions
setLocation(location);
ServletContext context = ServletActionContext.getServletContext();
ApplicationContext applicationContext = ServletUtil.getApplicationContext(context);
TilesContainer container = TilesAccess.getContainer(applicationContext);
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ServletRequest servletRequest = new ServletRequest(applicationContext, request, response);
container.render(location, servletRequest);
}
}
Реализация TilesUnknownHandler:
package com.kenmcwilliams.tiles.result;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig.Builder;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import flexjson.JSONSerializer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.convention.ConventionUnknownHandler;
public class TilesUnknownHandler extends ConventionUnknownHandler {
private static final Logger log = Logger.getLogger(TilesUnknownHandler.class.getName());
private static final String conventionBase = "/WEB-INF/content";
@Inject
public TilesUnknownHandler(Configuration configuration, ObjectFactory objectFactory,
ServletContext servletContext, Container container,
@Inject("struts.convention.default.parent.package") String defaultParentPackageName,
@Inject("struts.convention.redirect.to.slash") String redirectToSlash,
@Inject("struts.convention.action.name.separator") String nameSeparator) {
super(configuration, objectFactory, servletContext, container, defaultParentPackageName,
redirectToSlash, nameSeparator);
log.info("Constructed TilesUnknownHandler");
}
@Override
public ActionConfig handleUnknownAction(String namespace, String actionName)
throws XWorkException {
ActionConfig actionConfig;
log.info("TilesUnknownHandler: before handleUnknownAction");
ActionConfig handleUnknownAction = super.handleUnknownAction(namespace, actionName);
log.info("TilesUnknownHandler: after handleUnknownAction, returning with:");
log.log(Level.INFO, "...ActionConfig value: {0}", (new JSONSerializer().serialize(handleUnknownAction)));
log.log(Level.INFO, "Modifying handleUnknowAction result handler");
Map<String, ResultConfig> results = handleUnknownAction.getResults();
ResultConfig resultConfig = results.get("success");
Builder builder = new ResultConfig.Builder("com.opensymphony.xwork2.config.entities.ResultConfig", "com.kenmcwilliams.tiles.result.TilesResult");
Map<String, String> params = resultConfig.getParams();
String tilesResultString = null;
String location = params.get("location");
if (location != null && !location.isEmpty()) {
int length = conventionBase.length();
if(StringUtils.startsWith(location, conventionBase)){
String subString = location.substring(length); //chop off "/WEB-INF/content"
int count = StringUtils.countMatches(subString, "/");//TODO: maybe check for "//", although I don't know why it would be in the string
if (count == 1){//empty namespace
tilesResultString = subString.replaceFirst("/", "#"); //TODO: because I am doing a straight replacement of the last element the else can probably be removed
}else{ //replace the last slash between the namespace and the file with "#"
int lastIndex = subString.lastIndexOf("/");
//subString.substring(lastIndex, lastIndex);
String nameSpace = subString.substring(0, lastIndex);
String file = subString.substring(lastIndex + 1);
tilesResultString = nameSpace + "#" + file;
}
}
}
Map<String, String> myParams = new LinkedHashMap<String, String>();
myParams.put("location", tilesResultString);
builder.addParams(myParams);
ResultConfig build = builder.build();
Map<String, ResultConfig> myMap = new LinkedHashMap<String, ResultConfig>();
myMap.put("success", build);
log.log(Level.INFO, "\n\n...results: {0}\n\n", (new JSONSerializer().serialize(results)));
actionConfig = new ActionConfig.Builder(handleUnknownAction).addResultConfigs(myMap).build();
//className("com.kenmcwilliams.tiles.result.TilesResult")
return actionConfig;
}
@Override
public Result handleUnknownResult(ActionContext actionContext, String actionName,
ActionConfig actionConfig, String resultCode) throws XWorkException {
log.info("TilesUnknownHandler: before handleUnknownResult");
Result handleUnknownResult = super.handleUnknownResult(actionContext, actionName, actionConfig, resultCode);
log.info("TilesUnknownHandler: after handleUnknownResult, returning with:");
log.log(Level.INFO, "...Result value: {0}", (new JSONSerializer().serialize(handleUnknownResult)));
return handleUnknownResult;
}
}
Пример того, как использовать нашу строку «местоположение», которая имеет форму: NameSpace + «#» + ActionName + «.jsp», обратите внимание на это определение <definition name="REGEXP:(.*)#(.*)" extends="default">
в следующем:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="default" template="/WEB-INF/template/template.jsp">
<put-list-attribute name="cssList" cascade="true">
<add-attribute value="/style/cssreset-min.css" />
<add-attribute value="/style/cssfonts-min.css" />
<add-attribute value="/style/cssbase-min.css" />
<add-attribute value="/style/grids-min.css" />
<add-attribute value="/script/jquery-ui-1.8.24.custom/css/ui-lightness/jquery-ui-1.8.24.custom.css" />
<add-attribute value="/style/style.css" />
</put-list-attribute>
<put-list-attribute name="jsList" cascade="true">
<add-attribute value="/script/jquery/1.8.1/jquery.min.js" />
<add-attribute value="/script/jquery-ui-1.8.24.custom/js/jquery-ui-1.8.24.custom.min.js" />
<add-attribute value="/script/jquery.sort.js" />
<add-attribute value="/script/custom/jquery-serialize.js" />
</put-list-attribute>
<put-attribute name="title" value="defaults-name" cascade="true" type="string"/>
<put-attribute name="head" value="/WEB-INF/template/head.jsp"/>
<put-attribute name="header" value="/WEB-INF/template/header.jsp"/>
<put-attribute name="body" value="/WEB-INF/template/body.jsp"/>
<put-attribute name="footer" value="/WEB-INF/template/footer.jsp"/>
</definition>
<definition name="REGEXP:(.*)#(.*)" extends="default">
<put-attribute name="title" cascade="true" expression="OGNL:@com.opensymphony.xwork2.ActionContext@getContext().name"/>
<put-attribute name="body" value="/WEB-INF/content{1}/{2}"/>
</definition>
</tiles-definitions>
С этим вы можете создавать JSP в /WEB-INF/content/someplace/my-action.jsp.
Точно так же, как и в случае с соглашениями, И плитки также украсят его соответствующим образом, если вы создадите класс действия с именем com.myapp.action.someplace.MyAction
без какого-либо типа результата, этот код будет выполняться, и результат /WEB-INF/content/someplace/my-action.jsp
все равно будет отображаться.
Там у вас есть условности + плитки без аннотаций (ну для нормального случая).
ПРИМЕЧАНИЯ:
- Этот ответ, конечно, не идеален, но он представляет собой рабочий пример стратегии, которую можно применить к другим технологиям просмотра (сетка сайта и другие).
- В настоящее время вы можете видеть, что «.jsp» добавляется к результату плитки, а НЕ к определениям плитки, это негибко. Конкретное расширение должно быть указано внутри плиток, то есть атрибут body в определении должен добавлять конкретный тип представления (.jsp, .fml, .vm), потому что в то время вы должны знать лучше.
- Важно отметить, что определения пробуются в том порядке, в котором они даны, поэтому вы можете переопределить обычный случай
REGEXP:(.*)#(.*)
, поместив определения между определениями default
и REGEXP:(.*)#(.*)
. Например, определение под названием authenticated\(.*)
может быть помещено между этими двумя определениями. В конце концов, если бы вы не могли этого сделать и все страницы должны были быть выложены одинаковыми плитками, мы бы действительно не использовали плитки!
- Просто чтобы вы знали, что при использовании плитки 3 (плагин struts2tiles3) вы можете использовать все три типа технологий просмотра (jsp, freemarker, speed) для создания одной плитки. Оно работает. Вероятно, вы будете постоянно использовать одну технологию просмотра, но приятно знать, что это возможно.
person
Quaternion
schedule
20.04.2013