记一次线上操作bug

身为程序猿,可以说天天都会遇到bug,今天没为什么记下这次bug呢?说来惭愧,因为这次bug是由于自己不仔细没有仔细检查没有测试就对线上数据下手造成的,一方面是记下这个bug的由来,修复方法和犯下的失误的地方,另一方面也是留下记录警示自己操作线上数据一定要小心再小心,还有就是不要对自己过于自信,测试很重要。 先说一下bug的缘由,19号晚上我们上线了一个新功能,有一个功能模块是另外一个同事负责的,所以对其实现不是很了解,但数据导入有老夫负责,所以数据导入的时候,有一个结束时间没有考虑清楚,只有日期没有时间(产品经理和另一位同事当时也没有给我说),所以数据库里面结束时间变成了默认的“00:00:00”,本来修起来应该很简单,读出来update一下时间就好了,但由于是部门间的协作,比较麻烦,就考虑用SQL解决,所以就写出了如下的SQL: CREATE TABLE t_goods_bak AS SELECT REPLACE(a.endtime,'00:00:00','23:59:59') end_time,a.* FROM t_goods a; ALTER TABLE `commercialization`.`t_goods_bak` CHANGE `id` `id` INT(11) DEFAULT 0 NOT NULL FIRST, CHANGE `end_time` `end_time` DATETIME CHARSET utf8 COLLATE utf8_general_ci NOT NULL AFTER `endtime`, CHANGE `price` `price` DECIMAL(10,2) NOT NULL COMMENT '商品单价' AFTER `end_time`; ALTER TABLE `commercialization`.`t_goods_bak` DROP COLUMN `endtime`; ALTER TABLE `commercialization`.`t_goods_bak` CHANGE `end_time` `endtime` VARCHAR(19) CHARSET utf8 COLLATE utf8_general_ci DEFAULT " NOT NULL COMMENT '商品失效时间'; DROP TABLE `t_goods_bak`; RENAME TABLE `commercialization`.`t_goods_bak` TO `commercialization`.`t_goods`; 整体思想就是新建一张表,在新建这张表的时候,把数据修对,修对的数据放在了新添加的end_time字段,然后把这张新表t_goods_bak修改成和原来的表一致,最后把原表删除,再把这张表改一下名字,就达到了替换以前表的目的,所以就OK,看到这里也许有同学已经发现问题了:先别OK,你这新表没主键啊!!! 对,老夫当时就没有多想,以为就此OK了,所以就出现bug了,因为我没有仔细看SQL语句(这些SQL除了,第一句之外都是自动生成的),新表根本没主键,这还不是问题的关键,仔细看第二句SQL,id字段默认是0,所以所有插入的数据,默认值都是0,因为没有自增,这就是最为关键的两个问题。所以综上所述,关于修这个bug,老夫至少忘了如下几件事: ...

April 24, 2016 · 1 分钟 · Bridge Li

Spring和websocket整合应用示例(下)

