Project Student:维护Webapp(只读)

这是Project Student的一部分。 其他职位包括带有Jersey的 Web服务 客户端,带有Jersey的 Web服务服务器 , 业务层 , 具有Spring数据的持久性 ,分片集成测试数据 , Webservice集成和JPA标准查询 。

当我开始这个项目时,我有四个目标。 他们没有特别的次序:

  • 了解jQuery和其他AJAX技术。 为此,我需要一个了解的REST服务器,
  • 捕获最近获得的有关球衣和挂毯的知识,
  • 创建一个我可以用来了解其他技术(例如spring MVC,restlet,netty)的框架,以及
  • 在工作面试中有什么要讨论的

如果对其他人有用–太好了! 这就是为什么它在Apache许可下可用。

(不言而喻,可接受的用途不包括在没有适当归因的情况下将“ Project Student”变成学生项目!)

学习AJAX的问题在于,我一开始会不确定问题的根源。 jQuery不好吗? 不良的REST服务? 还有吗 广泛的单元和集成测试是一个好的开始,但始终会存在一些不确定性。

另一个考虑因素是,在现实世界中,我们经常需要对数据库的基本视图。 它不会供公众使用,而是当我们遇到WTF时刻时供内部使用。 它也可以用于维护我们不想通过公共界面管理的信息,例如下拉菜单中的值。

对此可以稍作改动,以提供适度的可伸缩性。 将大型服务器用于数据库和REST服务,然后让N台前端服务器运行常规的Web应用程序,充当用户和REST服务之间的中介。 前端服务器可以是相当轻量的,并且可以根据需要旋转。 将缓存服务器放置在前端和REST服务器之间的加分点,因为将读取压倒性的点击量。

这种方法无法扩展到Amazon或Facebook的规模,但是对于许多站点来说已经足够了。

维护Webapp

这将我们带到了webapp onion的可选层上-传统的webapp充当REST服务的前端。 由于各种原因,我在应用程序中使用Tapestry 5 ,但这是一个任意决定,我不会花很多时间研究Tapestry特定的代码。

您可以使用创建新的挂毯项目

$ mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org

我已经在http://jumpstart.doublenegative.com.au/jumpstart/examples/找到了有价值的示例。 在适当的地方,我会注明出处。

稍后,我还将创建webapp的第二个可选层-使用Selenium和WebDriver (即Selenium 2.0)进行功能和回归测试。

局限性

只读 –分解webapp需要大量工作,因此初始版本将仅提供对简单表的只读访问。 没有更新,没有一对多映射。

用户身份验证 –尚未进行身份验证的工作。

加密 –尚未对通信进行加密。

数据库锁 –我们在休眠版本中使用机会锁定,而不是显式数据库锁定。 安全说明:根据最少公开的原则,除非需要,否则我们不想使该版本可见。 一个好的规则是,您将看到它是否请求一个特定的对象,但看不到列表中的对象。

REST客户端 -每种类型的默认GET处理程序非常粗糙-它仅返回对象列表。 我们需要更复杂的响应(例如,记录数,起始索引和结束索引,状态码等),并将让UI驱动它。 现在,我们真正需要的只是一个计数,我们可以只请求列表并计数元素的数量。

目标

我们需要一个列出数据库中所有课程的页面。 它不必担心分页,排序等问题。它应该具有用于​​编辑和删除记录的链接(可能是无效的)。 它不需要添加新课程的链接。

该页面应如下所示:

项目维护

课程模板

Tapestry页面上列出的课程很简单–基本上只是修饰的值网格

(有关Layout.tml等信息,请参见挂毯原型。)

