<<上篇 | 首页 | 下篇>>

如何中断正在执行IO的 Quartz 作业

Interrupt a Quartz job that doing IO

 

如果你想中断正在执行IO的 Quartz 作业,在你使用 InterruptibleChannel 时这是可行的。引用一下Oracle链接:实现了这个接口的通道,是可中断的:如果一个线程在一个中断通道阻塞I/O操作,另一个线程能调用阻塞的线程的中断方法。这将导致的通道被关闭,被阻塞的线程收到一个ClosedByInterruptException,设置被阻塞的线程的中断状态。因此,获得自己工作的执行线程的作业计划,能保存供以后使用。当Quartz调度中断作业,你可以再调用该线程的interrupt()方法来停止读/写操作。这里有一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package demo;
 
// import statements excluded for brevity
 
public class MyJob implements InterruptableJob {
 
  private static Logger    LOG              = LoggerFactory.getLogger(MyJob.class);
 
  private volatile boolean isJobInterrupted = false;
 
  private JobKey           jobKey           = null;
 
  private volatile Thread  thisThread;
 
  public MyJob() {
  }
 
  public void execute(JobExecutionContext context) throws JobExecutionException {
    thisThread = Thread.currentThread();
    LOG.info("Thread name of the current job: " + thisThread.getName());
 
    jobKey = context.getJobDetail().getKey();
    LOG.info("Job " + jobKey + " executing at " + new Date());
 
    try {
      String fileUrl = "http://d2zwv9pap9ylyd.cloudfront.net/terracotta-3.6.1.tar.gz"; // 59 MB
      String localFile = "terracotta-3.6.1.tar.gz";
      download(fileUrl, localFile);
    } catch (ClosedByInterruptException e) {
      LOG.info("Caught ClosedByInterruptException... exiting job.");
    } catch (IOException e) {
      LOG.info("Caught IOException... exiting job.", e);
    } finally {
      if (isJobInterrupted) {
        LOG.info("Job " + jobKey + " did not complete");
      } else {
        LOG.info("Job " + jobKey + " completed at " + new Date());
      }
    }
  }
   
  // this method is called by the scheduler
  public void interrupt() throws UnableToInterruptJobException {
    LOG.info("Job " + jobKey + "  -- INTERRUPTING --");
    isJobInterrupted = true;
    if (thisThread != null) {
      // this called cause the ClosedByInterruptException to happen
      thisThread.interrupt();
    }
  }
 
  private void download(String address, String localFileName) throws ClosedByInterruptException, IOException {
    URL url = new URL(address);
    ReadableByteChannel src = Channels.newChannel(url.openStream());
    WritableByteChannel dest = new FileOutputStream(new File(localFileName)).getChannel();
    try {
      System.out.println("Downloading " + address + " to " + new File(localFileName).getCanonicalPath());
      int size = fastChannelCopy(src, dest);
      System.out.println("Download completed! " + (size / 1024 / 1024) + " MB");
    } finally {
      src.close();
      dest.close();
    }
  }
   
  private static int fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
    final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
    int count = 0;
    int total = 0;
    while ((count = src.read(buffer)) != -1) {
      total += count;
      // prepare the buffer to be drained
      buffer.flip();
      // write to the channel, may block
      dest.write(buffer);
      // If partial transfer, shift remainder down
      // If buffer is empty, same as doing clear()
      buffer.compact();
    }
    // EOF will leave buffer in fill state
    buffer.flip();
    // make sure the buffer is fully drained.
    while (buffer.hasRemaining()) {
      dest.write(buffer);
    }
    return total;
  }
}

 

这是我的主类,创建Quartz Scheduler和模拟预期的中断。下载将需要大约40秒完成(59MB文件)。为了看到我们的作业确实是在下载过程中中断,我们启动调度然后休息5秒。注:如果您想看到的作业完成,休息了约40秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package demo;
 
import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
 
// other imports excluded for brevity
 
public class InterruptExample {
 
  public void run() throws Exception {
    final Logger log = LoggerFactory.getLogger(InterruptExample.class);
 
    log.info("------- Initializing ----------------------");
 
    // First we must get a reference to a scheduler
    SchedulerFactory sf = new StdSchedulerFactory();
    Scheduler sched = sf.getScheduler();
 
    log.info("------- Initialization Complete -----------");
 
    log.info("------- Scheduling Jobs -------------------");
 
    // get a "nice round" time a few seconds in the future...
    Date startTime = nextGivenSecondDate(null, 1);
 
    JobDetail job = newJob(MyJob.class).withIdentity("myJob", "group1").build();
 
    SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime)
        .withSchedule(simpleSchedule()).build();
 