在上篇中,我们已经实现了websocket,但还有一个核心的业务实现类没有实现,这里我们就实现这个业务核心类,因为老夫参与的这个系统使用websocket发送消息,所以其实现就是如何发送消息了。 NewsListenerImpl的实现 package cn.bridgeli.websocket; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import cn.bridgeli.DateUtil; import cn.bridgeli.enumeration.PlatNewsCategoryType; import cn.bridgeli.model.PlatNewsVo; import cn.bridgeli.model.SearchCondition; import cn.bridgeli.quartz.impl.TimingJob; import cn.bridgeli.service.PlatNewsService; import org.apache.commons.lang.StringUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.socket.TextMessage; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Description : 站内消息监听器实现 * @Date : 16-3-7 */ @Component public class NewsListenerImpl implements NewsListener{ private static final Logger logger = LoggerFactory.getLogger(NewsListenerImpl.class); Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); //线程池 private ExecutorService executorService = Executors.newCachedThreadPool(); //任务调度 private SchedulerFactory sf = new StdSchedulerFactory(); @Autowired private PlatNewsService platNewsService; @Override public void afterPersist(PlatNewsVo platNewsVo) { logger.info("监听到有新消息添加。。。"); logger.info("新消息为:"+gson.toJson(platNewsVo)); //启动线程 if(null != platNewsVo && !StringUtils.isBlank(platNewsVo.getCurrentoperatoremail())){ //如果是定时消息 if(platNewsVo.getNewsType() == PlatNewsCategoryType.TIMING_TIME.getCategoryId()){ startTimingTask(platNewsVo); //定时推送 }else{ //立即推送 executorService.execute(new AfterConnectionEstablishedTask(platNewsVo.getCurrentoperatoremail())); } } } @Override public void afterConnectionEstablished(String email) { logger.info("建立websocket连接后推送新消息。。。"); if(!StringUtils.isBlank(email)){ executorService.execute(new AfterConnectionEstablishedTask(email)); } } /** * @Description : 如果新添加了定时消息,启动定时消息任务 * @param platNewsVo */ private void startTimingTask(PlatNewsVo platNewsVo){ logger.info("开始定时推送消息任务。。。"); Date timingTime = platNewsVo.getTimingTime(); if(null == timingTime){ logger.info("定时消息时间为null。"); return; } logger.info("定时推送任务时间为:"+DateUtil.date2String(timingTime)); JobDetail jobDetail= JobBuilder.newJob(TimingJob.class) .withIdentity(platNewsVo.getCurrentoperatoremail()+"定时消息"+platNewsVo.getId(), "站内消息") .build(); //传递参数 jobDetail.getJobDataMap().put("platNewsService",platNewsService); jobDetail.getJobDataMap().put("userEmail",platNewsVo.getCurrentoperatoremail()); Trigger trigger= TriggerBuilder .newTrigger() .withIdentity("定时消息触发"+platNewsVo.getId(), "站内消息") .startAt(timingTime) .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(0) //时间间隔 .withRepeatCount(0) //重复次数 ) .build(); //启动定时任务 try { Scheduler sched = sf.getScheduler(); sched.scheduleJob(jobDetail,trigger); if(!sched.isShutdown()){ sched.start(); } } catch (SchedulerException e) { logger.info(e.toString()); } logger.info("完成开启定时推送消息任务。。。"); } } /** * @Description : 建立websocket链接后的推送线程 */ class AfterConnectionEstablishedTask implements Runnable{ String email ; public AfterConnectionEstablishedTask(String email){ this.email = email; } @Override public void run() { logger.info("开始推送消息给用户:"+email+"。。。"); if(!StringUtils.isBlank(email)){ SearchCondition searchCondition = new SearchCondition(); searchCondition.setOperatorEmail(email); JSONArray jsonArray = new JSONArray(); for(PlatNewsCategoryType type : PlatNewsCategoryType.values()){ searchCondition.setTypeId(type.getCategoryId()); int count = platNewsService.countPlatNewsByExample(searchCondition); JSONObject object = new JSONObject(); object.put("name",type.name()); object.put("description",type.getDescription()); object.put("count",count); jsonArray.add(object); } if(null != jsonArray && jsonArray.size()>0){ UserSocketVo userSocketVo = WSSessionLocalCache.get(email); TextMessage reMessage = new TextMessage(gson.toJson(jsonArray)); try { if(null != userSocketVo){ //推送消息 userSocketVo.getWebSocketSession().sendMessage(reMessage); //更新推送时间 userSocketVo.setLastSendTime(DateUtil.getNowDate()); logger.info("完成推送新消息给用户:"+userSocketVo.getUserEmail()+"。。。"); } } catch (IOException e) { logger.error(e.toString()); logger.info("站内消息推送失败。。。"+e.toString()); } } logger.info("结束推送消息给"+email+"。。。"); } } } 这个类就是websocket的核心业务的实现,其具体肯定和业务相关,由于业务的不同,实现肯定不同,因为老夫参与的系统是发送消息,所以里面最核心的一句就是: ...

April 4, 2016 · 2 分钟 · Bridge Li

Spring和websocket整合应用示例(上)

嗯,这次真的仅仅是一个入门教程,因为老夫表示自己也不会。近期老夫参与开发公司的一个CRM系统,系统中有很多消息的推送,由一个同事负责,其用到了websocket技术,老夫比较感兴趣,删繁就简,整理了一个教程,留作自己笔记,因很多原理老夫也是不甚了了,以备将来用到了有资料可查。 maven依赖 <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>4.0.1.RELEASE</version> </dependency> spring-servlet的配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> ...... <!-- websocket --> <bean id="websocket" class="cn.bridgeli.websocket.WebsocketEndPoint"/> <websocket:handlers> <websocket:mapping path="/websocket" handler="websocket"/> <websocket:handshake-interceptors> <bean class="cn.bridgeli.websocket.HandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers> </beans> 其中,path对应的路径就是前段通过ws协议调的接口路径 HandshakeInterceptor的实现 package cn.bridgeli.websocket; import cn.bridgeli.utils.UserManager; import cn.bridgeli.util.DateUtil; import cn.bridgeli.sharesession.UserInfo; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import java.util.Date; import java.util.Map; /** * @Description :创建握手(handshake)接口 * @Date : 16-3-3 */ public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{ private static final Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class); @Override public boolean beforeHandshake(ServerHttpRequest request,ServerHttpResponse response, WebSocketHandler wsHandler, Map<String,Object> attributes) throws Exception { logger.info("建立握手前..."); ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); UserInfo currUser = UserManager.getSessionUser(attrs.getRequest()); UserSocketVo userSocketVo = new UserSocketVo(); String email= ""; if(null != currUser){ email = currUser.getEmail(); } if(StringUtils.isBlank(email)){ email = DateUtil.date2String(new Date()); } userSocketVo.setUserEmail(email); attributes.put("SESSION_USER", userSocketVo); return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { logger.info("建立握手后..."); super.afterHandshake(request, response, wsHandler, ex); } } 因为老夫不是很懂,所以最大限度的保留原代码,这其实就是从单点登录中取出当前登录用户,转成UserSocketVo对象,放到Map中。所以接下来我们看看UserSocketVo对象的定义 ...