<html t:type="layout" title="Course List"t:sidebarTitle="Framework Version"xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"xmlns:p="tapestry:parameter"><!-- Most of the page content, including <head>, <body>, etc. tags, comes from Layout.tml --><t:zone t:id="zone">   <p>"Course" page</p><t:grid source="courses" row="course" include="uuid,name,creationdate" add="edit,delete"><p:name><t:pagelink page="CourseEditor" context="course.uuid">${course.name}</t:pagelink></p:name><p:editcell><t:actionlink t:id="edit" context="course.uuid">Edit</t:actionlink></p:editcell><p:deletecell><t:actionlink t:id="delete" context="course.uuid">Delete</t:actionlink></p:deletecell><p:empty><p>There are no courses to display; you can <t:pagelink page="Course/Editor" parameters="{ 'mode':'create', 'courseUuid':null }">add some</t:pagelink>.</p></p:empty></t:grid></t:zone><p:sidebar><p>[<t:pagelink page="Index">Index</t:pagelink>]<br/>[<t:pagelink page="Course/List">Courses</t:pagelink>]</p></p:sidebar>
</html>

具有的属性文件

title=Courses
delete-course=Delete course?

我已经包括了用于编辑和删除的操作链接,但是它们不起作用。

GridDataSources

我们的页面需要显示值的来源。 这需要两个类。 第一个定义了上面使用的课程属性。

package com.invariantproperties.sandbox.student.maintenance.web.tables;import com.invariantproperties.sandbox.student.business.CourseFinderService;public class GridDataSources {// Screen fields@Propertyprivate GridDataSource courses;// Generally useful bits and pieces@Injectprivate CourseFinderService courseFinderService;@InjectComponentprivate Grid grid;// The codevoid setupRender() {courses = new CoursePagedDataSource(courseFinderService);}
}

实际的实现是

