写java程序的时候,经常要到官网(http://docs.oracle.com/javase/8/docs/api/index.html)查文档,用着用着感觉有时有些不方便:
1.java官网有时登不上去
2.没有搜索查询功能
所以针对这两个,就想到做一个文档离线查询器,把文档离线下来,然后可以进行搜索查找。(代码依然写得乱。。。)
项目介绍:
项目名称:java文档离线下载器(JDOR)
exe文件下载:
本地电脑具有jdk 1.8版本的(需要配置好java系统环境):
下载地址:http://download.csdn.net/download/name_z/9459596
本地环境没有配好的,可以下载jdk1.8的jre包,放到该exe所在文件夹便可使用(jdk1.8的jre包有180多兆,太大,传不上来)
使用环境:windows
程序分析:
下载组件、流程分析:
Record:
Record作为数据的集合,用于在各组件之间传递
/**
*
* 保存一条数据记录(一个包或一个类)的类
*
* url:string 网址(离线时是在线网址,查询时是本地网址)
* filePath:string 本地文件保存地址
* papge:string 网页数据(html源码)
* fullname:string 包名或类名
* isclass:boolean 是否为类
*
* @author zwh
* @version 1.0
*/
public class Record
{
public String url = "";
public String filePath = "";
public String page = "";
public String fullName = "";
public boolean isClass = false;
}
Offliner下载器:
流程图:
解析:
功能:
Offliner下载器是下载总组件,主要任务是为外部提供调用接口,内部启动、调用其它的组件完成下载任务。
//开始下载
public void offline(String version);
//获取总共需要下载的Record的数量
public int getTotalRecords();
//设置下载速度
public void setOfflineSpeed(SPEED speed);
RecordAllocater任务分配器:
流程图:
解析:
功能:
将传递进来的未下载的Record分派给RecordOffliner执行任务下载(多线程),以及启动异常Record重分派器。
分析:
由于采用循环列表创建线程执行任务,因此采用线程池可以更方便的进行管理:
public class RecordAllocater extends Thread
......
//该线程池会动态的增加、删除线程
private ExecutorService threadPool = Executors.newCachedThreadPool();
......
public void allocate()
{
for (Record record : this.nonOfflineRecords)
{
//offliner继承了Thread
RecordOffliner offliner = new RecordOffliner(record);
//直接调用线程池的execute方法启动新线程
this.threadPool.execute(offliner);
//登记信息:创建了一个新的下载线程
RecordOfflinerMonitor.createARecordOffliner();
/*
*用于控制下载速度,如果time==0,就会在短时间内创建了大量线程,对目标网站进行访问,
*容易造成目标网站封禁IP不能登录
*/
try
{
sleep(time);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
//在UI处显示信息
OfflineUI.appendOfflineExceptionMessage("RecordAllocater分派任务完成");
}
......
ExceptionRecordAllocater任务分配器:
流程图:
解析:
功能:
ExceptionRecordAllocater实际上是RecordAllocater的内部私有类,由RecordAllocater负责启动,当RecordOffliner下载过程中出现异常(由于短时间内大量线程对目标网站的访问导致的IP暂时被封禁)的时候,Record将被发送到RecordAllocater的静态变量exceptionRecords队列中,这时将唤醒ExceptionRecordAllocater对这些下载异常的Record重新分派。
//由于是并发访问,因此要使用线程安全的ConcurrentLinkedQueue<Record>
private static ConcurrentLinkedQueue<Record> exceptionRecords = new ConcurrentLinkedQueue<Record>();
......
/**
*
* RecordOffliner调用该方法发送异常Record
*
* @param record:下载异常的Record
*/
public static void exceptionRecord(Record record)
{
RecordAllocater.exceptionRecords.add(record);
synchronized (lockNewRecord)
{
//唤醒沉睡的ExceptionRecordAllocater
lockNewRecord.notifyAll();
}
}
......
private class ExceptionRecordsAllocater extends Thread
{
@Override
public void run()
{
while (true)
{
//当没有异常Record加入时,会一直陷入沉睡状态
synchronized (lockNewRecord)
{
try
{
lockNewRecord.wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
//UI处显示信息
OfflineUI.appendOfflineExceptionMessage("ExceptionRecordsAllocater唤醒,开始将发生下载错误的Record重新分派");
/*
*这时线程已被唤醒(一定存在异常Record),而且可能存在多个异常Record(其它线程在执行的时候也出现了异常)
*因此需要遍历exceptionRecords
*/
while (!exceptionRecords.isEmpty())
{
Record record = exceptionRecords.poll();
if (record != null)
{
//启动线程执行,依然使用RecordAllocater的线程池
RecordOffliner offliner = new RecordOffliner(record);
threadPool.execute(offliner);
RecordOfflinerMonitor.createARecordOffliner();
//由于这些是异常Record,增大分派时间间隔,确保这次成功的几率
try
{
sleep(500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
OfflineUI.appendOfflineExceptionMessage("发生下载错误的Record分派完成,ExceptionRecordsAllocater陷入沉睡");
}
}
}
RecordOffliner任务下载器:
流程图:
解析:
作为下载任务的最终执行者,每个RecordOffliner负责下载一个Record指定的页面。
/**
* 下载过程出现异常(爬取过程出现的异常),
* 将在OfflineUI中显示异常信息,并将Record重新交给RecordAllocater重新分配,最后将在
* RecordOfflinerMonitor登记信息;如果正常下载完,将发送给FinishedRecordRepertory,并
* 在RecordOfflinerMonitor登记信息
*/
@Override
public void run()
{
try {
//爬取指定url的页面
this.record.page = Crawler.crawl(this.record.url);
} catch (Exception e) {
Offl