April 4, 2016 · 3 分钟 · Bridge Li

MalformedByteSequenceException: 3 字节的 UTF-8 序列的字节 2 无效

今天这篇文章比较简单,写一个老夫近期工作中遇到的一个问题,这个问题困扰了老夫几个月了,虽然借助强大的Google百度了好久,但一直没有彻底解决,一直感觉挺简单一问题,也挺常见的一问题(网上问这个问题的还挺多),怎么就没有一个靠谱点的解决方案呢,刚好上个周呢时间稍有空闲,于是仔细研究了一下,终于找到了问题的根源,然后同事一言点醒梦中人,豁然开朗,一举解决,所以记录一下,先说一下异常:MalformedByteSequenceException: 3 字节的 UTF-8 序列的字节 2 无效,详细的堆栈信息如下: 严重: StandardWrapper.Throwable org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from file [D:J2EEapache-tomcat-7.0.62webappscrm-v1.0WEB-INFclassesspring-kafka-consumer.xml]; nested exception is com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 字节的 UTF-8 序列的字节 2 无效。 at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:409) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:335) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:540) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:454) at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658) at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:624) at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:672) at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:543) at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484) at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136) at javax.servlet.GenericServlet.init(GenericServlet.java:158) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197) at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:864) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:957) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 字节的 UTF-8 序列的字节 2 无效。 at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:687) at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:408) at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1735) at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.scanData(XMLEntityScanner.java:1234) at com.sun.org.apache.xerces.internal.impl.XMLScanner.scanComment(XMLScanner.java:778) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanComment(XMLDocumentFragmentScannerImpl.java:1038) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2988) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243) at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:347) at org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoader.java:76) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(XmlBeanDefinitionReader.java:428) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) ... 35 more 三月 16, 2016 5:41:59 下午 org.apache.catalina.core.StandardWrapperValve invoke 严重: Allocate exception for servlet springServlet com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 3 字节的 UTF-8 序列的字节 2 无效。 at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:687) at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:408) at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1735) at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.scanData(XMLEntityScanner.java:1234) at com.sun.org.apache.xerces.internal.impl.XMLScanner.scanComment(XMLScanner.java:778) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanComment(XMLDocumentFragmentScannerImpl.java:1038) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2988) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243) at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:347) at org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoader.java:76) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(XmlBeanDefinitionReader.java:428) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:335) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:540) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:454) at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658) at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:624) at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:672) at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:543) at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484) at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136) at javax.servlet.GenericServlet.init(GenericServlet.java:158) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197) at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:864) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:957) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) 看完堆栈信息,我相信遇到这个问题的同学,已经知道咋回事了。说起来也很简单,就是tomcat跑一个web项目时,报这个异常,导致项目起不来。在仔细看之后,应该是编码问题导致的,对,就是编码的问题了,看报错的那个XML文件编译之后的情况如下: ...

March 20, 2016 · 2 分钟 · Bridge Li

POI解析Excel示例

