`
jlj008
  • 浏览: 96439 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Building struts2 app with out xml gluecode

阅读更多
本文转自TSS

http://www.theserverside.com/tt/articles/article.tss?l=SmartURLs

In this article, we jettison XML gluecode for "convention over configuration". Using the SmartURLs plugin for Struts 2, we can autowire Action classes to page templates with search-engine-optimized URIs.

This article covers:

    * Exploiting convention over configuration
    * Eliminating XML gluecode
    * Validating input with annotations

What's XML gluecode?

Success breeds success, and successful applications tend to grow larger and more complex over time. Most applications are so large and so complex that we can't think about the entire application at once. To get through the day, we need to focus our attention on one thing at time.

One way to simplify complex applications is to separate concerns. We group similar tasks together, so that we can focus on one element of the application at a time. Two concerns that we like to separate are presentation logic and business logic. It's easier to design presentation logic using a page template, and it's easier to design business logic using conventional source code. Each concern is easy to manage on its own, but we need a way to put the concerns back together.

In a conventional Struts application, we glue concerns together using declarative statements in an XML document. In practice, we write a lot of XML stanzas that just say: "For this request, run this Java class and then render this page template." "Example 1: struts.xml" shows a bit of XML gluecode.

"Example 1: struts.xml"

<action name="hello-world" class="actions.HelloWorld">
  <result>/results/hello-world.jsp</result>
</action>

At first, a dash of gluecode may seem trivial, but as applications grow in size, we can waste as much time fussing with gluecode as we spend on coding Action classes or writing JSP pages.

Nowadays, a popular way to avoid XML "gluecode" is to use "convention over configuration".
What's convention over configuration?

Instead of wiring components together with configuration files, another strategy is to use consistent naming conventions, and then make the conventions part of the application framework. Many Struts developers already use naming conventions to track which components go together. We just need to program the framework to observe the same kind of conventions that most of us already use.

For example, if a client requests a "hello-world.action", it makes sense for the framework to look for a "HelloWorld.class" and/or a "hello-world.jsp". If the Action class returns "small" as a result code, it makes sense to first look for a "hello-world-small.jsp" (or a "hello-world-small.vm", you prefer Velocity templates). If "hello-world-small.jsp" is not found, then it would also make sense to check for a plain-old "hello-world.jsp".

These matching rules may sound simplistic, but, in practice, a few simple rules is all that we need to write entire Struts applications without even a smidgeon of XML gluecode!

Should our conventions fall short, the SmartURLs plugin also provides annotations that we can use to override the matched action or result, on a case-by-case basis.
Does Struts 2 support convention over configuration by default?

The Struts 2 core offers a few features that can reduce configuration. But to get the full benefit of "convention over configuration", we need to add a new plugin to the mix.

The SmartURLs plugin for Struts 2 (http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html) offers a full-featured approach to convention over configuration. A request for "/my-action" is automatically mapped to a MyAction class and the result to a my-action.jsp page. To keep the system flexible, heuristic defaults look for alternative matches if MyAction class or my-action.jsp is not present. The matching rules are laid out in "Sidebar 1: SmartURLs Rules"

"Sidebar 1: SmartURLs rules"

URL to Action class (/my/package/hello-world):

    * A-1 Extract the final URL path segment (hello-world).
    * A-2 Upper-case the initial letter, and if any hyphen appears within the URL path, upper-case any following letter, and remove the hyphen (HelloWorld).
    * A-3 Convert the rest of the path to lowercase, and substitute slashes with dots (my.package.).
    * A-4 Check the base action packages for a class matching package + action (actions.my.package.HelloWorld).
    * A-5 If a matching Action is not found, use the package's default Action class (ActionSupport).

Result code to Path:

    * B-1 Append the result-code returned by an Action to original URL path (/my/package/hello-world-success).
    * B-2 Check the base result folders for a matching JSP, Freemarker Template, or Velocity Template (/WEB-INF/results/my/package/hello-world-success.jsp).
    * B-3 If the template is not found, remove the response code, and try again (/WEB-INF/result/my/package/hello-world.jsp).
    * B-4 Raise a standard 404 error is a matching template is not found.

SmartURLs provides features like:

    * Extensionless URIs
    * Automatic binding of action URLs to conventional class and page names
    * Action URI format that is "Search Engine Optimization" (SEO) compliant
    * Annotations to specify a different action name, or even multiple names
    * Automatic support of JSP, Freemarker, and Velocity result types
    * Robust "index" page handling (/products will match actions.Products or actions.products.Index)

How would we write a SmartURLs "Hello World" page?

Frameworks like Struts decompose an application's workflow into a series of actions. Each action might have associated with it several concerns, including input validation, business logic, persistence logic, message resources, text formatting, and an output resource. Each action has its own context, and which concerns are applied to any given action may vary according to circumstance.

The simplest use case for an action is rendering a page template without specifying any other concerns. (Just show me the @%#$^ page!) For our SmartURLs hello world example, let's try the simplest case first and then add in some of the other concerns. "Sidebar 2: Render page template" outlines the use case.

"Sidebar 2: Render page template"

System renders a page template without a custom Action class.

   1. Client requests an action resource.
   2. System determines there is no custom Action class configured for the resource.
   3. System renders page template for the resource using a default Action class.
   4. Client presents HTML version of the page template.

We can configure SmartURLs to expect page resources (JSP, Freemarker, or Velocity) to be found under "WEB-INF/results". This location is accessible to the Struts 2 framework, but it is not directly accessible to a web browser. The page resources cannot be accessed except through Struts 2 (or another server-side component).

Into the "WEB-INF/results" folder, we can drop a simple JSP template, as shown by "Example 2: hello-world.jsp".

"Example 2: hello-world.jsp"

<html>
<body>
  <p>
   Hello World!
  </p>
  <p>
   It is now <%= new java.util.Date() %>.
  </p>
</body>
</html>

With the SmartURLs plugin installed, we can fire up our web container and open the URL "http://localhost:8080/smartapp/hello-world". (The hostname and port for your container may vary!)

In response, SmartURLs will render the "WEB-INF/results/hello-world.jsp" template, as shown in "Figure 1: Hello World!"

"Figure 1: Hello World!"

If the page renders, we know that SmartURLs is working!

Already, SmartURLs lets us

    * use extensionless URIs,
    * store templates under the secure WEB-INF folder, and
    * do without "gluecode" XML

How would we display our own data on the page?

As handy as checking the server time may be, we usually want to merge our own data into a page template. Typically, we want to set a property on a Struts Action and then display that property in the page.

The value of the property might come from a database, and be filtered through a set of business rules, but the page doesn't need to know that. All the page needs to know is that the property is available, and it's the page's job to display the value, whatever it is. Conversely, the Action class doesn't need to know anything about HTML or expression languages. It just sets the property.

Right now, our concern is whether the Action class and page template are automatically wired together by the framework. So all we really need to do is set an Action property to a known value, and see if the page displays the value we expect.

To test SmartURL's autowiring, let's add a HelloWorld class to an "actions" package in our application, as show in "Example 3: HelloWorldAction.java".

"Example 3: HelloWorldAction.java"

package actions;
public class HelloWorldAction  {
  private String greeting;
  public String getGreeting() {
    return greeting;
  }
  public String execute() {
    greeting = "The server time is " + new java.util.Date().toString();
    return "success";
  }
}

Notice that this is a "POJO" Action class (a standard Struts 2 feature). We don't need any of the framework's special services, so we didn't extend a base class or implement a special interface. The closest thing to a special feature is the execute method! One other concession is that we need to append "Action" as a suffix to the classname.

Also note that we changed the message slightly, so that we can be sure that our page is updated. The updated page is shown in "Example 4: hello-world.jsp (2)"

"Example 4: hello-world.jsp (2)"

<html>
<body>
  <p>
   Hello World!
  </p>
  <p>
   ${greeting}
  </p>
</body>
</html>

Of course, we could use a Struts 2 tag instead of the JSTL expression, but <s:property value="greeting"/> seems verbose compared to "${greeting]". Figure 2: "Hello World! (2)" shows the updated page.

"Figure 2: Hello World! (2)"

Can SmartURLs handle a data-entry workflow with input validation?

A strength of action frameworks, like Struts 2, is that, depending on the runtime circumstances, an action can change the workflow by selecting different output pages. For example, we might want to collect input on one page. If the input is correct, we might want to go to an output page. If the input is not correct, we might want return to the input page.

Since validation workflow is one of the special services that the framework provides, in "Example 5: HelloWorld.java (2)", we extend our class from the ActionSupport class.

"Example 5: HelloWorld.java (2)"

package actions;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.*;
@Validation()
public class HelloWorld extends ActionSupport {
  private String greeting;
  @RequiredStringValidator(message="Please enter a greeting!")
  public String getGreeting() {
    return greeting;
  }
  public void setGreeting(String value) {
    greeting = value;
  }
}

Since we now extend the Action interface, the SmartURLs rules also lets us drop the "Action" suffix from the classname.

In Example 5, we use validation by annotation (another standard Struts 2 feature) to ensure that a message is entered. (Annotations, introduced in Java 5, are specified in the program source by using the @ symbol.) Struts 2 and the SmartURLs plugin use annotations as an alternative to XML gluecode. In Example 5, we use an annotation to "glue" a RequiredStringValidator to the message property. The core framework provides annotations for all the usual validators.

To collect an input message to validate, we can add another page template, as shown in "Example 6: hello-world-input.jsp".

"Example 6: hello-world-input.jsp"

<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<body>
  <p>
   What would you like to say to the world?
  </p>
  <s:form action="hello-world">
    <s:textfield label="Greeting" name="greeting" />
    <s:submit />
  </s:form>
</body>
</html>

"Figure 3: Hello World Input!" shows the page as it would be rendered.

"Figure 3: Hello World Input!"

If the message field is left blank, validation will fail, the HelloWorld Action will return an "input" result code, and SmartURLs will forward control back to "hello-world-input.jsp", as shown by "Figure 4: Hello World Input! (2)".

"Figure 4: Hello World Input! (2)"

If we manage to pass validation, a page renders like the one shown in "Example 5: Howdy".

"Figure 5: Hello World Input! (3)!"

Note that we can implement this common workflow without any XML gluecode! In this workflow, the only metadata of any kind is the RequiredStringValidator annotation. The rest is driven by convention over configuration!
What if we wanted to redirect after submitting a form?

If a form passes validation, and we use it to update a database, we often want to "redirect" to a confirmation page. A redirect updates the page location in the web browser's address bar. Among other things, a redirect ensures that people do not resubmit the form again by pressing refresh.

Struts 2 supports redirects through the @Result annotation. To redirect to another location on "success", we can add a "@Result" annotation to the Action class. "Example 7: HelloWorld.java (3)" shows the @Result annotation.

"Example 7: HelloWorld.java (3)"

@Result(name="success", type="redirect", location="hello-world-view")
public class HelloWorld extends ActionSupport

The annotation in Example 7 implies that there is a hello-world-view action. To create one, all we need to do is rename hello-world.jsp to hello-world-view.jsp.

On success, the system will redirect from the Hello Action to "hello-world-view.jsp", and display the greeting we entered through the "hello-world-input" action.

Or not.

By default, the Action properties are retained in request scope. If we redirect, we lose the first request scope and create a second. For now, the simplest solution is to keep our greeting in session scope. The complete, updated class is shown in "Example 8: HelloWorld.java (4)".

"Example 8: HelloWorld.java (4)"

package actions;
import java.util.Map;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.*;
import org.apache.struts2.interceptor.SessionAware;
import org.texturemedia.smarturls.Result;
@Validation()
@Result(name="success", type="redirect-action", location="hello-world-view")
public class HelloWorld extends ActionSupport implements SessionAware {
    private Map<String, String> session;
    public void setSession(Map value) {
        session = value;
    }
    protected Map<String, String> getSession() {
        Map<String, String> value = session;
        return value;
    }
    public static String GREETING_KEY = "greeting";
    @RequiredStringValidator(message="Please enter a greeting!")
    public String getGreeting() {
        return (String) getSession().get(GREETING_KEY);
    }
    public void setGreeting(String value) {
        getSession().put(GREETING_KEY,value);
    }
}

Now, to enter a greeting we can open "hello-world-input", which submits to "hello-world". If we successfully enter a greeting, "hello-world" stores the property in session scope, and redirects to "hello-world-view". To complete the loop, we can add a link back to the data-entry page. "Example 9: hello-world-view.jsp" shows the updated page.

"Example 9: hello-world-view.jsp"

<html>
<body>
  <p>
   Hello World!
  </p>
  <p>
   ${greeting}
  </p>
  <p>
  <a href="hello-world-input.do">Try again!</a>
  </p>
</body>
</html>

At this point, our complete Hello World data-entry workflow is shown by

    * "Example 6: hello-world-input.jsp",
    * "Example 7: HelloWorld.java (3)", and
    * "Example 4: hello-world-view.jsp"

without a single line of XML gluecode!
How do we install the SmartURLs plugin?

To install the plugin, first add the SmartURLs JAR to the web application's lib folder, along with the other Struts JARs. To get the most benefit from SmartURLs, we should make two small changes to the configuration. Among other things, these changes will let us use extensionless URLs.

Second, replace the standard Struts filter with the SmartURLs version. "Example web.xml" shows the SmartURLs <filter> and <filter-mapping> stanzas.

"Example 8: web.xml"

    <filter>
        <filter-name>
            struts2
        </filter-name>
        <filter-class>
            org.texturemedia.smarturls.SmartURLsFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>
            struts2
        </filter-name>
        <url-pattern>
            /*
        </url-pattern>
    </filter-mapping>

Third, in the application's struts.xml, add the "constant" stanza shown in "Example 9: struts.xml".

"Example 9: struts.xml"

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
    <constant name="struts.devMode" value="false" />
    <constant name="struts.action.extension" value="" />
    <constant name="smarturls.action.packages" value="actions" />
    <constant name="smarturls.base.result.location" value="/WEB-INF/results/" ></constant>
    <constant name="struts.custom.i18n.resources" value="support.package" ></constant>
</struts>

A larger application may need to define some other one-time settings or package defaults. But not gluecode!

What's the minimum installation we need for a SmartURLs application?

If you are using MyEclipse or Eclipse with Web Tools (WTP), you can create a Dynamic Web Application, and then

   1. Drop the usual Struts 2 JARs (freemarker, ognl, struts2-core, xwork2) under the standard "lib" folder, along with the SmartURLs plugin JARs (java-net-commons and smarturls-s2).
   2. Under "WebContent/WEB-INF", create a "results" folder for page templates.
   3. Under the "src" folder, create a "struts.xml" based on Example 9, and a "actions" package for Action classes.

And you are ready to go!
Is that all there is?

We've walked through the simplest way to use SmartURLs. We've shown the simplest approach that requires the fewest annotations. Aside from the annotations we've shown, SmartURLs supports several others. For example, we can give an Action multiple names, or call methods other than execute.

The SmartURLs documentation details the various annotations and configuration options. For more of SmartURLS in action, a complete SmartURLs MailReader example application is also available as a ready-to-deploy WAR (check Resources at the end of the article).

Using SmartURLs conventions, we can write less code and still create powerful, enterprise-ready applications. We can then put the time we save toward improving the user interface and other parts of the application that make it more useful.

Today, SmartURLs is a third-party plugin, but work is underway to merge it with the standard CodeBehind Plugin for Struts 2.1. The CodeBehind plugin for Struts 2.0 offers similar capabilities, but fewer features.
Resources

    * SmartURLs plugin site (http://code.google.com/p/smarturls-s2/)
    * SmartURLs MailReader Example for Java 5 (http://husted.com/smart-urls/smart-urls.war)
    * Apache Struts site (http://struts.apache.org)
分享到:
评论

相关推荐

    mmp2.rar_glue

    在这个上下文中,"Shared glue code"可能指的是用于协调128位块加密算法的AVX2优化部分与其他软件组件交互的代码,确保高效地整合各种功能。 4. **汇编宏(Assembler Macros)**:汇编语言中的宏是一种预处理指令,...

    camellia_aesni_avx2_glue.rar_glue

    《x86_64 AVX2 AES-NI Camellia Glue Code解析》 在IT领域,优化代码性能是一项至关重要的任务,特别是在密码学和加密算法中,速度与安全性并重。Camellia是一种高效的分组密码算法,而AES-NI是Intel处理器中内置的...

    外文翻译 stus MVC

    With Struts this is done with an Action class as a thin wrapper to the actual business logic. • Model state The model represents the state of the application. The business objects update the ...

    Screw locking glue standard

    标题“Screw locking glue standard”所指代的知识点涉及螺纹锁紧胶(粘合剂)的应用标准。该标题暗示了一个特定的工业标准,而标准是用来指导如何进行螺纹锁紧胶测试以确保其满足预紧扭矩要求的规程。 描述中提到...

    scheduler-eventhandler-cron:@ gluecode-itscheduler的cron事件处理程序

    `gluecode-itscheduler`是一个可能的开源项目,提供了一套强大的定时任务管理和执行框架。本文将深入探讨`scheduler-eventhandler-cron`,这是一个基于`gluecode-itscheduler`的特定事件处理器,它利用了Cron表达式...

    sha1_glue.rar_glue

    标题“sha1_glue.rar_glue”提示我们关注的是与SHA1算法相关的“胶水代码”(glue code)。在编程领域,胶水代码通常指的是用于连接不同编程语言、库或者模块的代码,使得它们能够协同工作。在这个场景中,SHA1的...

    Glue

    Glue在IT行业中通常指的是Amazon Glue,这是一个完全托管的数据集成服务,由亚马逊AWS提供。Amazon Glue帮助企业轻松地发现、编目、清理和转换数据,以便进行分析和机器学习。它是一个全托管的服务,无需预置或管理...

    GLUE数据集GLUE数据集

    GLUE(General Language Understanding Evaluation)数据集是自然语言处理领域的一个重要资源,旨在评估模型在理解和生成人类语言方面的性能。这个数据集包含了多种任务,涵盖了广泛的语言理解挑战,包括语义相似度...

    Laravel开发-glue

    "Laravel开发-glue" 提到的"glue"很可能是某个特定的扩展包或者工具,它是为了方便Laravel开发者更好地整合和管理项目中的组件。在Laravel生态中,经常有开发者创建自定义的包来解决特定问题或提供额外的功能,"glue...

    CSS 精灵生成工具 Glue.zip

    2. 下载并解压Glue的源码(如glue-master)。 3. 在命令行中进入Glue的源码目录,运行Python脚本,提供需要合并的图像文件夹路径和输出配置。 4. 检查生成的CSS文件和精灵图,将其引入到HTML页面中。 **优点与应用...

    Android代码-Glue

    Glue A lightweight view and resource injection library for android Usage gradle dependency Add below dependency to build.gradle repositories { jcenter() } dependencies { compile '...

    client-adapter.es7x-1.1.5-jar-with-dependencies、canal-glue-core

    client-adapter.es7x-1.1.5-jar-with-dependencies、canal-glue-core

    GLUE通用语言理解评估标准.rar

    GLUE(General Language Understanding Evaluation)是一项基准测试,旨在评估机器学习模型在自然语言处理任务中的性能,特别是理解和生成人类语言的能力。这个压缩包文件“GLUE通用语言理解评估标准.rar”包含了...

    Glue26_2016_glue262016ver_max_

    《3ds Max脚本插件Glue26_2016:提升3D建模与拼接效率的利器》 在3D建模领域,3ds Max是一款广泛使用的专业软件,其强大的功能和丰富的扩展性深受用户喜爱。在这款软件中,各种脚本和插件的使用可以极大地提高工作...

    Hands-On Game Development with WebAssembly Learn WebAssembly C++.epub

    Understand how Emscripten HTML shell templates, JavaScript glue code, and a WebAssembly module interact Debug and performance tune your WebAssembly application Who this book is for Web developers and ...

    swigwin-3.0.12.zip

    SWIG is typically used to parse C/C++ interfaces and generate the 'glue code' required for the above target languages to call into the C/C++ code. SWIG can also export its parse tree in the form of ...

    Glue常用类帮助文档_CN.doc

    2. addProperty方法:在运行时动态增加属性值。增加的属性将被保存在相应的Service Activity中,并在后续的Request中持续生效。因此,如果需要按Request单位来使用,应谨慎使用这个方法。 3. removeProperty方法:...

    GLUE.rar_GLUE MATLAB_glue方法_不确定_不确定分析_水文模型参数

    参数不确定性分析方法GLUE 来自王书功水文模型参数估计及不确定性分析

    Building Serverless Architectures-Packt Publishing(2017).pdf

    Lambda designed to run code pieces responding to external or internal cloud events, without needing to set up any infrastructure upfront, and it brought a real, managed, and pay-per-go infrastructure...

    cluster-glue-1.0.12

    cluster-glue_1.0.12文件内容: cluster-glue_1.0.12.orig.tar.bz2 cluster-glue_1.0.12~rc1+hg2777.orig.tar.bz2 cluster-glue-glue-1.0.12.tar.gz cluster-glue-glue-1.0.12.zip

Global site tag (gtag.js) - Google Analytics