    sched.scheduleJob(job, trigger);
 
    // start up the scheduler (jobs do not start to fire until
    // the scheduler has been started)
    sched.start();
    log.info("Scheduler thread's name: " + Thread.currentThread().getName());
    log.info("------- Started Scheduler -----------------");
 
    try {
      // if you want to see the job to finish successfully, sleep for about 40 seconds
      Thread.sleep(5 * 1000L);
      // tell the scheduler to interrupt our job
      sched.interrupt(job.getKey());
      Thread.sleep(3 * 1000L);
    } catch (Exception e) {
      e.printStackTrace();
    }
 
    log.info("------- Shutting Down ---------------------");
 
    sched.shutdown(true);
 
    log.info("------- Shutdown Complete -----------------");
  }
 
  public static void main(String[] args) throws Exception {
    InterruptExample example = new InterruptExample();
    example.run();
  }
}

 

 

这是日志,说明我们的作业被intterupted提早退出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
INFO [main] ------- Initializing ----------------------
INFO [main] Using default implementation for ThreadExecutor
INFO [main] Job execution threads will use class loader of thread: main
INFO [main] Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
INFO [main] Quartz Scheduler v.2.1.3 created.
INFO [main] RAMJobStore initialized.
INFO [main] Scheduler meta-data: Quartz Scheduler (v2.1.3) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
 
INFO [main] Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
INFO [main] Quartz scheduler version: 2.1.3
INFO [main] ------- Initialization Complete -----------
INFO [main] ------- Scheduling Jobs -------------------
INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
INFO [main] Scheduler thread's name: main
INFO [main] ------- Started Scheduler -----------------
INFO [DefaultQuartzScheduler_Worker-1] Thread name of the current job: DefaultQuartzScheduler_Worker-1
NFO [DefaultQuartzScheduler_Worker-1] Job group1.myJob executing at Mon Apr 16 16:24:40 PDT 2012
 Downloading http://d2zwv9pap9ylyd.cloudfront.net/terracotta-3.6.1.tar.gz to S:\quartz-interrupt-demo\terracotta-3.6.1.tar.gz
INFO [main] Job group1.myJob  -- INTERRUPTING --
INFO [DefaultQuartzScheduler_Worker-1] Caught ClosedByInterruptException... exiting job.
INFO [DefaultQuartzScheduler_Worker-1] Job group1.myJob did not complete
ERROR [DefaultQuartzScheduler_Worker-1] Worker thread was interrupt()'ed.
 java.lang.InterruptedException
 at java.lang.Object.wait(Native Method)
 at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:552)
INFO [main] ------- Shutting Down ---------------------
INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.
INFO [main] ------- Shutdown Complete -----------------

 

整个例子可以在这里下载

 来源:英文          中文编译:IT瘾       转载请保留原文链接

 

标签 : ,

开发杀手移动用户界面的7个步骤

有些微妙,你有一个智能手机,而没有意识到你个人和它已经发展出一种特殊的关系。我们最近了解到,史蒂夫·乔布斯掌管的他自己组建的团队,创建了第一代iPhone——“人们会爱上的电话。”无论我们承认与否,我们与我们的iPhone,黑莓,Android或Windows Phone有着特殊的联系。

但爱是善变的。最近的哈里斯互动研究表明,坏的移动应用程序,可以极大地损害品牌的声誉。几乎有三分之一的受访者表示,当有一个坏的移动应用程序经验时他们告诉过别人。不过,超过半数表示,他们推荐过有良好体验的移动应用程序。

为移动应用提供一个良好的用户体验是至关重要的。这里有七个步骤,让你以正确的方式交付伟大的移动应用程序。

 

1。定义你的目标

你想完成的移动应用程序是什么?最重要的是,你的用户拿它想要干什么?你必须确定你的应用程序将达成的目标。对于这一点,你需要了解用户用这个程序每天的活动,围绕它的目标和动机。