在Java的世界里,对于解析Excel,目前市场上有两个不错的框架,一个是jxl另一个是poi,之前老夫曾对jxl可以说是倍加赞赏(当时老夫还为了它而写了一篇文章,详见这里),因为一直认为它虽然有bug,虽然兼容性不好,但是它简单易用啊,只要自己够仔细认真就能避开这些坑,但是从这周起,老夫决定jxl一生黑,因为随着时间的推移,现在Excel的版本越来越新,而jxl只支持2003之前的版本,可以说解析起来异常麻烦,而poi有Apache做保证,表现越来越好,使用起来其实也不是很复杂,所以老夫决定之后再次遇到解析Excel的只用poi。 下面是老夫写的一个解析Excel的一个工具类,希望对大家有所帮助。 解析Excel所需的类库的maven依赖 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.14</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.14</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> 解析的具体方法 package cn.bridgeli.demo; import java.io.FileInputStream; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.io.FilenameUtils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; public class ExcelReader { protected static final String dateTimeFmtPattern = "yyyy-MM-dd HH:mm:ss"; protected static final String dateFmtPattern = "yyyy-MM-dd"; protected static final DataFormatter formatter = new DataFormatter(); @Test public void testReader() throws Exception { List<Map<String, readExcel("E readExcel("E:/test1.xls"); List<Map<String, String>> list2 = readExcel("E:/test1.xlsx"); } /** * 读取excel文件(同时支持2003和2007格式) * * @param fileName * 文件名,绝对路径 * @return list中的map的key是列的序号 * @throws Exception * io异常等 */ public static List<Map<String, String>> readExcel(String fileName) throws Exception { FileInputStream fis = null; Workbook wb = null; List<Map<String, String>> list = null; try { String extension = FilenameUtils.getExtension(fileName); fis = new FileInputStream(fileName); list = read(fis, extension); return list; } finally { if (null != wb) { wb.close(); } } if (null != fis) { fis.close(); } } /** * 读取excel文件(同时支持2003和2007格式) * * @param fis * 文件输入流 * @param extension * 文件名扩展名: xls 或 xlsx 不区分大小写 * @return list中的map的key是列的序号 * @throws Exception * io异常等 */ public static List<Map<String, String>> read(FileInputStream fis, String extension) throws Exception { Workbook wb = null; List<Map<String, String>> list = null; try { if ("xls".equalsIgnoreCase(extension)) { wb = new HSSFWorkbook(fis); } else if ("xlsx".equalsIgnoreCase(extension)) { wb = new XSSFWorkbook(fis); } else { throw new Exception("file is not office excel"); } list = readWorkbook(wb); } finally { if (null != wb) { wb.close(); } } return list; } protected static List<Map<String, String>> readWorkbook(Workbook wb) throws Exception { List<Map<String, String>> list = new LinkedList<Map<String, String>>(); for (int k = 0; k < wb.getNumberOfSheets(); k++) { Sheet sheet = wb.getSheetAt(k); int rows = sheet.getPhysicalNumberOfRows(); for (int r = 0; r < rows; r++) { Row row = sheet.getRow(r); if (row == null) { continue; } Map<String, String> map = new HashMap<String, String>(); int cells = row.getPhysicalNumberOfCells(); for (int c = 0; c < cells; c++) { Cell cell = row.getCell(c); if (cell == null) { continue; } String value = getCellValue(cell); map.put(String.valueOf(cell.getColumnIndex() + 1), value); } list.add(map); } } return list; } protected static String getCellValue(Cell cell) { String value = null; switch (cell.getCellType()) { case Cell.CELL_TYPE_FORMULA: // 公式 case Cell.CELL_TYPE_NUMERIC: // 数字 double doubleVal = cell.getNumericCellValue(); short format = cell.getCellStyle().getDataFormat(); String formatString = cell.getCellStyle().getDataFormatString(); if (format == 14 || format == 31 || format == 57 || format == 58 || (format >= 176 && format <= 183)) { // 日期 Date date = DateUtil.getJavaDate(doubleVal); value = formatDate(date, dateFmtPattern); } else if (format == 20 || format == 32 || (format >= 184 && format <= 187)) { // 时间 Date date = DateUtil.getJavaDate(doubleVal); value = formatDate(date, "HH:mm"); } else { value = String.valueOf(doubleVal); } break; case Cell.CELL_TYPE_STRING: // 字符串 value = cell.getStringCellValue(); break; case Cell.CELL_TYPE_BLANK: // 空白 value = ""; break; case Cell.CELL_TYPE_BOOLEAN: // Boolean value = String.valueOf(cell.getBooleanCellValue()); break; case Cell.CELL_TYPE_ERROR: // Error,返回错误码 value = String.valueOf(cell.getErrorCellValue()); break; default: value = ""; break; } return value; } @SuppressWarnings("deprecation") private static String formatDate(Date d, String sdf) { String value = null; if (d.getSeconds() == 0 && d.getMinutes() == 0 && d.getHours() == 0) { value = DateTimeUtil.getFormatedDate(d, dateFmtPattern); } else { value = DateTimeUtil.getFormatedDate(d, sdf); } return value; } } 对于这些第三方工具类的框架来说,老夫一直认为我们没有必要每次都自己去一步一步的写,只要写一次就够了,所以本文就是老夫的一个笔记而已,希望做到无论是老夫还是渎职今后只要需要解析Excel的时候,找到这里,把这里的方法copy出去,改吧改吧就能用了,另外本文也只牵涉到对Excel的解析而已,并没有生成的部分,一方面我在工作中解析多余生成,另一方面我相信大家只要会解析生成也一定不是大问题,网上资料这么多,所以就留给读者自己去探索了

March 13, 2016 · 3 分钟 · Bridge Li

Junit Test之Easy Mock Test入门

这一段时间公司的项目进行分模块分层进行专人维护开发,所以就会有不同的service和dao有不同的人来开发,这里我们假设service和dao不同的人开发,service是依赖dao的,如果我们的dao开发人员比较忙并没有把dao模块开发好,service如果要对自己的模块进行测试该怎么做呢?这个时候我们的Easy Mock Test就可以派上用场了。 首先开发service的和dao的会讨论商量出来一套接口,假设dao的接口如下: package cn.bridgeli.dao; import cn.bridgeli.model.User; public interface UserDao { public User getUserById(int id); public User getUserByUsername(String username); //... } dao模块的小朋友把它打成一个jar包,扔给service开发人员,然后我们亲爱的service开发人员就自己玩去了,最后我们的service开发人员完成了自己的任务,写下了如下的代码,当然这只是一个demo而已: package cn.bridgeli.service.impl; import cn.bridgeli.dao.UserDao; import cn.bridgeli.model.User; import cn.bridgeli.service.UserService; public class UserServiceImpl implements UserService { private final UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public boolean login(String username, String password) { User user = userDao.getUserByUsername(username); System.err.out.println("id==" + user.getId()); if (user != null) { String passwordInDao = user.getPassword(); if (passwordInDao != null && passwordInDao.equalsIgnoreCase(password)) { return true; } return false; } @Override public User getUserById(int userId) { return userDao.getUserById(userId); } } 但是我们的service怎么知道自己写的有没有问题呢?现在我们的service想对自己的模块进行测试,但dao开发人员还没开始,这肯定是没办法开始的,那么怎么办呢?很简单,只需要这么做就可以了: ...

February 29, 2016 · 1 分钟 · Bridge Li

Maven私服之Nexus入门图文教程

老夫相信看到这篇文章的人一定已经知道maven和nexus分别是什么东西了,所以就不多做介绍了,下面直接从下载安装开始讲。 下载安装 大家可以直接从这个链接http://www.sonatype.org/nexus/go/下载系统,下载完成之后解压到系统的任何文件夹下就可以了,老夫下载是:nexus-2.8.1-01,然后可就是安装了。 解压一路进到nexus-2.8.1-01/bin/jsw。然后选择适合自己的系统的文件夹进去,老夫的电脑是win32,所以进去之后是这个样子 然后用管理员身份先运行install-nexus.bat安装服务,然后运行console-nexus.bat,等起来之后直接在浏览器中输入:http://localhost:8081/nexus,我们看到下图就是成功了,忘了是不是需要安装服务:wrapper了,大家试一试就知道了。 配置nexus 要管理Nexus,你首先需要以管理员身份登陆,点击界面右上角的login,输入默认的登录名和密码:admin/admin123,登陆成功后,你会看到左边的导航栏增加了很多内容,变成了下面这个样子 这里,可以管理仓库,配置Nexus系统,管理任务,管理用户,角色,权限,查看系统的RSS源,管理及查看系统日志,等等。你会看到Nexus的功能十分丰富和强大,本文,老夫只介绍一些最基本的管理和操作。 因为是老夫习惯于让大伙看了这篇文章就知道怎么做,所以这里多内容咱就不说了,直接上配置,点击左边Administration菜单下面的Repositories,找到右边仓库列表中的三个仓库Apache Snapshots,Codehaus Snapshots和Maven Central,然后再没有仓库的configuration下把Download Remote Indexes修改为true。如下图: 然后在Apache Snapshots,Codehaus Snapshots和Maven Central这三个仓库上分别右键,选择Repari Index,这样Nexus就会去下载远程的索引文件。 这样设置以后, Nexus会自动从远程中央仓库下载索引文件, 为了检验索引文件自动下载是否生效,可以切换到Browse Index看看是否生成了这些索引文件。 添加一个代理仓库 加入我们想要代理Sonatype的公共仓库,其地址为:http://repository.sonatype.org/content/groups/public/。步骤如下,在Repositories面板的上方,点击Add,然后选择Proxy Repository,在下方的配置部分,我们填写如下的信息:Repository ID – sonatype;Repository Name – Sonatype Repository;Remote Storage Location – http://repository.sonatype.org/content/groups/public/。其余的保持默认值,需要注意的是Repository Policy,我们不要代理snapshot构件,至于原因老夫相信读者应该已经很清楚了,然后点击Save。因为比较简单就不上图了。 管理宿主仓库 Nexus预定义了3个本地仓库,分别为Releases,Snapshots,和3rd Party。这三个仓库都有各自明确的目的。Releases用于部署我们自己的release构件,Snapshots用于部署我们自己的snapshot构件,而3rd Party用于部署第三方构件,有些构件如Oracle的JDBC驱动,我们不能从公共仓库下载到,我们就需要将其部署到自己的仓库中。 当然你也可以创建自己的本地仓库,步骤和创建代理仓库类似,点击Repository面板上方的Add按钮,然后选择Hosted Repository,然后在下方的配置面板中输入id和name,注意这里我们不再需要填写远程仓库地址,Repository Type则为不可修改的hosted,而关于Repository Policy,你可以根据自己的需要选择Release或者Snapshot。 管理Maven仓库组 Nexus 中仓库组的概念是Maven没有的,在Maven看来,不管你是hosted也好,proxy也好,或者group也好,对我都是一样的,我只管根据 groupId,artifactId,version等信息向你要构件。为了方便Maven的配置,Nexus能够将多个仓库,hosted或者 proxy合并成一个group,这样,Maven只需要依赖于一个group,便能使用所有该group包含的仓库的内容。 最新neuxs默认自带了一个名为“Public Repositories”组,点击该组可以对他保护的仓库进行调整,把刚才建立的仓库Sonatype Repository加入其中,这样就不需要再在maven中明确指定仓库的地址了。同时创建一个Group ID为public-snapshots、Group Name为Public Snapshots Repositories的组,把Apache Snapshots、Codehaus Snapshots、Snapshots和Sonatype Repository也加入其中。 配置Maven使用Nexus 默认情况下,Maven依赖于中央仓库,这是为了能让Maven开箱即用,但仅仅这么做明显是错误的,这不仅会造成大量的时间及带宽的浪费,而且也不能使用公司自己的jar文件。既然本文的前面已经介绍了如何安装和配置Nexus,现在我们就要配置Maven来使用本地的Nexus来解决这个问题。 其实我们可以将Repository配置到POM中,但一般来说这不是很好的做法,原因很简单,你需要为所有的Maven项目重复该配置。因此,这里我们将Repository的配置放到$user_home/.m2/settings.xml中,放一个老夫在公司里面的完整的settings.xml的完整文件: <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <!-- <localRepository>E:maven_repository</localRepository> --> <localRepository>D:/J2EE/repo</localRepository> <!--非官方插件命令行运行配置--> <pluginGroups> <pluginGroup>org.mortbay.jetty</pluginGroup> </pluginGroups> <servers> <server> <id>releases</id> <username>XXX</username> <password>XXXXXX</password> </server> <server> <id>snapshots</id> <username>XXX</username> <password>XXXXXX</password> </server> <server> <id>nexus</id> <username>XXX</username> <password>XXXXXX</password> </server> </servers> <mirrors> <!--配置仓库镜像--> <mirror> <id>nexus</id> <mirrorOf>*</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://nexus.lagou.com/content/groups/public/</url> </mirror> </mirrors> <profiles> <!--配置仓库和插件仓库--> <profile> <id>nexus</id> <repositories> <repository> <id>central</id> <name>central</name> <url>http://central</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <name>central</name> <url>http://central</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <!--激活profile--> <activeProfiles> <activeProfile>nexus</activeProfile> </activeProfiles> </settings> 其实如果只需要下载的话,settings.xml中server是没有必要配置的,至于为什么配置了,下文会作出说明。 ...

January 31, 2016 · 1 分钟 · Bridge Li

集群Quartz的配置方法

一般系统随着用户量的增长,慢慢的都会由单机走向集群,而很多时候我们又需要跑一些定时任务,Quartz就是为此而生,那么单机好办,集群中的Quartz又该如何配置呢?集群中的Quartz各节点之间是通过同一个数据库实例(准确的说是同一个数据库实例的同一套表)来感知彼此的,既然是通过数据库,那么就先看看数SQL文件 SQL文件 # # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar # # In your Quartz properties file, you'll need to set # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # DROP TABLE IF EXISTS QRTZ_JOB_LISTENERS; DROP TABLE IF EXISTS QRTZ_TRIGGER_LISTENERS; DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_VOLATILE VARCHAR(1) NOT NULL, IS_STATEFUL VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_JOB_LISTENERS ( JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, JOB_LISTENER VARCHAR(200) NOT NULL, PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER), FOREIGN KEY (JOB_NAME,JOB_GROUP), REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, IS_VOLATILE VARCHAR(1) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (JOB_NAME,JOB_GROUP), REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP), REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP), REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_TRIGGER_LISTENERS ( TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, TRIGGER_LISTENER VARCHAR(200) NOT NULL, PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER), FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, IS_VOLATILE VARCHAR(1) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_STATEFUL VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (LOCK_NAME) ); INSERT INTO QRTZ_LOCKS values('TRIGGER_ACCESS'); INSERT INTO QRTZ_LOCKS values('JOB_ACCESS'); INSERT INTO QRTZ_LOCKS values('CALENDAR_ACCESS'); INSERT INTO QRTZ_LOCKS values('STATE_ACCESS'); INSERT INTO QRTZ_LOCKS values('MISFIRE_ACCESS'); commit; 其实这个文件在Quartz的文档中是可以找到的,这里贴出来只是为了大家方便,建好数据库之后,接下来肯定就是看看数据源的问题了 ...

January 3, 2016 · 4 分钟 · Bridge Li

maven打包dubbo接口之最佳实践

之前刚开始学习dubbo的时候,曾写过一个入门的小例子,当时生产者也是用tomcat去跑的,其实dubbo只需要提供service层接口就好了,并不需要和http相关的东西,所以其实并不需要用tomcat去跑,我们完全打成其他的包直接去跑,这样dubbo接口也不会tomcat性能的限制,而打包可以说是maven最擅长的事情之一,今天就记录一下我们公司的实际项目中使用maven-assembly-plugin打包的方法。 首先在pom文件中,添加maven-assembly-plugin插件 <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptor>src/main/assembly/assembly.xml</descriptor> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single </goal> </goals> </execution> </executions> </plugin> 在该插件的第四行我们指定了一个assembly.xml文件,下面我们就看看assembly.xml的内容 assembly.xml文件 <assembly> <id>assembly</id> <formats> <format>tar.gz</format> </formats> <includeBaseDirectory>true</includeBaseDirectory> <fileSets> <fileSet> <outputDirectory>/</outputDirectory> <includes> <include>README.txt</include> </includes> </fileSet> <fileSet> <directory>src/main/scripts</directory> <outputDirectory>/bin</outputDirectory> </fileSet> </fileSets> <dependencySets> <dependencySet> <useProjectArtifact>true</useProjectArtifact> <outputDirectory>lib</outputDirectory> </dependencySet> </dependencySets> </assembly> 该文件的第四行中的tar.gz指的就是打包的文件格式,对于Linux用户,对这个格式一定非常熟悉,当然大家也可以指定为zip格式,另外在该文件的第十五行,指定了一个scripts文件夹,那么这里面放的又是什么呢?我们知道打包之后的系统我们要跑起来才能用,那么这里面放的就是对我们的系统操作的一些脚本,打包之后,我们的系统都是一些jar文件,放在了倒数第四行指定的lib文件中,而这些脚本则放在了和lib同级的bin文件中,下面就让我们一一看看scripts中几个文件的内容 scripts文件夹 ①. start.bat @echo off setlocal enabledelayedexpansion :: 初始化变量 set "LIB_JARS=" :: 切换到 lib 目录并拼接 jar 包路径 cd ..\lib for %%i in (*) do ( set "LIB_JARS=!LIB_JARS!;..\lib\%%i" ) cd ..\bin :: 判断启动参数 if "%1" == "debug" goto debug if "%1" == "jmx" goto jmx :: 默认启动模式 java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -classpath ..\conf;%LIB_JARS% com.alibaba.dubbo.container.Main goto end :debug :: Debug 模式启动 java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -classpath ..\conf;%LIB_JARS% com.alibaba.dubbo.container.Main goto end :jmx :: JMX 模式启动 java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -classpath ..\conf;%LIB_JARS% com.alibaba.dubbo.container.Main :end pause ②. start.sh ...

December 20, 2015 · 6 分钟 · Bridge Li

工作流Activiti5入门(下)

看了工作流上和中的人,相信已经在慢慢入门了,因为本教程也只是致力于做一个入门而已,然后在用的过程中再慢慢学习,用来工作也许是一点问题都没有了,如果没有看过请看这篇和这篇,所以本没有必要再写下了,但实在感觉留下一个小尾巴:数据库还没有给大家介绍,这个入门实在是太过简陋,所以今天就把这篇文章来一个扫尾,下面先看最后一个想用的task 六. 组任务 流程图: 其实和用户任务看起来并无差别,下面看实现 分配组任务方式一(直接指定办理人) 具体配置: 完成任务: /**查询当前人的个人任务*/ @Test public void findMyPersonalTask(){ String assignee = "小A"; List<Task> list = processEngine.getTaskService()//与正在执行的任务管理相关的Service .createTaskQuery()//创建任务查询对象 /**查询条件(where部分)*/ .taskAssignee(assignee)//指定个人任务查询,指定办理人 // .taskCandidateUser(candidateUser)//组任务的办理人查询 /**排序*/ .orderByTaskCreateTime().asc()//使用创建时间的升序排列 /**返回结果集*/ .list();//返回列表 if(list!=null && list.size()>0){ for(Task task:list){ System.out.println("任务ID:"+task.getId()); System.out.println("任务名称:"+task.getName()); System.out.println("任务的创建时间:"+task.getCreateTime()); System.out.println("任务的办理人:"+task.getAssignee()); System.out.println("流程实例ID:"+task.getProcessInstanceId()); System.out.println("执行对象ID:"+task.getExecutionId()); System.out.println("流程定义ID:"+task.getProcessDefinitionId()); System.out.println("########################################################"); } } } /**查询当前人的组任务*/ @Test public void findMyGroupTask(){ String candidateUser = "大大"; List<Task> list = processEngine.getTaskService()//与正在执行的任务管理相关的Service .createTaskQuery()//创建任务查询对象 /**查询条件(where部分)*/ .taskCandidateUser(candidateUser)//组任务的办理人查询 /**排序*/ .orderByTaskCreateTime().asc()//使用创建时间的升序排列 /**返回结果集*/ .list();//返回列表 if(list!=null && list.size()>0){ for(Task task:list){ System.out.println("任务ID:"+task.getId()); System.out.println("任务名称:"+task.getName()); System.out.println("任务的创建时间:"+task.getCreateTime()); System.out.println("任务的办理人:"+task.getAssignee()); System.out.println("流程实例ID:"+task.getProcessInstanceId()); System.out.println("执行对象ID:"+task.getExecutionId()); System.out.println("流程定义ID:"+task.getProcessDefinitionId()); System.out.println("########################################################"); } } } /**完成我的任务*/ @Test public void completeMyPersonalTask(){ //任务ID String taskId = "6905"; processEngine.getTaskService()//与正在执行的任务管理相关的Service .complete(taskId); System.out.println("完成任务:任务ID:"+taskId); } /**查询正在执行的任务办理人表*/ @Test public void findRunPersonTask(){ //任务ID String taskId = "6204"; List<IdentityLink> list = processEngine.getTaskService()// .getIdentityLinksForTask(taskId); if(list!=null && list.size()>0){ for(IdentityLink identityLink:list){ System.out.println(identityLink.getTaskId()+" "+identityLink.getType()+" "+identityLink.getProcessInstanceId()+" "+identityLink.getUserId()); } } } /**查询历史任务的办理人表*/ @Test public void findHistoryPersonTask(){ //流程实例ID String processInstanceId = "6201"; List<HistoricIdentityLink> list = processEngine.getHistoryService()// .getHistoricIdentityLinksForProcessInstance(processInstanceId); if(list!=null && list.size()>0){ for(HistoricIdentityLink identityLink:list){ System.out.println(identityLink.getTaskId()+" "+identityLink.getType()+" "+identityLink.getProcessInstanceId()+" "+identityLink.getUserId()); } } } /**拾取任务,将组任务分给个人任务,指定任务的办理人字段*/ @Test public void claim(){ //将组任务分配给个人任务 //任务ID String taskId = "6905"; //分配的个人任务(可以是组任务中的成员,也可以是非组任务的成员) String userId = "大大"; processEngine.getTaskService()// .claim(taskId, userId); } /**将个人任务回退到组任务,前提,之前一定是个组任务*/ @Test public void setAssigee(){ //任务ID String taskId = "6204"; processEngine.getTaskService() .setAssignee(taskId, null); } /**向组任务中添加成员*/ @Test public void addGroupUser(){ //任务ID String taskId = "6204"; //成员办理人 String userId = "大H"; processEngine.getTaskService()// .addCandidateUser(taskId, userId); } /**从组任务中删除成员*/ @Test public void deleteGroupUser(){ //任务ID String taskId = "6204"; //成员办理人 String userId = "小B"; processEngine.getTaskService()// .deleteCandidateUser(taskId, userId); } 分配组任务方式二(使用流程变量) 具体配置: ...

November 29, 2015 · 3 分钟 · Bridge Li