|
2008年5月8日
现在做互联网产品的团队都比较小,也可能没有特别多运维人员。因此特别需要用一些系统或是工具来监控服务器或者是服务是否正常。之前比较直接的做法是自己搭建一套开源的监控系统,现在随着云服务器的流行,也有越来越多的人用户会使用云端的监控平台。
从我的经验来看,云服务器监控是有些特别的好处的:
1.自建的监控平台,有一部分问题是没办法发出警报。比如,一般监控服务器也会在内部网络中,如果出现外部网络问题,监控服务是没办法通知到相关人员(因为发邮件或者是发短信的通路也会出现问题)。
2.自建的监控平台,只能检测到一个点的访问情况。阿里云监控平台可以检测杭州和青岛两个节点(这是我的帐号看到的),可以比较有效地了解非监测点的一个访问情况。
3.云监控平台都有一套不错的管理界面,可有效减少部署维护和使用成本。
下面我介绍一下,我使用过的一些云监控平台,并对他们的优点和缺点进行比较。
阿里云监控
阿里的云服务器在市场中是做的很不错的,我的很多朋友都购买了阿里的云服务器。以阿里的公司实力,做一款云监控的产品应该不会差到哪里去。
优点:
1.产品体验好,进入住界面就能轻而易举找到你想要的功能。
2.监控功能全,包括站点监控、服务器监控和自定义监控。
3.多点监控,可以在全国提供几个点的监控。从用户的角度了解服务是否可用。
4.站点监控功能也很全面,包含SMTP、POP3、FTP监控。
5.免费,且监控站点数没限制。
缺点:
1.服务器监控和自定义监控,要求是云服务器,如果不是云服务器,只能使用站点检测功能。
360网站服务监控
360的个人用户产品很少使用,但是360的企业产品还真有不少做的不错的。说说360监控的优缺点。
优点:
1.提供服务器监控。可以监控到服务器的磁盘,CPU,内存等情况。
2.UI和告警都设计的不错。
3.免费。
缺点:
1.需要在服务器上开SNMP协议。(不过360提供很多脚本工具,可以一键安装)
2.有20台服务器的限制。(这个基本上够用,超过20台服务器,可以购买服务了)
监控宝
名字很专一,一看就知道是专业做监控的。
优点:
1.专注监控,界面设计还算可以。
2.监控功能全面,内网采集的方式较多。可以采集数据库数据。
缺点:
1.免费用户,服务器和网站监控都有限制,而且数量极少2台服务器监控,5台网站监控。
作者简介: qiyadeng(www.qiyadeng.com)对互联网技术、运营及市场领域有浓厚的兴趣,喜爱思考、阅读、讨论;擅长Java开发及分布式技术。现专注于互联网的创新产品– 老来宝(http://www.laolaibao.com),帮助年轻用户获得补充养老金,并提供养老金增值产品。
比较少参加这类大型的会议,进到会场的第一眼,发现会议室已经全部坐满,后来主办方发现站的人太多了,找来了一些小板凳,我快速找到一个小板凳坐下。坐下开始认真听,非常开心地听到广告时间结束,和我计划的时间完美一致。
回顾一下我比较关系的几个主题
基于用户画像的大数据实例
演讲嘉宾是联通沃商店的大数据技术经理,该大数据实例主要是通过联通营运商的数据和沃商店进行分析,通过绘制用户画像的形式,在其他应用场景,如广告、游戏下载中为用户推荐用户喜欢的产品。可以看出来嘉宾技术实例及基础功是十分不错,至少是一个硕士毕业。近些年被大家挂在嘴边的机器学习算法、推荐算法、语义分析都有部分介绍,实在是接受不过来;比较熟悉的还是我们当初硕士的专业方向推荐算法,看到了简单的介绍觉得很亲切。不过后来提问环节看,现场还是很多高人,有不少是做这个领域的。不过归根是国有企业和类似研究机构,是否能产生非常大的价值,我表示怀疑,不过这些算法一罗列,对经费的分配还是很有好处的。
电商系统的心得分享
这又是一个国有企业,号称是线上卖大力丸的人(国药1健康)。从技术成长为总经理,有很多心得体会。感觉和我有那么一点像,有一些体会也迫不及待的分析给这些年轻的IT从业者,为人严肃,总是会把困难估计的充分一点(估计年轻也没少教学费)。演讲中说了构建系统中的四个原则
权限独立,相互制约
非常务实的看到某些大型企业的,部门斗争。从系统层面开始设计制约(这个应该非常符合老板心意)。这个对很多小型企业在成才过程中是非常有帮助的。
设计流程 减少犯错
在电商行业非常清楚客服和仓库的员工流动性,以及普遍受教育程度偏低,通过流程设计,而不是提高对用人的要求。这也是非常务实的方法。回顾之前在系统层面独自设计支持中央预订系统,设计出来的自动传真(当时网络不如现在易得)及新订单提醒(感谢施总的支持,增加音响进行声音)等等,简直觉得找到了知音。
多了解一些财务知识。
谈到的两点是数据之间需要有勾稽关系和不能修改历史数据,很骄傲我对财务的理解还是不错,从未犯过这种不靠谱的错误。
跨平台大型在线客服系统的技术构架
嘉宾谈了的是一套客服系统,比较多的关键字是客服妹子,可以看出IT从业者苦中作乐的精神。给我的体会是,客服系统都可以做成这样。从一个项目到一个产品,在云计算的世界,可以好一个客服的组件,也是有很大的价值。和我的理想事业很接近,可以花上一生中最精华的时间,做好一个有价值的小众专业的行业。
阿里分布式数据库服务实践
阿里的人就是高调,上场就调戏京东双11前系统崩溃。我也经历过很多系统崩溃,简直是开发人员的噩梦,也是IT人员信用受损的严重事件(因此我一直比较注意防止崩溃及崩溃后的快速恢复)。回到分布式数据库,这个是收获最大的一个演讲。虽然这个演讲看上去是再给阿里云的DRDS做宣传,但是嘉宾演讲的很进行,深入浅出地介绍了分布式数据库和单机数据库的区别。对分布式事务的重新认识是一个很大的收获,以前一直把教程中的数据库原理中的事务定义,作为分布式事务需要解决的问题,其实不是。需要更加务实,在淘宝阿里这类订单处理系统中,有一类对分布式事务的模式(异步消息机制);在其他领域会有其他模式分布式的事务模型,这些分布式模型肯定都不满足单机的事务模型,但是可以满足和解决相应领域的问题。
平台架构的服务器监控
一个APP的监控模型,猜测项目立项的原因,有两个。一个是和竞争对手的数据比较(UPYUN的对手主要是七牛),一个其实可以真正从用户的角度看,用户的体检速度如何,以及影响用户体验速度的真实原因。目前一般行业还不会做的这么细,因为UPYUN是技术支持公司,因此一定需要用这些数据去说服和支持用户。我们现在做的比较多的服务器的可用性、性能和应用的可用性、性能监控。前端时间刚好再比较,发现互联网上有不少好的监控平台,一般的创业公司,可以无需自己搭建监控平台,接入到相应的监控平台即可。下次再开文进行讨论。
作者简介:qiyadeng(www.qiyadeng.com)对互联网技术、运营及市场领域有浓厚的兴趣,喜爱思考、阅读、讨论;擅长Java开发及分布式技术。现专注于互联网的创新产品--老来宝(http://www.laolaibao.com),立志于帮助广大凤凰(diao)男(si)提供补充养老金管理平台。
摘要: guava是Java的一个扩展类库,在google的许多项目中使用过了,现在最为一个 开源的Java类库广泛使用(http://code.google.com/p/guava-libraries/)。
guava类库扩展的主要是这些相关类:collections(集合类),concurrency(并发),primitives,reflection(反射),comparison,I/O,hashi... 阅读全文
简单介绍一下8个Java牛人,他们为Java社区,创建了框架(framework),产品或者是写书,影响甚至改变了Java开发的方法(根据个人喜好排序)。 8.Tomcat创始人 James Duncan Davidson,是当时Sun公司的软件工程师(1997-2001),创建了Java的Web服务器Tomcat,Tomcat广泛应用于Java Web开发的各个领域。 7.测试驱动开发JUnit创始人 Kent Beck,极限编程和测试驱动开发方法的缔造者。此外,他还创造了JUnit,JUnit目前一次成为Java开发测试的事实标准。基于测试驱动的开发方法和JUnit给Java开发的方法带了巨大的变化。 6.Java Collections框架设计者 Joshua Bloch,领导设计了Java平台的许多功能,包括Java 5.0 版本中饱受赞誉的Java Collections框架。2004年他离开Sun公司,成为Google的首席Java架构师,此外他的著作“Effective Java”基本上是学习Java的必读之书。 5.JBoss创始人 Marc Fleury,在2001年创造了JBoss,JBoss是一个Java开源的应用服务器,也已经成为Java Web应用部署中的事实标准。后来他把JBoss买给了RedHat,之后继续从事JBoss的开发工作。不过2007年他离开了RedHat去追求他的个人爱好。 4.Struts创始人 Craig Mcclanahan,创建了Struts,一个流行的基于Java的MVC开源框架,基本上很多Java开发者都知道如何开发Struts的应用程序。 3.Spring创始人 Rod Johnson,Spring框架的创始人,Spring Source的CEO。Spring是一个非常流行的Java应用程序开发的开源框架。此外,他的著作Expert One-to-One J2EE Design and Development,是J2EE最有影响力的一本书。 2.Hibernate创始人 Gavin King,Hibernate的创始人,一个流行的Java ORM解决方案;同时他也是Seam的创始人,此外他为EJB3.0和JPA也做出了突出的贡献。 1.Java之父 James Gosling,1994年发明了Java语言,他创建了Java编译器和虚拟机。在2010年,当Oracle收购Sun公司时,他离开了Sun公司。 原创文章,作者:qiyadeng,转载请注明: 转载自http://www.qiyadeng.com/ 本文链接地址: 你应该知道的8个Java牛人
1.申请开发者帐号 首先注册百度的帐号,然后申请成为百度开发者(需要通过手机进行身份证验证)。 2.新建应用 点击菜单中的创建应用,我们目前选择的是Web应用。 应用创建之后,选择左边菜单的云环境,环境类型需要选择JAVA。 并新创建一个版本,输入1作为版本号 3.在百度集成开发环境中开始开发 百度提供了基于Eclipse的插件,由于该插件不能支持最新的Eclipse版本。建议下载百度的一键安装版本。百度文档中介绍了如何使用开发环境,详细请看集成开发环境使用。 打开百度集成开发环境,在Eclipse左下角点击Login to Baidu,使用你的账号登陆。然后点击工具栏中百度Logo,选择Import BAE Project,填入application和version 之后选择Java作为Project Language。 4.解决项目错误 刚导入的BAE project,在Eclipse中会报错。通过problems view可以看到是因为JRE环境配置不正确和Web运行环境设置不正确。 A.右键项目属性--选择JavaBuildPath,在Libraries中选择Add Library,之后再选择JRE System Library。 B.接下来把Java project转换为Java Web Project(Eclipse中Java Project转换为Java web Project),注意如果你的tomcat是6版本的话,请注意选择Dynamic web Module的版本不超过2.5。 设置Web应用的运行环境,在servers view中新建一个tomcat服务器。 C.和A类似,在Java Build Path中加入 Server Runtime,选择Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代码 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把项目部署到tomcat中。
至此项目错误全部解决,应该可以看到运行结果。
5.新建Servert测试
新建一个Servlet,HomeServlet,Eclipse会自动在web.xml中加入配置信息,HomeServet.java和web.xml的部分代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代码
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 运行tomcat,可以看到如下运行成功,这样你就可以像一般的Java Web Project一样进行开发。
6.最后
BAE中Java环境中百度使用的Jetty,而不是tomcat,Jetty的好处是不需要频繁的重启,修改的代码即时就可看到运行结果。
通过SVN提交代码到BAE,如果有需要做小的修改,可以通过百度的在线编辑工具直接修改。
原创文章,转载请注明: 转载自http://www.qiyadeng.com/
本文链接地址: 百度开发者中心BAE新建Java应用
1.申请开发者帐号 首先注册百度的帐号,然后申请成为百度开发者(需要通过手机进行身份证验证)。 2.新建应用 点击菜单中的创建应用,我们目前选择的是Web应用。 应用创建之后,选择左边菜单的云环境,环境类型需要选择JAVA。 并新创建一个版本,输入1作为版本号 3.在百度集成开发环境中开始开发 百度提供了基于Eclipse的插件,由于该插件不能支持最新的Eclipse版本。建议下载百度的一键安装版本。百度文档中介绍了如何使用开发环境,详细请看集成开发环境使用。 打开百度集成开发环境,在Eclipse左下角点击Login to Baidu,使用你的账号登陆。然后点击工具栏中百度Logo,选择Import BAE Project,填入application和version 之后选择Java作为Project Language。 4.解决项目错误 刚导入的BAE project,在Eclipse中会报错。通过problems view可以看到是因为JRE环境配置不正确和Web运行环境设置不正确。 A.右键项目属性--选择JavaBuildPath,在Libraries中选择Add Library,之后再选择JRE System Library。 B.接下来把Java project转换为Java Web Project(Eclipse中Java Project转换为Java web Project),注意如果你的tomcat是6版本的话,请注意选择Dynamic web Module的版本不超过2.5。 设置Web应用的运行环境,在servers view中新建一个tomcat服务器。 C.和A类似,在Java Build Path中加入 Server Runtime,选择Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代码 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把项目部署到tomcat中。
至此项目错误全部解决,应该可以看到运行结果。
5.新建Servert测试
新建一个Servlet,HomeServlet,Eclipse会自动在web.xml中加入配置信息,HomeServet.java和web.xml的部分代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代码
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 运行tomcat,可以看到如下运行成功,这样你就可以像一般的Java Web Project一样进行开发。
6.最后
BAE中Java环境中百度使用的Jetty,而不是tomcat,Jetty的好处是不需要频繁的重启,修改的代码即时就可看到运行结果。
通过SVN提交代码到BAE,如果有需要做小的修改,可以通过百度的在线编辑工具直接修改。
空指针异常(Null Pointer Exception)是我们平时最容易碰到的,也是最令人讨厌的异常。本文介绍如何避免出现空指针异常。 首先我们看如下的示例 private Boolean isFinished(String status) { if (status.equalsIgnoreCase("Finish")) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 如果status的值为空的话,那么将会出现空指针异常(本例第2行)。所以我们应该使用如下的方法
private Boolean isFinished(String status) { if ("Finish".equalsIgnoreCase(status)) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 这样的话,如果status为空,也不会出现空指针异常。相信我们大多数朋友已经知道这样的方法了,如果一个对象可能为null,那么不需要直接调用它的方法。
接下来我将接着提供几种避免空指针的建议。
1.判断Collection是否为空。
2.使用一些判断方法。
3.assert关键字。
4.Assert类。
5.异常处理。
6.太多的点.操作语法。
7.使用StringUtils类
1.判断Collection是否为空
Collection 为空是指Collection中没有元素。一些开发者如果碰到Collection中没有元素的时候,经常return null,更好的做法是,你应该return Collections.EMPTY_LIST,Collections.EMPTY_SET或者是Collections.EMPTY_MAP.
错误的代码
public static List getEmployees() { List list = null; return list; }
正确的代码
public static List getEmployees() { List list = Collections.EMPTY_LIST; return list; }
2.使用一些判断方法
使用一些方法如contains(),indexOf(),isEmpty(),containsKey(),ContainsValue和hasNext()等来判断,确保不存在空值。
示例:
String myName = "qiyadeng"; List list = Collections.EMPTY_LIST; boolean exist = list.contains(myName); int index = list.indexOf(myName); boolean isEmpty =list.isEmpty(); Map map =Collections.EMPTY_MAP; exist=map.containsKey(myName); exist=map.containsValue(myName); isEmpty=map.isEmpty(); Set set=Collections.EMPTY_SET; exist=set.contains(myName); isEmpty=set.isEmpty(); Iterator iterator; exist = iterator.hasNext();
3.assert关键字
在Java1.4版本之后,提供了断言assert来确定你的代码中的假设。使用的语法如下:
expression1是一个boolean表达式,如果expression1返回的false,系统将会抛出AssertError(没有详细信息)。
另外一种使用方法
assert expression1:expression2 如果expression1返回false,那么系统将会抛出AssertError,并且详细信息为expression2。
示例:
public static String getManager(String employeeId) { assert (employeeId != null) : "employeeId must be not null"; return "qiyadeng"; } 我使用getManager(null)来调用getManger方法,最后运行的结果是"java.lang.AssertionError:employeedId must be not null"
注意记得使用java选项中加入-enableassertion开启assertion功能。
4.Assert类
Assert类在com.bea.core.repackaged.springframework.util包中,有许多方法可以用于断言。
public static String getManager(String employeeId) { Assert.notNull(employeeId, "employeeId must be not null"); Assert.hasLength(employeeId, "employeeId must has length greater than 0"); return "qiyadeng"; } 当我同样使用getManager(null)来调用getManager方法,将获得信息"java.lang.IllegalArgumentException: employeeId must be not null"。
5.异常处理
使用try catch处理异常或是检查变量是否为空。
public static String getManager(String employeeId) { return null; } 如上代码,我使用下面方法调用
String managerId = getManager("A015"); System.out.println(managerId.toString()); 将会发生"java.lang.NullPointerException",为了处理这个异常,我们应该使用try catch来处理异常或者是检查变量是否为null。
try-catch方法
String managerId = getManager("A015"); try { System.out.println(managerId.toString()); } catch (NullPointerException npe) { //write your code here } 或者是对变量进行检查
String managerId = getManager("A015"); if (managerId != null) { System.out.println(managerId.toString()); } else { //write your code here }
6.不要太多的点.操作语法
一些开发者使用太多的这样的方法来减少代码,但是这个对后面的维护和异常处理都是不太好的。
错误的写法
String attrValue = (String)findViewObject("VO_NAME").getCurrentRow().getAttribute("Attribute_NAME"); 正确的写法
ViewObject vo = findViewObject("VO_NAME"); Row row = vo.getCurrentRow(); String attrValue = (String)row.getAttribute("Attribute_NAME");
7.使用StringUtils类
StringUtil是org.apache.commns.lang包中的类,我们可以使用该类来避免空指针异常。
例如 StringUtils.isEmpty(),StringUtils.isBlank,StringUtils.equals()等等,更多的你可以参考文档。
为了不出现空指针异常,在写代码的过程中需要时刻检查你的代码是否会抛出NullPointerException,如果你没有时间及时调整的话,使用//TODO标记,便于你后面解决问题。
现在经常需要根据用户提供的位置,提供一些和位置相关的信息。有时可以直接确定用户的经度和纬度,有时不一定可以确定用户的经度和纬度信息,用户是 通过输入一些路名、标志性建筑或是商场名等位置,但是我们的数据库可能并没有存法用户可能输入的这些位置信息的经度纬度,这时候可以使用一些地图提供的 API来确定,用户所输入的位置信息的经度和纬度。 我们使用百度地图提供的GeoCoding API实现从位置信息到经度纬度的转换,详细的使用说明可以参考 GeoCoding API。我们这里做一个简单的演示 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//百度返回的经度纬度信息xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 使用JUnit进行测试 @Test public void testGeoCode() throws Exception { BaiduMapService bms = new BaiduMapService(); String response = bms.getGeoCode("上地十街十号"); BaiduGeoCodeResponse res = BaiduGeoCodeResponse.getBaiduGeoCode(response);//解析xml System.out.println(res.toString()); } 输出的结果 <GeocoderSearchResponse> <status>OK</status> <result> <location> <lat>40.057098</lat> <lng>116.307175</lng> </location> <precise>1</precise> <confidence>80</confidence> <level>道路</level> </result> </GeocoderSearchResponse> BaiduGeoCodeResponse [lat=40.057098, lng=116.307175]
到了一个较陌生的环境,经常会在周边找一些基础设施,比如银行,商场,餐厅等(还有一种更急切的是找厕所)。通过百度提供的地图API,可以在你的应用中简单做到,详情可阅读 Place API。我们以查找周边银行作为示例,需确定的参数至少有三个,要查找的位置的经度和纬度,需要查找的内容的类型或是关键字。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//位置xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } Junit测试 @Test public void testGetBaiduPlace() throws Exception{ BaiduMapService bms = new BaiduMapService(); String response = bms.getPalace("银行", "39.915", "116.404"); List<BaiduPlaceResponse> list = BaiduPlaceResponse.getBaiduPlace(response); for(BaiduPlaceResponse res:list){ System.out.println(res.toString()); } } 输出内容(省略部分内容) <?xml version="1.0" encoding="utf-8" ?> <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中国工商银行东长安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>东城区东长安街1号东方广场西三办公楼1楼</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi</detail_url> <tag>银行,王府井/东单</tag> </result> </results> </PlaceSearchResponse> BaiduPlaceResponse [name=中国工商银行东长安街支行, telephone=null, address=东城区东长安街1号东方广场西三办公楼1楼, lat=39.915891, lng=116.41867, tag=null, detailUrl=http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&output=html&source=placeapi]
位置识别这是实际应用经常应用的消息,特别是很多商家,通过了解用户位置,给用户提供特别的产品或是商场的推荐。其中用户可能发送两种类型的消息: 1.微信地理位置信息 2.路名、标志性建筑或是商场名称 1.微信地理位置消息认识一下,微信地理位置消息,包含一些什么信息 <xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1351776360</CreateTime> <MsgType><![CDATA[location]]></MsgType> <Location_X>23.134521</Location_X> <Location_Y>113.358803</Location_Y> <Scale>20</Scale> <Label><![CDATA[位置信息]]></Label> <MsgId>1234567890123456</MsgId> </xml>
包含的主要信息有经度纬度和Label的位置。可以根据label中描述的位置信息,提供给用户对应的服务。也可根据用户的经度纬度信息,提供你最近的产品或是有地域性的产品。 首先根据微信的地理位置信息,定义WeChatLocationMessage类,并能把Xml转换为WeChatLocationMessage对象 public class WeChatLocationMessage { private String toUserName; private String fromUserName; private String createTime; private String msgType; private String locationx; private String localtiony; private String scale; private String label; private String msgId; public static WeChatLocationMessage getWeChatLocationMessage(String xml){ XStream xstream = new XStream(new DomDriver()); WeChatLocationMessage message = null; xstream.alias("xml", WeChatLocationMessage.class); xstream.aliasField("ToUserName", WeChatLocationMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatLocationMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatLocationMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatLocationMessage.class, "msgType"); xstream.aliasField("Location_X", WeChatLocationMessage.class, "locationx"); xstream.aliasField("Location_Y", WeChatLocationMessage.class, "localtiony"); xstream.aliasField("Scale", WeChatLocationMessage.class, "scale"); xstream.aliasField("Label", WeChatLocationMessage.class, "label"); xstream.aliasField("MsgId", WeChatLocationMessage.class, "msgId"); message = (WeChatLocationMessage)xstream.fromXML(xml); return message; } //getter and setter } 本文利用百度的地图API,查找最近的银行做为示例。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 输出的结果 <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中国工商银行东长安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>东城区东长安街1号东方广场西三办公楼1楼</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi </detail_url> <tag>银行,王府井/东单</tag>
</result> </results> </PlaceSearchResponse> 接下来,把百度地图反映出来的最近位置信息,以图文消息的格式展示给微信用户 public static String getWeChatReplyNewsMessageByBaiduPlace(List<BaiduPlaceResponse> placeList, double lat, double lng,String userName, int size){ WeChatReplyNewsMessage newsMessage = new WeChatReplyNewsMessage(); List<Item> items = new ArrayList<Item>(); StringBuffer strBuf = new StringBuffer(); logger.log(Level.INFO,"placeList count="+placeList.size()); newsMessage.setItems(items); if(placeList.size()>size){ newsMessage.setArticleCount(size); } else{ newsMessage.setArticleCount(placeList.size()); } logger.log(Level.INFO,"article count="+newsMessage.getArticleCount()); newsMessage.setCreateTime(new Date().getTime()+""); newsMessage.setMsgType("news"); newsMessage.setFuncFlag("0"); newsMessage.setToUserName(userName); newsMessage.setFromUserName(WeChatConstant.FROMUSERNAME); for(int i = 0;i <newsMessage.getArticleCount();i++){ BaiduPlaceResponse place = placeList.get(i); Double distance = GeoUtil.DistanceOfTwoPoints(Double.valueOf(place.getLng()), Double.valueOf(place.getLat()), lng, lat, GaussSphere.Beijing54); Item item = new Item(); item.setTitle(place.getName()+"["+distance+"米]"+"\n"+place.getAddress()+"\n"+place.getTelephone()); item.setPicUrl(""); item.setUrl(place.getDetailUrl()); item.setDescription(""); items.add(item); } logger.log(Level.INFO,"newMessage="+newsMessage.toString()); strBuf = strBuf.append(getWeChatNewsMessage(newsMessage)); return strBuf.toString(); } public static String getWeChatNewsMessage(WeChatReplyNewsMessage newsMessage){ XStream xstream = new XStream(new DomDriver()); xstream.alias("xml", WeChatReplyNewsMessage.class); xstream.aliasField("ToUserName", WeChatReplyNewsMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatReplyNewsMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatReplyNewsMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatReplyNewsMessage.class, "msgType"); xstream.aliasField("ArticleCount", WeChatReplyNewsMessage.class, "articleCount"); xstream.aliasField("Content", WeChatReplyNewsMessage.class, "content"); xstream.aliasField("FuncFlag", WeChatReplyNewsMessage.class, "funcFlag"); xstream.aliasField("Articles", WeChatReplyNewsMessage.class, "items"); xstream.alias("item", Item.class); xstream.aliasField("Title", Item.class, "title"); xstream.aliasField("Description", Item.class, "description"); xstream.aliasField("PicUrl", Item.class, "picUrl"); xstream.aliasField("Url", Item.class, "url"); return xstream.toXML(newsMessage); } 2.路名、标志性建筑或是商场名称对路名、标志性建筑等信息,方法还是通过第三方地图信息,确定输入的位置信息的经度纬度。 本文使用百度地图API,确定所查找的位置的经度和纬度。 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 确定了经度和纬度,问题就变成和第1种消息类型一致了,根据经度纬度去做相应处理。 3.源代码本文的代码较长,提供源代码下载。 WeChatDemo下载
本文介绍,如果把Java Project转换为Java Web Project,应该在多数的Eclipse的版本都类似。 1.Java Project一个Java Projec,在Eclipse中显示的是一个“J”的蓝色文件夹。 2.Project Facets右键项目属性Properties,右侧选择菜单Project Facets,点击converted to faceted form... 勾选dynamic web module 选择下面的further configuration available 项目中的Web目录和设置保持一致。 3.Java Web Project这样你就转换到了Java Web Project。
1.新建Project 新建Java Project,并把mongo-java-driver驱动加入到项目bulid path中,如果你使用的是maven增加依赖。 <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.10.1</version> </dependency> 2.连接上MongoDB //>2.10版本 MongoClient mongo = new MongoClient( "localhost" , 27017 );
//老版本 Mongo mongo = new Mongo("localhost", 27017); 如果需要验证,需要输入用户名和密码 MongoClient mongoClient = new MongoClient(); DB db = mongoClient.getDB("database name"); boolean auth = db.authenticate("username", "password".toCharArray()); 3.MongoDB数据库 得到MongoDB中的数据库,如果数据库名不存在,MongoDB会自动创建 DB db = mongo.getDB("database name"); 显示所有的数据库 List<String> dbs = mongo.getDatabaseNames(); for(String db : dbs){ System.out.println(db); } 4.MongoDB Collection(MongoDB表) 得到数据库中的表 DB db = mongo.getDB("testdb"); DBCollection table = db.getCollection("user"); 显示数据库中的所有表 DB db = mongo.getDB("testdb"); Set<String> tables = db.getCollectionNames(); for(String coll : tables){ System.out.println(coll); } 5.插入、查找、更新、删除操作 插入数据,向Collection(表)中插入一个Document DBCollection table = db.getCollection("user"); BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); 更新Document中的name="qiyadeng.com" DBCollection table = db.getCollection("user");
BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng");
BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com");
BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); 从Collection中查找name="qiyadeng.com"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } 删除name="qiyadeng"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); table.remove(searchQuery);
6.完整的例子 package com.qiyadeng.mongodb;
import java.util.Date;
import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.MongoClient;
public class MongoDBSample {
public static void main(String[] args) throws Exception{ /**** Connect to MongoDB ****/ //2.10.0后,使用MongoClient MongoClient mongo = new MongoClient("localhost", 27017); /**** Get database ****/ // if database doesn't exists, MongoDB will create it for you DB db = mongo.getDB("testdb"); /**** Get collection / table from 'testdb' ****/ // if collection doesn't exists, MongoDB will create it for you DBCollection table = db.getCollection("user"); /**** Insert ****/ // create a document to store key and value BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); /**** Find and display ****/ BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } /**** Update ****/ // search document where name="qiyadeng" and update it with new values BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng"); BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com"); BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); /**** Find and display ****/ BasicDBObject searchQuery2 = new BasicDBObject().append("name", "qiyadeng.com"); DBCursor cursor2 = table.find(searchQuery2); while (cursor2.hasNext()) { System.out.println(cursor2.next()); } } }
输出 { "_id" : { "$oid" : "51444c88874c79654063356b"} , "name" : "qiyadeng" , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"}} { "_id" : { "$oid" : "51444c88874c79654063356b"} , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"} , "name" : "qiyadeng.com"}
使用mongo验证创建的数据库testdb,collection user是否存在。
本文介绍如何安装在windows 7中安装MongoDB。 注:MongoDB并不像Windows上安装其他软件,只需要下载Zip包并解压,然后配置数据存放目录并启动即可。 1.下载MongoDB从MongoDB官方网站,根据你的平台选择对应的windows的压缩包并解压,本文解压到D:\mongodb\。
注:如果需要在命令行中快速使用MongoDB bin目录下的命令,可以将D:\mongoDB\bin加入到Window环境变量。 2.配置数据文件在D:\mongodb\创建mongo.config文件,如下(并在d:\mongodb目录下新建data,log文件夹) ##数据存储的位置 dbpath=D:\mongodb\data ##所有的输出位置 logpath=D:\mongodb\log\mongo.log ##日志读写操作 diaglog=3 3.运行MongoDB Server在命令控制行,切换到d:\mongodb\bin目录下,使用命令mongod.exe --config d:\mongdb\mongo.config启动MongoDb Server。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config all output going to: D:\mongodb\log\mongo.log 4.连接MongoDB新开启一个命令行控制窗口,使用mongo.exe连接MongoDB Server. 5.设置MongoDB为Windows服务在命令行控制窗口,加入--install选项可以把MongoDB安装为Windows服务。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config 启动MongoDB的命令为:net start MongoDB 停车MongODB的命令为:net stop MongoDB 删除MongoDB的命令为:mongod --remove
1.设置成为开发者模式
登录微信工作平台,选择高级功能-进入开发模式,成为开发者。需要做如下图配置。URL配置的信息是指,微信的后台服务器把您的用户消息发送到该URL处理。Token是你和微信之间的一个密码,用来验证消息是否是从微信的服务发送而来,而不是其他来攻击你的系统。
现在你还不能设置,在设置时微信会GET请求你设置的URL,已检测接口是否可以使用。只有等你准备好GET方法之后才可以进行设置。
2.实现GET方法
从文档中知道,我们需要实现POST和GET方法,GET方法用于验证微信和你的通讯验证,POST用于消息处理。
新建Servlet HelloWeChat,先实现其中的GET方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 为了简单起见,先不对消息来源进行校验
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
可以在本地使用http://localhost:8080/QiyadengWeb/HelloWeChat?echostr=hello中文,先进行测试,如果没有问题,可以部署到服务器上,然后在微信公众平台进行设置了。
3.实现POST方法
POST方法首先接收到微信公众平台传送过来的XML,从中提取消息发送人和消息内容。更加消息发送内容,你可以增加自己的处理逻辑,最后拼装成回复消息XML,返回给微信公众平台。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的处理逻辑,这里只是简单重复消息
replyMsg.append("您给我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暂时看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
关于调试,这里推荐一个工具Fiddler,你可以模拟微信的POST消息到你的本地,而不必每次部署到服务器上进行调试。关于Fiddler的POST数据使用方法,可以参考下图标注内容。
4.部署并测试
完成第一步,并和你的公众帐号好进行对话,回复消息没有问题的话,那就恭喜你了。
5.依赖库
使用maven的同学,添加以下依赖即可。非maven用户,找到这些库添加到buider path中即可。
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.3</version>
</dependency>
6.完整的代码
package com.qiyadeng.wechat;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* Servlet implementation class HelloWeChat
*/
public class HelloWeChat extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloWeChat() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 为了简单起见,先不对消息来源进行校验
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的处理逻辑,这里只是简单重复消息
replyMsg.append("您给我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暂时看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
private WeChatTextMessage getWeChatTextMessage(String xml){
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatTextMessage.class);
xstream.aliasField("ToUserName", WeChatTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatTextMessage.class, "content");
xstream.aliasField("MsgId", WeChatTextMessage.class, "msgId");
WeChatTextMessage wechatTextMessage = (WeChatTextMessage)xstream.fromXML(xml);
return wechatTextMessage;
}
private String getReplyTextMessage(String content, String weChatUser){
WeChatReplyTextMessage we = new WeChatReplyTextMessage();
we.setMessageType("text");
we.setFuncFlag("0");
we.setCreateTime(new Long(new Date().getTime()).toString());
we.setContent(content);
we.setToUserName(weChatUser);
we.setFromUserName("shanghaiweather");//TODO 你的公众帐号微信号
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatReplyTextMessage.class);
xstream.aliasField("ToUserName", WeChatReplyTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatReplyTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatReplyTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatReplyTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatReplyTextMessage.class, "content");
xstream.aliasField("FuncFlag", WeChatReplyTextMessage.class, "funcFlag");
String xml =xstream.toXML(we);
return xml;
}
}
开始微信公众平台的开发,我们首先要了解微信平台可以帮助我们做哪些事情? 使用您的公众账号登陆http://mp.weixin.qq.com/,选择菜单--高级功能-开发模式--查看文档,即能看到微信公众平台目前所能开发的功能。 一、通讯机制 公众平台的主要内容是 - 接受用户发送给您公众账号的消息
- 给您的用户回复消息
需要特别说明的是,发送消息和回复消失是一个连贯的过程,只能在一个对话中完成。也就是说您的用户不找您说话,您是不能主动发送消息给你的客户(群发是另外一种情况,有次数限制。你也可以申请付费使用微信CRM平台)。所有的发送消息和接受消息,都需要微信平台进行中转。 二、消息类型 下面介绍用户能给您发送的消息类型,也就是目前接受到的消息类型。 1.接受消息类型 1.1文本消息: 这也是我们平时碰到最多的,可以根据文本中提到的一些关键字,进行判断,判断用户的含义,并进行回复。 1.2图片消息: 目前通过图片理解用户想表达的意思,还是有较大难度,因此多数的公众账号,会选择忽略图片信息或选择由人工来处理。只能说一句:图片很美,但是我看不懂。 1.3地理位置消息: 用户把他的位置发给您,这对大多数公众账号来说,是一个重要的信息。可以提供一些基于位置信息的服务,比如酒店预订公众账号,可以给你推荐你周边的酒店。 另外一个补充是,可以在文本消息中分析出位置信息,并加以利用。比如用户输入“南京路步行街”,可以提供用户南京路步行街的相关商户。 1.4链接消息: 目前还没有看到开发模式中特别有效的使用方法。使用比较多的可能会是购物时或是咨询时,对所谈论的对象进行明确。 1.5事件推送消息: 当用户进入到和你对话的过程中,可以先和用户打招呼等。这个消息目前只支持4.5版本,且暂时还没有开发。后续可想想的空间很大,比如用户进入到会话之后,摇一摇会发生什么呢? 2.回复消息类型 2.1文本消息 这是我们平时发送最多的一类消息,当只需要简单的文字即可回答用户的消息时,可用文本消息。文本消息中可以带有链接地址。 2.2图文消息 图文消息,这是我们在推送消息中经常看到的消息格式。每项内容可以点击查看更详细信息(当然你也可以把链接设置为空,使其不能跳转) 2.3音乐消息 在你的答复中给用户一个语音消息或是音乐,可以获得不少用户的亲睐。 了解了公众平台的通讯机制和消息类型,接下来,我们开始准备开发环境了。
在类似PHP的项目中,经常会碰到项目开发目录和运行目录不是一个目录的情况。 在window7下面有一个非常好的工具,可以做到自动同步。 如: mklink /J D:\wamp\www\shanghaisales C:\Users\Admin\git\ShanghaiSales\ShanghaiSales
本文中演示如何通过URLConnection获取Http响应Header信息 1.从响应中获得Header信息 URL obj = new URL("http://www.qiyadeng.com"); URLConnection conn = obj.openConnection(); Map<String, List<String>> map = conn.getHeaderFields(); 2.从响应Header中获取Server信息 Map<String, List<String>> map = conn.getHeaderFields(); List<String> server = map.get("Server"); 完整的示例 package com.qiyadeng.http;
import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class GetHttpResponseHeader { public static void main(String[] args) { try { URL obj = new URL("http://www.qiyadeng.com"); URLConnection conn = obj.openConnection(); Map<String, List<String>> map = conn.getHeaderFields(); System.out.println("显示响应Header信息\n"); for (Map.Entry<String, List<String>> entry : map.entrySet()) { System.out.println("Key : " + entry.getKey() + " ,Value : " + entry.getValue()); } System.out.println("\n使用key获得响应Header信息 \n"); List<String> server = map.get("Server"); if (server == null) { System.out.println("Key 'Server' is not found!"); } else { for (String values : server) { System.out.println(values); } } } catch (Exception e) { e.printStackTrace(); } } } 输出 显示响应Header信息...
Key : null ,Value : [HTTP/1.1 200 OK] Key : X-Pingback ,Value : [http://www.qiyadeng.com/xmlrpc.php] Key : Date ,Value : [Sun, 10 Mar 2013 12:16:26 GMT] Key : Transfer-Encoding ,Value : [chunked] Key : Connection ,Value : [close] Key : Content-Type ,Value : [text/html; charset=UTF-8] Key : Server ,Value : [Apache/2.2.3 (CentOS)] Key : X-Powered-By ,Value : [PHP/5.2.17]
使用key获得响应Header信息 ...
Apache/2.2.3 (CentOS)
在.net中也有非常多的日志工具,今天介绍下NLog。NLog特别好的地方就是和Vs(Visual Studio)开发环境的集成。 只需下载(下载地址)安装包,安装之后NLog就会在VS的新建项中增加很多选项,并且在编辑NLog配置文件时也会提供智能提示和校验。 NLog工作主要依赖的是两个文件一个是NLog.dll,另外一个是NLog.config,解下来演示下如何引入和进行配置 1.在你的项目中加入NLog。右击项目,选择添加新项目,选择Empty NLog Configuration,并选择添加(如图)。 (说明:有可能不像官网上说的在NLog的目录下面,在ASP.net Web项目中,会在VB的目录中。) 在非Asp.net项目中,记得把NLog.config文件复制到输出目录(右击NLog.config文件属性)。 2.编辑配置文件NLog.config. 关于配置文件如何编辑有大量的篇幅(https://github.com/nlog/nlog/wiki/Configuration-file),我们这里介绍两种常用的场景。 A)在Vs的输出窗口输出日志,关于这些变量的说明${},请参看文档Configuration Reference。(https://github.com/nlog/nlog/wiki) <target name="debugger" xsi:type="Debugger" layout="${logger}::${message}" />
B)以文件形式输出。 <target name="file" xsi:type="File" maxArchiveFiles="30"
layout="${longdate} ${logger} ${message}"
fileName="${basedir}/logs/log${shortdate}.txt"
keepFileOpen="false" />
完整的配置文件例子: <?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true" internalLogFile="d:\internal_log_file.txt" internalLogLevel="Trace" internalLogToConsole="true">
<targets>
<target name="debugger" xsi:type="Debugger" layout="${logger}::${message}" />
<target name="file" xsi:type="File" maxArchiveFiles="30"
layout="${longdate} ${logger} ${message}"
fileName="${basedir}/logs/log${shortdate}.txt"
keepFileOpen="false" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="debugger" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
3.在程序中使用NLog 在程序中使用就特别简单了,和大多数日志工具类似。 using NLog; namespace MyNamespace { public class MyClass { private static Logger logger = LogManager.GetCurrentClassLogger(); } }
本文演示如何使用Collections.frequency和Map来计算重复项出现的次数。(Collections.frequency在JDK 1.5版本以后支持)
package com.qiyadeng.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class CountDuplicatedList {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("b");
list.add("c");
list.add("a");
list.add("a");
list.add("a");
System.out.println("\n例子1 - 计算'a'出现的次数");
System.out.println("a : " + Collections.frequency(list, "a"));
System.out.println("\n例子2 - 计算所有对象出现的次数");
Set uniqueSet = new HashSet(list);
for (String temp : uniqueSet) {
System.out.println(temp + ": " + Collections.frequency(list, temp));
}
System.out.println("\n例子3 -用Map来计算对象出现的次数");
Map map = new HashMap();
for (String temp : list) {
Integer count = map.get(temp);
map.put(temp, (count == null) ? 1 : count + 1);
}
printMap(map);
System.out.println("\nMap排序-以key排序");
Map treeMap = new TreeMap(map);
printMap(treeMap);
}
public static void printMap(Map map) {
for (Map.Entry entry : map.entrySet()) {
System.out.println("Key-value : " + entry.getKey() + "- "
+ entry.getValue());
}
}
}
输出结果
例子1 - 计算'a'出现的次数
a : 4
例子2 - 计算所有对象出现的次数
d: 1
b: 2
c: 2
a: 4
例子3 -用Map来计算对象出现的次数
Key-value : d- 1
Key-value : b- 2
Key-value : c- 2
Key-value : a- 4
Map排序-以key排序
Key-value : a- 4
Key-value : b- 2
Key-value : c- 2
Key-value : d- 1
抓紧年前的两天时间,自己开发了一个微信天气预报的自定义接口。做微信公众平台的开发已经不是第一次,这也算是自娱自乐,第一个版本不需要太注重外观,主要注重天气信息。 微信号(shanghaiweather):上海天气预报- 2692296679
这篇文章将使用两个例子计算两个日期的时间差。
1.使用Java SDK。
2.使用Joda库。
1.使用Java SDK
计算两个Date之间的时间差,基本思路为把Date转换为ms(微秒),然后计算两个微秒时间差。时间的兑换规则如下:
1s秒 = 1000ms毫秒 1min分种 = 60s秒 1hours小时 = 60min分钟 1day天 = 24hours小时
|
package com.qiyadeng.date;
import java.text.SimpleDateFormat; import java.util.Date;
public class DateDifferentExample { public static void main(String[] args) { String dateStart = "2013-02-19 09:29:58"; String dateStop = "2013-02-20 11:31:48";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null; Date d2 = null;
try { d1 = format.parse(dateStart); d2 = format.parse(dateStop);
//毫秒ms long diff = d2.getTime() - d1.getTime();
long diffSeconds = diff / 1000 % 60; long diffMinutes = diff / (60 * 1000) % 60; long diffHours = diff / (60 * 60 * 1000) % 24; long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.print("两个时间相差:"); System.out.print(diffDays + " 天, "); System.out.print(diffHours + " 小时, "); System.out.print(diffMinutes + " 分钟, "); System.out.print(diffSeconds + " 秒.");
} catch (Exception e) { e.printStackTrace(); }
} }
运行结果:
两个时间相差:1 天, 2 小时, 1 分钟, 50 秒.
|
2.Joda时间库
package com.qiyadeng.date;
import java.text.SimpleDateFormat; import java.util.Date;
import org.joda.time.DateTime; import org.joda.time.Days; import org.joda.time.Hours; import org.joda.time.Minutes; import org.joda.time.Seconds;
public class JodaDateDifferentExample { public static void main(String[] args) { String dateStart = "2013-02-19 09:29:58"; String dateStop = "2013-02-20 11:31:48";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null; Date d2 = null;
try { d1 = format.parse(dateStart); d2 = format.parse(dateStop);
DateTime dt1 = new DateTime(d1); DateTime dt2 = new DateTime(d2);
System.out.print("两个时间相差:"); System.out.print(Days.daysBetween(dt1, dt2).getDays() + " 天, "); System.out.print(Hours.hoursBetween(dt1, dt2).getHours() % 24 + " 小时, "); System.out.print(Minutes.minutesBetween(dt1, dt2).getMinutes() % 60 + " 分钟, "); System.out.print(Seconds.secondsBetween(dt1, dt2).getSeconds() % 60 + " 秒.");
} catch (Exception e) { e.printStackTrace(); }
} }
运行结果:
两个时间相差:1 天, 2 小时, 1 分钟, 50 秒.
|
问题: 页面保证是保证的一部分。页面机制我这边采用的是操作结束前不能重做,服务端结束返回信息后,适当条件下(服务器忙)是允许重新操作的。 你说的唯一值保证页面不重复的方法,可以本页面再次提交呢? 解决办法: 哈哈,还是流程图吧,有的时候更容易说清楚问题。应该回答了你的问题。 关于页面(我们叫前端吧)和后台(服务端)在判断中和校验中的关系,我认为的原则应该是: 1.如果前端和后端校验只能选一个的话,选择后端校验。可以都选的话,两者都需要。 2.前端校验可以检查一些格式错误或是基本校验,可以减轻服务器校验负担,也可以让用户更快知道自己错在哪里。 3.后台校验更擅长做的是逻辑校验,数据的格式可能都正确,但是发生的关系不正确或是重复数据,只有通过后台校验才能校验出来。
今天看到关于一个整数表示为n个连续整数和的问题。搜索了下找到如下文章: 还搜索到程序的实现:http://blog.csdn.net/Solstice/archive/2006/09/13/1217700.aspx http://squall.cs.ntou.edu.tw/cprog/Assignments/99Fall/FindGivenSum.html http://blog.chinaunix.net/u2/76292/showart_1359876.html(这个是不正确的) 我也简单的实现了下:(g++编译,主要思路是利用等差数列求和公式n=(a+a+k)*(k+2)/2列举k,找到合适的a) #include <iostream>
#include <math.h>
using namespace std;
int main(){
int n;
cout<<"input n:"<<endl;
cin>>n;
int maxk=(int)sqrt((double)n*2);
int flag = 0;
//cout<<"maxk="<<maxk<<endl;
//for(int i=1;i<maxk+1;i++){
for(int i=maxk+1;i>0;i--){
double a = n/(double)(i+1)-i/(double)2;
//cout<<"a="<<a<<endl;
if(a>0&&a-(int)a==0){
//cout<<i<<endl;
for(int j=0;j<i+1;j++){
cout<<a+j<<" ";
}
cout<<endl;
flag=1;
}
}
if(flag==0){
cout<<"NONE"<<endl;
}
return 0;
}
事实上,媒体也非常认可这两份由专家为总统写作的简历,包括ABCNews,并将他们作为介绍总统候选人的核心提要。像其他的高端工作职位一样,只有更加有力地呈现自己作为候选人的资格,您才能成功获得工作职位。这就像美国的总统竞选一样。 这两份出色的简历,首先是非常简洁。人力资源经理,招聘经理或者猎头顾问没有时间去翻阅每个申请人的详细简历。因此要保持你的简历在1-2页之间,最多不超过2页。要把重点放在你最近15年的工作经历,对于工作经历丰富的高端候选人,你可能还需要对以往的一些工作做出简短的总结性说明。 其次,确保简历的整体布局清晰明白。简历要给人一个良好的视觉吸引力。简历的格式要编排有序。字体大小选择要正确,避免留下太多的空白,也要避免把一页纸塞得太满。整体效果上,简历应该突出高品位和专业化的特点。 这两个人的简历虽然没有得到官方的认可,但是我们认为这两份由专家写作的简历不仅呈现了他们卓越的任职资格,而且突出了他们最优秀的职业成就。这也是简历专家建议候选者首先要在简历中呈现的。因为卓越的任职资格和最优秀的职业成就是简历的核心内容。简历的大部分内容应当呈现你过去或现在的工作职责和“专业经验”标题下的职业成就。典型来讲,职业经验是以逆时序排列,从你最近的职位开始,列出公司名称,雇佣日期,担当的职务,也包括每一个职务你的重点工作内容和职责的简要概述。 最有效的简历,也应该包括每个工作所取得的工作成绩。工作成绩或者工作成就可以通过简要的,针对性的陈述来呈现,要证明你如何增加销售,降低费用,扩大市场份额等。
极光是南北极地区特有的一种大气发光现象。极光在东西方的神话传说中都留下了美丽的身影,现代科学的发展,使人类能够用理性的眼光看待极光,对它作出科学的解释。 长期以来,极光的成因机理未能得到满意的解释。在相当长一段时间内,人们一直认为极光可能是由以下三种原因形成的。一种看法认为极光是地球外面燃起的大火,因为北极区临近地球的边缘,所以能看到这种大火。另一种看法认为,极光是红日西沉以后,透射反照出来的辉光。还有一种看法认为,极地冰雪丰富,它们在白天吸收阳光,贮存起来,到夜晚释放出来,便成了极光。总之,众说纷纭,无一定论。直到20世纪60年代,将地面观测结果与卫星和火箭探测到的资料结合起来研究,才逐步形成了极光的物理性描述。 现在人们认识到,极光一方面与地球高空大气和地磁场的大规模相互作用有关,另一方面又与太阳喷发出来的高速带电粒子流有关,这种粒子流通常称为太阳风。由此可见,形成极光必不可少的条件是大气、磁场和太阳风,缺一不可。具备这三个条件的太阳系其他行星,如木星和水星,它们的周围,也会产生极光,这已被实际观察的事实所证明。 地磁场分布在地球周围,被太阳风包裹着,形成一个棒槌状的胶体,它的科学名称叫做磁层。为了更形象化,我们打这样一个比方。可以把磁层看成一个巨大无比的电视机显像管,它将进入高空大气的太阳风粒子流汇聚成束,聚焦到地磁的极区,极区大气就是显像管的荧光屏,极光则是电视屏幕上移动的图像。但是,这里的电视屏幕却不是 18英寸或 24英寸,而是直径为4000公里的极区高空大气。通常,地面上的观众,在某个地方只能见到画面的l/50。在电视显像管中,电子束击中电视屏幕,因为屏上涂有发光物质,会发射出光,显示成图像。同样,来自空间的电子束,打入极区高空大气层时,会激发大气中的分子和原子,导致发光,人们便见到了极光的图像显示。在电视显像管中,是一对电极和一个电磁铁作用于电子束,产生并形成一种活动的图像。在极光发生时,极光的显示和运动则是由于粒子束受到磁层中电场和磁场变化的调制造成的。 极光不仅是个光学现象,而且是个无线电现象,可以用雷达进行探测研究,它还会辐射出某些无线电波。有人还说,极光能发出各种各样的声音。极光不仅是科学研究的重要课题,它还直接影响到无线电通信,长电缆通信,以及长的管道和电力传送线等许多实用工程项目。极光还可以影响到气候,影响生物学过程。当然,极光也还有许许多多没有解开的谜。 极光被视为自然界中最漂亮的奇观之一。如果我们乘着宇宙飞船,越过地球的南北极上空,从遥远的太空向地球望去,会见到围绕地球磁极存在一个闪闪发亮的光环,这个环就叫做极光卵。由于它们向太阳的一边有点被压扁,而背太阳的一边却稍稍被拉伸,因而呈现出卵一样的形状。极光卵处在连续不断的变化之中,时明时暗,时而向赤道方向伸展,时而又向极点方向收缩。处在午夜部分的光环显得最宽最明亮。长期观测统计结果表明,极光最经常出现的地方是在南北磁纬度67度附近的两个环带状区域内,分别称作南极光区和北极光区。在极光区内差不多每天都会发生极光活动。在极光卵所包围的内部区域,通常叫做极盖区,在该区域内,极光出现的机会反而要比纬度较低的极光区来得少。在中低纬地区,尤其是近赤道区域,很少出现极光,但并不是说压根儿观测不到极光。1958年2月10日夜间的一次特大极光,在热带都能见到,而且显示出鲜艳的红色。这类极光往往与特大的太阳耀斑暴发和强烈的地磁暴有关。 在寒冷的极区,人们举目瞭望夜空,常常见到五光十色,千姿百态,各种各样形状的极光。毫不夸大地说,在世界上简直找不出两个一模一样的极光形体来,从科学研究的角度,人们将极光按其形态特征分成五种:一是底边整齐微微弯曲的圆弧状的极光孤;二是有弯扭折皱的飘带状的极光带;三是如云朵一般的片朵状的极光片;四是面纱一样均匀的帐幔状的极光幔;五是沿磁力线方向的射线状的极光芒。 极光形体的亮度变化也是很大的,从刚刚能看得见的银河星云般的亮度,一直亮到满月时的月亮亮度。在强极光出现时,地面上物体的轮廓都能被照见,甚至会照出物体的影子来。最为动人的当然是极光运动所造成的瞬息万变的奇妙景象。我们形容事物变得快时常说:“眼睛一眨,老母鸡变鸭。”极光可真是这样,翻手为云,覆手为雨,变化莫测,而这一切又往往发生在几秒钟或数分钟之内。极光的运动变化,是自然界这个魔术大师,以天空为舞台上演的一出光的活剧,上下纵横成百上千公里,甚至还存在近万公里长的极光带。这种宏伟壮观的自然景象,好像沾了一点仙气似的,颇具神秘色彩。令人叹为观止的则是极光的色彩,早已不能用五颜六色去描绘。说到底,其本色不外乎是红、绿、紫、蓝、白、黄,可是大自然这一超级画家用出神入化的手法,将深浅浓淡、隐显明暗一搭配、一组合,好家伙,一下子变成了万花筒啦。
看清楚了,这可是154公斤。 这可是我们国家老鼠的强项 “刘翔”
每个Java程序员都要处理异常,异常处理在应用程序中起着重要的作用。在Java世界中,处理异常看上去不是那么简单,不仅仅是在异常发生的地方,优雅的报告异常。 使用ThreadGroup处理异常 ThreadGroup类中在这个时候大部分方法已经作废了,但是该类还是很有用的。ThreadGroup类有一个方法uncaughtException(Thread,Throwable),意思是:当线程组中的线程因为异常而终止时,Java虚拟机调用该方法。这样你可以考虑继承ThreadGroup类来处理你应用中没有捕获的异常。 public class AppSpecificThreadGroup extends ThreadGroup { public void uncaughtException(Thread, Throwable) { // app specific error handling here // ex: if fatal, release resources // show user dialog } } 这样在你的应用启动的时候,把你的线程加入到线程组(Thread group)中。 public class ApplicationMain { public static void main(String[] args) { Runnable r = new ApplicationStarter(args); ThreadGroup g = new AppSpecificThreadGroup(); Thread t = new Thread(g, r); t.start(); } private static void doStart(String[] args) { // start application here... } private static class ApplicationStarter { private String[] args; ApplicationStarter(String[] args) { this.args = args; } public void run() { doStart(args); } } }
使用Java5的Thread.UncaughtExceptionHandler
当认识到ThreadGroup.uncaughtException(Thread,Throwable)方法的巨大作用时,Sun团队在Java5版本站中增强了未捕获的异常处理的功能,引入了Thread.UncaughtExceptionHandler接口,另外这个接口引入了两个新的”Check points”用于处理异常。Thread类有两个属性-uncaughtExceptionHandler和defaultUncaughtExceptionHandler,它们有相应的get/set方法。但是这两个方法是不同的。让我们看看处理异常的过程。
n 使用uncaughtExceptionHandler为当前线程处理异常。
n 如果uncaughtExceptionHandler为空(null),使用线程组(如果用户没有显示设置,线程组是uncaughtExceptionHandler的默认实现)。
n 如果线程组是默认的实现,但是不是根线程组,将把错误处理委派给父线程组。
n 如果线程组是默认的实现,并且是根线程组,将使用线程(Thread)类上设置的defaultUncaughtExceptionHandler方法。
n 如果在线程类没有设置defaultUncaughtExceptionHandler,将调用Java5之前的ThreadGroup类中的未捕获异常方法。
实现Thread.UncaughtExceptionHandler接口看起来也像上面实现ThreadGroup接口。 public class AppSpecificExceptionHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread, Throwable) { // app specific error handling here // ex: if fatal, release resources // show user dialog } }
在应用程序中插入如下: public class ApplicationMain { public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler(new AppSpecificExceptionHandler()); } }
Swing异常处理实现
许多人没有注意到现有的JOptionPanel类,该类中有许多的优美的弹出对话框的静态方法(showOptionDialog,showConfirmDialog,showInputDialog,和showMessageDialog),这些方法接受的参数不仅仅是字符串。这些静态方法(也包括JOptionPanel构造函数)都是接受一个Object作为参数的的。下面是一个实现该类的核心代码:
public class ErrorDialog {
public ErrorDialog() {
super();
}
public static void showQuickErrorDialog(JFrame parent, Exception e) {
final JTextArea textArea = new JTextArea();
textArea.setFont(new Font("Sans-Serif", Font.PLAIN, 10));
textArea.setEditable(false);
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
textArea.setText(writer.toString());
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setPreferredSize(new Dimension(350, 150));
JOptionPane.showMessageDialog(parent, scrollPane,
"An Error Has Occurred", JOptionPane.ERROR_MESSAGE);
}
}
在程序中的一个例图如下:
使用InputVerifier设计Swing校验包 尽管有许多针对Swing的校验框架,并且Swing中也有API InputVerifier用于数据校验,InputVerifier易于使用并且容易定制。本文将在InputVerifier的基础上构建一个可用的校验包,可以在其它的Swing应用之中。 InputVerifier类在javax.swing包中。它有一个抽象方法,我们需要实现一个叫verify()的方法,这个方法接受一个JComponent作为参数,返回值为true或false。一旦我们实现了verify()方法,我们可以在JTextField中使用如下: JTextField text = new JTextField(); Text.setInputVerifier(new MyVerifier()); 然而,InputVerifier本身并没有什么意思。它的作用是防止用户输入错误的信息在组件中,阻止用户把输入光标移动到其他组件上。这种方式令人感到厌烦并且用户不知道错误所在。InputVerifier并没有提供一些更好的功能,所以我们需要构建一个如下校验包: 1. 如果数据不正确时,可以改变输入组件的背景颜色。 2. 如果数据不正确时,可以弹出消息提示用户错误出在什么地方,应该怎么处理。 3. 我们需要通知Form窗体每个组件的校验结果,这样的话Form窗体可以执行相应的动作。(例如启动”OK”按钮) 4. 如果用户改正了组件的错误数据,则错误提示信息应消失。 我们的包中包含一个抽象类,这个抽象类处理了大部分的工作,我们只需要实现一个方法,决定使用什么规则来进行数据校验;同时也提供一个接口,如果需要校验的Form窗体需要校验结果通知Form窗体,则可以实现该接口。好了,我们现在可以设计这个接口。 WantsValidationStatus接口 下面的接口可以提供一种方式用于出发Form窗体的制定事件。 package ica.swing.validation; public interface WantsValidationStatus { void validateFailed(); // Called when a component has failed validation. void validatePassed(); // Called when a component has passed validation. } 实现这个接口可以通知Form窗体校验状态,但是实现该接口不是必须的。我们也可以不是实现这个接口,如果校验失败不做任何事情。但是,我认为这样的设计是比较优美的。接下来看看我们的抽象类。 AbstractValidator类 AbstarctValidator处理了显示出错提示,改变出错背景,通知Form窗体等许多工作。只留下一个抽象的方法待实现,protected abstract Boolean validateCriteria(JComponent c),这个方法我们需要提供自己的校验规则用于校验组件。以下是抽象类: protected abstract boolean validationCriteria(JComponent c); public boolean verify(JComponent c) { if (!validationCriteria(c)) { c.setBackground(Color.PINK); //messageLabel.setSize(0,0); //messageLabel.setBackground(color); point = c.getLocation(); double x =point.getX(); double y = point.getY(); Point p = new Point(); p.setLocation(x,y+c.getHeight()); tooltip= new HalfOpaqueToolTip( message, new Color( 250 , 250 , 200 ), Color.RED, Color.BLACK, 1,dialog,p ); c.addMouseListener(tooltip); return false; } Verify()方法 当需要校验是调用方法boolean verify(JComponent c),下面用一例子示之。 public class ToolTipsEmptyValidator extends ToolTipsAbstractValidator { public ToolTipsEmptyValidator(JDialog dialog, JTextField c, String message) { super(dialog, c, message); } public ToolTipsEmptyValidator(JFrame dialog, JTextField c, String message) { super(dialog, c, message); } public ToolTipsEmptyValidator(JDialog dialog, JTextArea c, String message) { super(dialog, c, message); } public ToolTipsEmptyValidator(JFrame dialog, JTextArea c, String message) { super(dialog, c, message); } @Override protected boolean validationCriteria(JComponent c) { if (c instanceof JTextField){ if (((JTextField) c).getText().equals("")) return false; } else if (c instanceof JTextArea){ if (((JTextArea) c).getText().equals("")) return false; } return true; } } 调用方法如下: ToolTipsEmptyValidator validator1 = new ToolTipsEmptyValidator(jDialog,localdbPanel.getIpAddressField(),"Error,IP Address can't be empty"); boolean flag1 = validator1.verify(localdbPanel.getIpAddressField()); 下图是使用该校验包的示例。
向导在今天的桌面应用中非常常用。向导应该是个什么样子呢?相信你应该很清楚,因为你使用过很多的向导。也许你使用过一些安装程序向导或是一些程序的配置向导。这篇文章,我们会创建一个简单的向导框架。 一个向导包括很多Panel,每个Panel里面包含用户的配置组件或是文本域或是选择框等。用户点击”Next”或是”Back”按钮,在各个Panel之间切换,输入需要的信息。注意的是,当最后一个Panel是”Next”按钮要变成”Finish”按钮,并且再次按下的时候向导关闭。在向导关闭的时,发起向导的类要得到向导Panel中所有的数据。在任何情况下,用户可以点击”Cancel”按钮关闭向导并丢弃前面所填的所有数据。 看上去很简单是吗?对的,但是有些设计细节我们需要考虑。 第一,向导中的每个Panel不是都需要访问的,换句话说,如果向导包含1到5个Panel,点击”Next”按钮可以从第一个Panel依次到第五个Panel,但是有时候可能由于用户的选项直接从第一个Panel跳到第五个Panel. 而且还有的情况是,假设向导中需要连接到远程服务器或是远程数据库,如果连接不上的,那么就不能到达下一个Panel。这样向导中的Panel就像是树形,你从树的根开始,通过不同的分支到达叶子节点,这时”Next”按钮变成”Finish”按钮。 第二,有些时候Next按钮和Back按钮需要禁用。比如,第一个Panel出现的时候,back按钮应该禁用,因为没有上一个Panel。另外,当有些值必须输入的时候,没有输入的情况下Panel中的Next按钮应该为禁用。 第三,输入的数据需要一直保持到用户完成向导或是取消。因为当用户点击Back按钮会到上一个Panel时,上一个Panel填写的数据应该能够保持,并且再次使用Next按钮时,本Panel中的数据也应该保持。 有了这些设计细节,我们可以考虑设计自己的向导了。我们先规划下我们将要完成的一些类。 Wizard-这个类包含模型(model)和控制器(controller),其主要是一个对话框(JDialog),并且包含有Next,Back和Cancel按钮。还有一个使用CardLayout布局管理的大组件,可以把各个Panel显示在上面。想下图的样子: Java.awt.Componet的子类,这个类一般是继承了java.awt.Componet,通常是一个javax.swing.JPanel.这个类是用于显示在wizard类中的大组件位置。下图是其中一个Panel。 WizardPanelDescriptor-这第三个类用于关联wizard和panel。这个需要类用户继承,并用于标识Panel.这个类指定了访问下一个和前一个Panel的规则,并且在Panel的显示前,现实中,和显示后执行相应的动作。 Wizard 首先我们需要创建一个用于显示向导对话框本身,它包含有三个按钮Back,Next,Cancel.一般这些按钮是按照从左到右的顺序分布的,另外Cancel按钮要离其他的两个按钮远一点,这样防止用户不小心点击到Cancel按钮。接下来,就用需要一个布局,可以在同一个区域显示各个Panel,在AWT中有CardLayout布局。 在这个设计中,我们使用一个简单的方法来检测我们的数据。下面我们看看Wizard类: Public Class Wizard{ private WizardModel wizardModel; private WizardController wizardController; private JDialog wizardDialog; private JPanel cardPanel; private CardLayout cardLayout; private JButton backButton; private JButton nextButton; private JButton cancelButton; private MainFrame mainFrame; private int returnCode; public Wizard(MainFrame owner) { this.mainFrame = owner; wizardModel = new WizardModel(); wizardDialog = new JDialog(owner); Point np = owner.getLocation(); wizardDialog.setLocation(np); initComponents(); } } 注意到initComponents()方法,这个方法是用于布置界面中的组件和按钮的,并且把按钮事件关联到控制器中。 private void initComponents() { JPanel buttonPanel = new JPanel(); Box buttonBox = new Box(BoxLayout.X_AXIS); cardPanel = new JPanel(); cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); cardLayout = new CardLayout(); cardPanel.setLayout(cardLayout); backButton = new JButton(); nextButton = new JButton(); cancelButton = new JButton(); backButton.setActionCommand(BACK_BUTTON_ACTION_COMMAND); nextButton.setActionCommand(NEXT_BUTTON_ACTION_COMMAND); cancelButton.setActionCommand(CANCEL_BUTTON_ACTION_COMMAND); buttonPanel.setLayout(new BorderLayout()); buttonPanel.add(separator, BorderLayout.NORTH); buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); buttonBox.add(backButton); buttonBox.add(Box.createHorizontalStrut(10)); buttonBox.add(nextButton); buttonBox.add(Box.createHorizontalStrut(30)); buttonBox.add(cancelButton); buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST); wizardDialog.getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH); wizardDialog.getContentPane().add(cardPanel, java.awt.BorderLayout.CENTER); } 接下来,我们要把Componet Panel注册到Wizard中。Wzard中使用registerWizardPanel()方法。我们知道CardLayout布局中包含有next(),previous()这样的方法来回翻动Panel,然而我们需要的是树形结构,而不是线性的结构,因此我们需要使用标识符来标识各个Panel对象。 public void registerWizardPanel(Object id, WizardPanelDescriptor panel) { cardPanel.add(panel.getPanelComponent(), id); wizardMdel.registerPanel(id, panel); } 最后wizard中是用setCurrentPanel(Object id)来设置,Wizard初始化时显示的第一个Panel。剩下的就是一些事件处理,比较简单。Wizard中大量的使用Wizard来保存数据,并使用WizardController来处理对话框本身的事件。 WizardPanelDescriptor 注册到Wizard的每个方法都需要继承WizardPanelDescriptor类,这个类包含一些方法可以把组件集成到Wizard向导中。以下的四个方法:是访问组件和组件对象标识符的方法。 public final void setPanelComponent(Component panel) { targetPanel = panel; } public final Component getPanelComponent() { return targetPanel; } public final Object getPanelDescriptorIdentifier() { return panelIdentifier; } public final void setPanelDescriptorIdentifier(Object id) { panelIdentifier = id; } 下面是比较重要的一部分,就是每个继承类都需要改写的一些方法。包括控制Next,Back之后显示的Panel。在每次Panel初始化的时候都会执行Next这个方法,当Next和Back方法中返回的是null之的时候Next和Back按钮被禁用。因此,Next方法不能用于数据的校验,需要有另外的方法,在这里使用了一个Validator方法,当然如果数据需要校验,也需要在WizardPanelDescriptor子类中进行覆盖。具体如下: public Object getNextPanelDescriptor() { return null; } public Object getBackPanelDescriptor() { return null; } public boolean validator(){ return true; } 另外提供三个方法,用于控制Panel显示前,显示中和显示后的事件。 public void aboutToDisplayPanel() { } public void displayingPanel() { } public void aboutToHidePanel() { } WizardPanelDescriptor 最后,用上图来表示Wizard类和其他几个类之间的关系图,并展示了两个实例。
ci-bayes实现了两种贝叶斯分类方法:a Navie implementation 和 a Fisher implementation,是Toby segaran's 的书"Programming Collective Intelligence"的一部分的Java实现版本。 简单的一个例子如下: FisherClassifier fc=new FisherClassifierImpl();
fc.train("The quick brown fox jumps over the lazy dog's tail","good");
fc.train("Make money fast!", "bad");
String classification=fc.getClassification("money"); // should be "bad"
水星(Mercury) 金星(Venus) 地球(Earth) 火星(Mars) 木星(Jupiter) 土星(Saturn) 天王星(Uranus) 海王星(Neptune) 冥王星(Pluto) Twelve Years of Animals( 12生肖) 1.Year of the Rat 鼠年 2.Year of the Ox 牛年 3.Year of the Tiger 虎年 4.Year of the Rabbit 兔年 5.Year of the Dragon 龙年 6.Year of the Snake 蛇年 7.Year of the Horse 马年 8.Year of the Goat 羊年 9.Year of the Monkey 猴年 10.Year of the Rooster 鸡年 11.Year of the Dog 狗年 12.Year of the Boar 猪年 Twelve Constellations (12星座): 1.Aquarius(the Water Carrier)水瓶座 2.Pisces(the Fishes)双鱼座 3.Aries(the Ram)白羊座 4.Taurus(the Bull)金牛座 5.Gemini(the Twins)双子座 6.Cancer(the Crab)巨蟹座 7.Leo(the Lion)狮子座 8.Virgo(the Virgin)处女座 9.Libra(the Scales)天秤座 10.Scorpio(the Scorpion)天蝎座 11.Sagittarius(the Archer)射手座 12.Capricorn(the Goat)山羊座
一下是一些收集的关于动物的习语。有些没有翻译的分两种情况,一是从字面意思很容易看出来,而写出来不一定准确的;另外一类是我还不确定其真正的含义。 Bear 熊 - bear market 熊市-空头市场
- You can't have your cake and eat it too.鱼和熊掌不可兼得
Bird 鸟 - A bird in the hand is worth two in the bush.
- A little bird told me.
- a bird's-eys view鸟瞰
- Birds of a feather flock together.物以类聚
- eat like a bird吃的很少,食量小
- Fine feathers make fine birds.人要衣装,佛要金装。
- give a person the bird喝倒彩
- Kill two birds with one stone.一石二鸟
- the birds and the bees有关性的基本常识
- The early birds catches the worm.早起的鸟儿有虫吃
- newbie菜鸟
Bull,calf,cattle,cow,ox牛 - bull market 牛市-多头市场
- bull session 闲谈,男性之间的谈话
- like a bull in china shop笨手笨脚
- to milk the bull缘木求鱼
- You can't sell the cow and drink the milk.与和熊掌不可兼得
- why buy the cow when the milk is free.
- Take the bull by the horns.勇敢的客服困难,挺身而出
- as dumb as an ox像公牛一般沉默
- as strong as an ox像牛般强壮
- dirnk lisk a fish牛饮
- as stubborn as mule牛脾气
- talk horse吹牛
- to eat like a horse食量大如牛
- to lead a dog's life 牛马不如的生活
- to cast the pearls before swine对牛弹琴
Cat, Kitten猫 - A cat has nine lives.
- cat-and-mouse玩弄,折磨
- cat burglar蜘蛛盗
- cat house妓院
- cat nap/sleep小睡
- Cat got you tongue?舌头打结
- bell the cat做危险的事
- cats and dogs形同水火
- Curiousity killed the cat.勿多管闲事
- Let the cat out of the bag.泄漏机密
- live a cat-and-dog life争吵度日
- It rains cats and dogs.倾盆大雨
- There is not even enough room for swing a cat.无转身之处
- She is a scary cat.很容易受到惊吓
- While the cat is away, the mice will play.猫去鼠嬉
Chicken ,cock,hen,rooster鸡 - a hen party女性聚会
- chicken pox水痘
- chicken out临阵脱逃
- cock-eyed斜眼的
- henpeck对丈夫作威作福
- Which comes first,the chicken or the eggs?
- better to be the head of duck than the ass of a horse宁为鸡头不为凤尾
Dog, doggie, puppy狗 - a dog in the manager 鸠占鹊巢
- an old dog can't learn new tricks
- Barking dogs seldom bite.会叫的狗不咬人
- doggy bag剩菜袋
- dog's life生活艰难
- dog-eat-dog world
- dog tired
- a dog chance极有限的一点希望
- dog's day很热的日子
- dog-eared书页的折角
- dog eat dog无情而野蛮的竞争
- dog sleep小寐
- Every dog has its day.狗也有出头的日子
- Let sleeping dog lie.少惹麻烦
- Love me ,love my dog.爱屋及乌
- put on the dog装腔作势
- red dog beer私家酿酒
- top dog人上人
- paparazzi狗仔队
- bite the hands the feed one
- You can't make a silk purse out of sow's ear.够最终吐不出象牙
Fish鱼 - cold fish冷淡的人
- drink like a fish酗酒
- There are other fish in the sea天涯何处无芳草
Goat,lamb,sheep,kid羊 - as gentle as a lamb像绵羊一样温和
- a black sheep害群之马
- follow like sheep盲目顺从
- separate the sheep from and goats辨别好人与坏人
- Lock the stable door after the horse is stolen.亡羊补牢
Goose鹅 - as silly as goose像鹅般蠢
- Fair for the goose is fair for gander
- goose bump鸡皮疙瘩
- goose egg考零分
- gooseflesh鸡皮疙瘩
- Kill the goose that lays the golden eggs.杀鸡取卵
Hog,pig,swine,sow猪 - road hog挡道的自私驾驶
- bleed like a stuck pig大量出血
- eat like a pig吃的多而且吃相不好
- make a pig of oneself狼吞虎咽
- Pig may fly.Or to milk the bull.
Horse,pony马 - Don't put the cart before the horse.不要本末倒置
- Don't ride the high horse.不要妄自尊大
- Every horse thinks its own pack heaviest.
- It is useless to flog a dead horse.鞭策死马,徒劳无功
- Lock the stable after the horse has gone.亡羊补牢
- Don't look a gift horse in the mouth.受赠之马,勿探其齿
- Zeal without knowledge is runaway horse.
- Don't change horses in mid-stream.
- a horse of a different color
- a dark horse黑马
- from the horse's mouth第一手消息
- horses sense常识
- I can eat a horse.
- one-horse town小镇
- You can lead a horse to water,but you can't make him drink.师傅领进门,修行在个人
- Cruise chicks泡马子
- All lay loads on a willing horse马善被人骑
- leapfrog
- piggyback fight骑马打仗
Lion狮子 - as brave as a lion像狮子般勇敢
- That software designer still the lion's share of the market.那位软件设计师人人握有大部分的市场
- Put one's head into the lion's mouth自愿冒险
Monkey猴子 - monkey/horse/idle/fool around鬼混
- monkey business恶作剧
- monkey on the back某事已成了负担
- Monkey see,monkey de.有样学样
- monkey uncle吃惊
Mouse,rat鼠 - as poor as church mouse
- rat fink告密者
- a rug rat指不会走路的小婴儿
- I can smell the rat.我觉得事有蹊跷。
- Rats!胡说八道,该死,可恶
Snake蛇 - a snake in the grass口蜜腹剑的人
- Once bitten twice shy.一朝被蛇咬,十年怕井绳。
- to cherish a snake in one's bosom养护为患
- crawl蛇行
- overdo it画蛇添足
Tiger,cub虎 - ride the tiger骑虎难下
- the ass in the lions skin狐假虎威
- How can one get the tiger cubs if one does not enter tiger den?不入虎穴,焉得虎子
Other Animal其他 - as big as a whale
- as blind as a bat
- as busy as a bee
- as pround as peacock
- as slow as a turtle
- as slow as a snail
- as sly as a fox
- as stupid as a donkey
- as stubborn as a mule
- as wise as an owl
- butterfly effect连锁反应
- to have or get butterfly in one's stomach非常紧张
- The last straw breaks camel's back.忍无可忍
- crocodile tears不真诚的同情
- eat crow不得已认错
- A deer caught in the headlight.手足无措
- eagle-eyed眼力好
- mad as a March hare
- A leopard can't change his spots.江山易改本性难移。
- pet peeve不能忍受的事
- stag party男人的聚会
- stool pigeon奸细
- One swallow does not make a summer
- swan song最后作品
- white elephant 昂贵而无用的东西。
在刚刚结束的的WWW2008的会议中,有篇超短的paper 《Size Matters: Word Count as a Measure of Quality on Wikipedia》,这篇文章针对Wikipedia的质量的评估,提出了一种方法来评估一个Wikipedia文章的质量,这种方法很简单,就是数文章字数(Word Count)。 作者认为这种方法的好处有如下几点: 1.文章的长度很容易测量; 2.许多其他的方法需要一些其他的信息,而这些信息不是很容易得到; 3.许多其他的方式对用户来说不容易理解; 4.文章的长度评估的方法比其他的复杂的方法更有意义。 (说的好像是一点简单^_^)这确实是一种简单的方法,但是效率怎么样?根据作者的实验,这个方法在二个分类(featured and random)的情况下能得到96.3%的准确率。 当然,作者也比较实事求是,也不会夸大这种方法的作用,作者将寻找新的方法来评估Wikipedia文章质量。 除了这篇文章,其实还有其他几种方法,可以参看下面这些论文: 1.Information Quality Discussion in Wikipedia 2.Assessing Information Quality of a Community-Base Encyclopedia 3.Measuring Wikipedia 4.Wikipedia Article Quality Assessment and Ranking Tips for Users and Search Engine Engineers 这些文章虽然都是关于wiki质量讨论的,但是研究的意义很重大。特别是针对目前网络的信息泛滥的情况下,需要用一种方式来评估信息的有用性,这样可以让我们更轻松找到我们需要的有用信息,同样搜索引擎如果能判断提供给我们的文章的质量好坏,这也就免去了我们很多时间去挑选了。 对于推荐系统(如新闻、文章推荐系统),用户很少评分的情况下,可以采用这种方法自动给项目评分。
|