一个好的方法是创建人物 - 代表用户的虚构人物 - 编写敏捷用户故事规格。这种方法使你以正确的心态来为您的应用程序陈述需求。类似这样的东西:“作为保安人员,约翰尼·布拉沃必须用他的手机,找出下一个他应该到检查点,签到,这样他就可以完成随机巡逻。”这种方法可以让你从用户的角度来看应用。

当然,要考虑移动和桌面应用程序的用户故事之间的差异,尤其是当考虑到移动环境。例如,移动用户可以步行或用一只手。

经过收集几个这些故事后,你必须根据他们的频率区分优先级。我会解释为什么这非常重要。

 

2。分析现有的应用程序

如果它是第一次你要创建的移动应用程序,仔细看看现有应用的界面。有两个突出的明显的事情:屏幕尺寸很小,所以许多移动设备上使用触摸屏;屏幕上的元素一定要大,所以他们很容易触摸。

较小的屏幕和更大的元素意味着你在屏幕上的显示的项目数量有限。

选择在手机屏幕上怎么样做是一个挑战;一些可用性专家甚至主张,在桌面版本之前创建一个移动网站版本。

区分你的用户故事优先次序对此很有帮助。你要认真研究最重要用户使用的功能。分析出在80%的时间使用的20%的功能,是每一个可用性专家的目标。如果你在移动上做得正确,桌面也将有优先权力。

 

3。本机应用或移动Web应用?

你需要决定是否使用HTML5或本地API。这一决定对用户界面技术实现有很大的影响。本机应用程序通常运行速度更快,是游戏,离线,硬件密集的应用程序的最佳选择。移动Web应用程序可更快地实施,易于维护,并常常更好地适合企业应用程序。混合的方法也是有道理的,如果你想要移动Web应用程序的维护方便,但需要特定的硬件功能,如手机的摄像头或GPS。

可以利用现有的框架和平台解决方案,但无论您选择什么,确保你可以快速地创建和修改用户界面,以便你可以经常迭代。

 

4。快速制作原型

项目启动时测试移动界面的一种快速方法是使用低保真原型。Palm Pilot推出之前,它说,发明家把小木块大小的设备放进口袋来感觉如何。后来,他在这上面勾勒了几个粗糙版本的用户界面。

早期的Palm Pilot原型慢慢的接近真实的东西。

移动应用程序原型制作很简单:铅笔和纸都是你所需要的,因为屏幕小,你不会绘制太多。原型测试通过请用户尝试完成他们的任务。看看他们做什么,问他们,他们正在想什么。其结果对帮助您改进设计是惊人的。一个伟大的设计的口头禅是:“不要让我思考!”如果你的移动设计测试失败,那么你的应用程序也将失败。

 

5。避免破坏 UX 用户体验的错误

当人们开始创建移动应用程序,他们通常会犯一些已知的错误。首先,如果他们创造一个现有网站的移动版本,开发人员常常试图复制桌面上每一个功能,没有考虑到移动用户的意图。

另一个常见的错误是有太多的导航结构。深层结构不能很好地适用于移动,简单的模型才能更好地工作。希望用户能够轻松地输入文字是另一个错误:移动设备上文本输入很难,所以你要尽量减少输入。

在手机上运行优秀的应用是让阅读更多内容而不是书写内容。想一想:您最喜爱的移动应用程序需要输入大量的文字?

 

6。添加令人愉快的细节

移动电话永远伴随着你,知道你在哪里,这就是为什么mapping地图应用程序是如此的成功。电话可以听到你所听到和看到你所看到的,这也解释了Shazam和Instagram的普及应用。使用位置,摄像头,麦克风,都是获得用户所处的环境聪明的方法,你可以以独特的方式利用它。

即使你不使用移动设备的硬件,也有几个技巧你可以用它来取悦用户。动画可以增加你的应用程序的兴奋。如果您正在创建移动Web应用程序,缓存静态内容,并考虑使用 CDN 内容交付网络,使页面载入更快。

受欢迎触摸移动Web应用程序的另一个是可让用户将它们添加到自己的主屏幕,使他们看起来像本地应用程序的感觉。如果你做到这一点,请记住,在你的用户界面上包含后退按钮,使用户可访问退出选项。

记住用户在不同会话的选择(例如,最近的项目列表),是另一种智能触摸,这将使应用程序具有上下文感知和解放用户不必重复导航步骤。这些可用性能快速完成操作,使用户的生活更轻松。

 