package com.invariantproperties.sandbox.student.maintenance.web.tables;import com.invariantproperties.sandbox.student.business.CourseFinderService;
import com.invariantproperties.sandbox.student.domain.Course;
import com.invariantproperties.sandbox.student.maintenance.query.SortCriterion;
import com.invariantproperties.sandbox.student.maintenance.query.SortDirection;public class CoursePagedDataSource implements GridDataSource {private int startIndex;private List<Course> preparedResults;private final CourseFinderService courseFinderService;public CoursePagedDataSource(CourseFinderService courseFinderService) {this.courseFinderService = courseFinderService;}@Overridepublic int getAvailableRows() {long count = courseFinderService.count();return (int) count;}@Overridepublic void prepare(final int startIndex, final int endIndex, final List<SortConstraint> sortConstraints) {// Get a page of courses - ask business service to find them (from the// database)// List<SortCriterion> sortCriteria = toSortCriteria(sortConstraints);// preparedResults = courseFinderService.findCourses(startIndex,// endIndex - startIndex + 1, sortCriteria);preparedResults = courseFinderService.findAllCourses();this.startIndex = startIndex;}@Overridepublic Object getRowValue(final int index) {return preparedResults.get(index - startIndex);}@Overridepublic Class<Course> getRowType() {return Course.class;}/*** Converts a list of Tapestry's SortConstraint to a list of our business* tier's SortCriterion. The business tier does not use SortConstraint* because that would create a dependency on Tapestry.*/private List<SortCriterion> toSortCriteria(List<SortConstraint> sortConstraints) {List<SortCriterion> sortCriteria = new ArrayList<>();for (SortConstraint sortConstraint : sortConstraints) {String propertyName = sortConstraint.getPropertyModel().getPropertyName();SortDirection sortDirection = SortDirection.UNSORTED;switch (sortConstraint.getColumnSort()) {case ASCENDING:sortDirection = SortDirection.ASCENDING;break;case DESCENDING:sortDirection = SortDirection.DESCENDING;break;default:}SortCriterion sortCriterion = new SortCriterion(propertyName, sortDirection);sortCriteria.add(sortCriterion);}return sortCriteria;}
}

应用模块

现在有了GridDataSource,我们可以看到它的需求– CourseFinderService。 虽然有Tapestry-Spring集成,但我们希望保持维护Webapp尽可能的薄,所以现在我们使用标准的Tapestry注入。

package com.invariantproperties.sandbox.student.maintenance.web.services;import com.invariantproperties.sandbox.student.business.CourseFinderService;
import com.invariantproperties.sandbox.student.business.CourseManagerService;
import com.invariantproperties.sandbox.student.maintenance.service.impl.CourseFinderServiceTapestryImpl;
import com.invariantproperties.sandbox.student.maintenance.service.impl.CourseManagerServiceTapestryImpl;/*** This module is automatically included as part of the Tapestry IoC Registry,* it's a good place to configure and extend Tapestry, or to place your own* service definitions.*/
public class AppModule {public static void bind(ServiceBinder binder) {binder.bind(CourseFinderService.class, CourseFinderServiceTapestryImpl.class);binder.bind(CourseManagerService.class, CourseManagerServiceTapestryImpl.class);}....
}

请注意,我们正在使用标准的CourseFinderService接口以及特定于挂毯的实现。 这意味着我们可以直接使用标准实现,只需要对配置文件进行一点改动即可!

CourseFinderServiceTapestryImpl

CourseFinderService接口的本地实现必须使用REST客户端而不是Spring Data实现。 使用早期使用的由外而内的方法,Tapestry模板的需求应推动服务实现的需求,进而驱动REST客户端和服务器的需求。

package com.invariantproperties.sandbox.student.maintenance.service.impl;public class CourseFinderServiceTapestryImpl implements CourseFinderService {private final CourseFinderRestClient finder;public CourseFinderServiceTapestryImpl() {// resource should be loaded as tapestry resourcefinal String resource = "http://localhost:8080/student-ws-webapp/rest/course/";finder = new CourseFinderRestClientImpl(resource);// load some initial datainitCache(new CourseManagerRestClientImpl(resource));}@Overridepublic long count() {// FIXME: grossly inefficient but good enough for now.return finder.getAllCourses().length;}@Overridepublic long countByTestRun(TestRun testRun) {// FIXME: grossly inefficient but good enough for now.return finder.getAllCourses().length;}@Overridepublic Course findCourseById(Integer id) {// unsupported operation!throw new ObjectNotFoundException(id);}@Overridepublic Course findCourseByUuid(String uuid) {return finder.getCourse(uuid);}@Overridepublic List<Course> findAllCourses() {return Arrays.asList(finder.getAllCourses());}@Overridepublic List<Course> findCoursesByTestRun(TestRun testRun) {return Collections.emptyList();}// method to load some test data into the database.private void initCache(CourseManagerRestClient manager) {manager.createCourse("physics 101");manager.createCourse("physics 201");manager.createCourse("physics 202");}
}

我们的JPA Criteria查询可以快速计数,但是我们的REST客户端尚不支持。

包起来

完成艰苦的工作后,我们将获得一个维护文件.war。 我们可以使用webservice .war将其部署在我们的应用服务器上,也可以不部署。 除了Web服务的临时硬编码URL外,这两个.war文件没有理由必须位于同一系统上。

我们首先应该转到http:// localhost:8080 / student-maintenance-webapp / course / list。 我们应该看到如上所示的简短课程清单。 (在那种情况下,我已经重新启动了webapp三次,因此每个条目都会重复三倍。)

现在,我们应该访问位于http:// localhost:8080 / student-ws-webapp / rest / course的webservice webapp,并验证我们是否也可以通过浏览器获取数据。 经过一点清理后,我们应该看到:

{"course":[{"creationDate":"2013-12-28T14:40:21.369-07:00","uuid":"500069e4-444d-49bc-80f0-4894c2d13f6a","version":"0","name":"physics 101"},{"creationDate":"2013-12-28T14:40:21.777-07:00","uuid":"54001b2a-abbb-4a75-a289-e1f09173fa04","version":"0","name":"physics 201"},{"creationDate":"2013-12-28T14:40:21.938-07:00","uuid":"cfaf892b-7ead-4d64-8659-8f87756bed62","version":"0","name":"physics 202"},{"creationDate":"2013-12-28T16:17:54.608-07:00","uuid":"d29735ff-f614-4979-a0de-e1d134e859f4","version":"0","name":"physics 101"},....]
}

源代码

  • 源代码位于https://github.com/beargiles/project-student [github]和http://beargiles.github.io/project-student/ [github页面]。

参考: 项目学生: Invariant Properties博客上来自JCG合作伙伴 Bear Giles的Maintenance Webapp(只读) 。

翻译自: https://www.javacodegeeks.com/2014/01/project-student-maintenance-webapp-read-only.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364672.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于vue的无缝滚动组件

vue-seamless-scroll A simple, Seamless scrolling for Vue.js 在awesome上一直没有发现vue的无缝滚动组件&#xff0c;在工作之余写了个组件&#xff0c;分享出来希望大家一起学习进步。Demo https://github.com/chenxuan0000/vue-seamless-scroll/index.html Installatio…

教务管理及教材订购系统设计文档

教务管理及教材订购系统设计文档目录 一、概述 1.1 开发背景 1.2 使用技术 1.3运行环境 1.4 设计目标 1.4.1权限管理 1.4.2信息管理 1.4.3选课管理 1.4.4 成绩管理 1.4.5教材订购 二、功能分析划分 2.1信息管理 2.1.1班级信息管理 2.1.2专业信息管理 2.1.3课程信息管理 2.1.4学…

关于构造器中的super()

1、为什么在子类的constructor里面要加一句super&#xff08;&#xff09;&#xff1f; 答&#xff1a;如果子类用了extends的关键字继承的父类&#xff0c;那么子类在使用构造器的时候就要加super&#xff08;&#xff09;语句&#xff0c;这是语法规范&#xff0c;就是这么定…

php 复制行,phpstorm怎么快速复制当前行?

qq_花开花谢_0PhpStorm 默认快捷键ctrlj 插入活动代码提示ctrlaltt 当前位置插入环绕代码altinsert 生成代码菜单Shift Enter 新一行ctrlq 查看代码注释ctrld 复制当前行ctrly 删除当前行ctrlalty 刷新项目缓…

Project Student:维护Webapp(可编辑)

这是Project Student的一部分。 其他帖子包括带有Jersey的 Web服务 客户端&#xff0c;带有Jersey的 Web服务服务器 &#xff0c; 业务层 &#xff0c; 具有Spring数据的持久性 &#xff0c;分片集成测试数据 &#xff0c; Webservice集成 &#xff0c; JPA标准查询和维护Webap…

express接受get数据

server.use(/,function(req,res){res.writeHead(200, { content-type:text/html;charsetutf-8});var namereq.query[name]; //直接req.query[参数名称]&#xff1b;var pwdreq.query[pwd];if(!users[name]){res.write("不存在该用户");}else if(users[name]!pwd){re…

php curl 采集文件,curl获取远程文件内容

/**获取远程文件内容param $url 文件http地址*/function fopen_url($url){if (function_exists(file_get_contents)) {$file_content file_get_contents($url);} elseif (ini_get(allow_url_fopen) && ($file fopen($url, rb))){$i 0;while (!feof($file) &&…

SAP work process Memory allocate

SAP work process Memory allocate Memory allocation sequence to dialog work processes in SAP What is the memory allocation sequence to dialog work processes in SAP?When does a work process go to PRIV mode?How to avoid or minimize work process going to PRI…

基于 Vue.js 的移动端组件库mint-ui实现无限滚动加载更多

通过多次爬坑&#xff0c;发现了这些监听滚动来加载更多的组件的共同点&#xff0c; 因为这些加载更多的方法是绑定在需要加载更多的内容的元素上的&#xff0c; 所以是进入页面则直接触发一次&#xff0c;当监听到滚动事件之后&#xff0c;继续加载更多&#xff0c; 所以对…

创建Maven源代码和Javadoc工件

许多人都知道Maven源代码和Javadoc工件&#xff0c;但是不知道为什么要创建它们。 我肯定在这个阵营中–我可以理解为什么人们想要此信息&#xff0c;但是由于要手动导航Maven存储库&#xff0c;因此获取信息似乎相对效率较低。 然后我被线索棒击中。 这些工件由IDE而非人员使…

JS内置方法(object)

属性 constructorprototype 实例方法 1、toString()返回当前对象的字符串形式&#xff0c;返回值为String类型。 2、toLocaleString()返回当前对象的"本地化"字符串形式&#xff0c;以便于当前环境的用户辨识和使用&#xff0c;返回值为String类型。 3、valueOf()返回…

金蝶云php webapi,K/3 Cloud Web API销售出库单PHP完整示例【分享】

按照惯例&#xff0c;先上图【销售出库单】保存&#xff0c;如图&#xff1a;已经打印出 登陆请求及登陆成功&#xff0c;保存请求及保存成功的返回信息。如下代码&#xff0c;是完全可以直接进行运行的代码&#xff0c;具体详见代码中注释。[code]//K/3 Cloud 业务站点地址$cl…

JavaFX自定义控件– Nest Thermostat第2部分

自从我开始创建Nest恒温器FX自定义控件以来&#xff0c;已经有一段时间了&#xff01; 因此&#xff0c;上次&#xff0c;如Gerrit Grunwald所建议&#xff0c;我花了一些时间用inkscape复制Nest恒温器设计&#xff0c;这是构建JavaFX版本的第一步。 今天&#xff0c;我想与大…

函数和模块的使用

函数&#xff1a; 函数作用&#xff1a; 减少代码重复 增加程序可扩展性 使程序易于维护 函数定义&#xff1a; 关键字&#xff1a;def 名称&#xff1a;与变量名命名规则相同 参数&#xff1a; def fun() #无参数 def fun(x) #普通参数 def fun(name, age22, happyalex) #默…

关于 Error: No PostCSS Config found in 的错误

问题描述&#xff1a; 项目在本地运行不报错&#xff0c;上传到 GitHub 之后&#xff0c;再 clone 到本地&#xff0c; npm install安装完成之后再执行 npm run dev这时报错 Error: No PostCSS Config found in... 本以为是 GitHub 上传的问题&#xff0c;后开又试了两回&am…

haproxy实现会话保持

HAProxy系列文章&#xff1a;http://www.cnblogs.com/f-ck-need-u/p/7576137.html 1.反向代理为什么需要设置cookie 任何一个七层的http负载均衡器&#xff0c;都应该具备一个功能&#xff1a;会话保持。会话保持是保证客户端对动态应用程序正确请求的基本要求。 还是那个被举烂…

java dubbo 方案,Missing artifact com.alibaba:dubbo:jar:2.8.4 dubbo解决方案

由于maven中心仓库中没有dubbo2.8.4&#xff0c;所以需要到github中下载源码包自己编译。下载解压后&#xff0c;进入解压目录执行命令&#xff1a;mvn install -Dmaven.test.skiptrue2.mvn install:install-file -Dfiled:\xxx\dubbo-2.8.4.jar -DgroupIdcom.alibaba -Dartifac…

Java 8:Lambda表达式与自动关闭

如果您通过Neo4j的Java API和Java 6使用了Neo4j的早期版本&#xff0c;则可能具有与以下类似的代码&#xff0c;以确保在事务中进行写操作&#xff1a; public class StylesOfTx {public static void main( String[] args ) throws IOException{String path "/tmp/tx-st…

vue之computed和watch

计算属性 computed 侦听器or观察者 watch 一直以来对computed和watch一知半解&#xff0c;用的时候就迷迷糊糊的&#xff0c;今天仔细看了看文档&#xff0c;突然茅塞顿开&#xff0c;原来就是这么简单啊&#xff1a; computed&#xff0c;通过别人改变自己watch&#xff0c;…

python实现简单的百度翻译

这段时间&#xff0c;一直在学python,想找点东西实现一下&#xff0c;练手&#xff0c;所以我想通过python代码来实现翻译&#xff0c;话不多说&#xff0c;看吧&#xff01; 以chrome为例 1 打开百度翻译 https://fanyi.baidu.com 2 找到请求的url地址 https://fanyi.baidu.…