7。失败宁早,恢复快

即使有设计师和开发人员良好的团队合作,你第一次尝试创建一个移动应用程序可能也会失败。 (最近的一项调查表明,38%的人不满意他们用到品牌应用。)最好的策略是失败宁早和快速迭代,学习人们是如何使用您的应用程序并不断改善。敏捷方法是你交付用户需要的移动用户界面一个伟大的方式。

 

杀手移动用户界面是很难的,但在这篇文章中概述的战略将有帮助。认真的考虑它们,以你自己的方式来写一些伟大的移动应用程序吧!

 

来源:英文原文          中文编译:IT瘾       转载请保留原文链接

 

标签 : , , ,

在HTML5里用Ajax上传文件到Java Servlet

Ajax file upload to a Java Servlet in HTML5

HTML5带来一个很棒的功能,就是能够使用XMLHttpRequest版本2上传文件。

现代Gecko和WebKit浏览器,包括一个完美的对象参数formdata,允许结合既简单又复杂的表单数据(文本和文件)包含在Ajax请求的对象中。

让我们告诉你如何做到这个。

在这个例子中,我们有两个输入框的表单,一个代表一个简单的文本字段,另一个代表一个文件字段,如下面的代码所示。

 

<form id="form1">    

 

    <label for="sampleText">Please enter a text</label>

    <input id="sampleText" name="sampleText" type="text" /> <br/>

    <label for="sampleFile">Please select a file

    <input id="sampleFile" name="sampleFile" type="file" /> <br/>

    <input id="uploadBtn" type="button" value="Ajax Submit" onClick="performAjaxSubmit();"></input>

 

</form>

 

<script type="text/javascript">

 

    function performAjaxSubmit() {

        var sampleText = document.getElementById("sampleText").value;

        var sampleFile = document.getElementById("sampleFile").files[0];

        var formdata = new FormData();

        formdata.append("sampleText", sampleText);

        formdata.append("sampleFile", sampleFile);

 

        var xhr = new XMLHttpRequest();       

        xhr.open("POST","/fileUploadTester/FileUploader", true);

        xhr.send(formdata);

        xhr.onload = function(e) {

            if (this.status == 200) {

               alert(this.responseText);

            }

        };                    

    }   

 

</script> 

 

正如我们在上面代码中看到,它是一个正常的老XHR的代码,但它有两个差异:

1。在HTML5输入文件的文件属性,这使你能够得到的文件对象。

2。参数formdata对象,其中有一个方法叫做append,允许加入任何形式的数据(文本和文件)的对象。参数formdata对象具有另一大优势,这是Ajax请求“multipart/ form-data”没有任何特殊代码。

 

现在,让我们继续看Servlet代码(这里用的是Apache Commons File Upload处理multipart表单请求解析)。

 

 public class FileUploader extends HttpServlet {

 

    protected void doPost(HttpServletRequest request,

                          HttpServletResponse response)

                          throws ServletException, IOException {

        String ajaxUpdateResult = "";

        try {

            List items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);            

            for (FileItem item : items) {

                if (item.isFormField()) {

                    ajaxUpdateResult += "Field " + item.getFieldName() + 

                    " with value: " + item.getString() + " is successfully read\n\r";

                } else {

                    String fileName = item.getName();

                    InputStream content = item.getInputStream();

                    response.setContentType("text/plain");

                    response.setCharacterEncoding("UTF-8");

                    // Do whatever with the content InputStream.

                    System.out.println(Streams.asString(content));

                    ajaxUpdateResult += "File " + fileName + " is successfully uploaded\n\r";

                }

            }

        } catch (FileUploadException e) {

            throw new ServletException("Parsing file upload failed.", e);

        }

        response.getWriter().print(ajaxUpdateResult);

    }

 

Servlet从请求中简单解析multipart表单,然后构造一个结果消息。

请记住,此代码能与Chrome5和Safari5,Firefox 4的工作,但不幸的是将不能在IE9下工作,因为悲惨的IE9没有参数formdata对象,我将告诉你如何可以在IE9实现相同的行为,“我相信你会不喜欢它,因为它是有点难“。

 

从这里下载完整的代码

 

来源:英文原文          中文编译:IT瘾      转载请保留原文链接

 

标签 : , ,