最近需要对我们做的网站程序做一个安装程序,今天下午花了点时间写了框架。
1、需求。
首先,这个安装程序应该是通用的,不同的程序使用这个安装程序只需要做少许修改就可以正确执行;其次,安装程序应该可配置,如安装过程中显示的版权、协议等信息,还有安装程序一共要执行多少个步骤、每个步骤执行前是否要检查上一个步骤是否执行成功等;然后,安装完毕时还要做必要的清理工作,如删除安装程序,包括class文件、模板文件和配置文件等;最后,如果安装过程中出现错误,应该有一个友好的错误处理界面。
2、设计。
配置:由于是要做一个通用的安装程序,因此需要有配置信息,也就是说要有一个获取配置信息的接口,如何获取,从哪儿获取,我们不关心,这些由具体的实现类来做。
数据的保存:前台提交进来的数据保存在哪里?是每一步都执行相应的安装操作还是将左右操作放到最后一步来执行?这里我们将数据用一个SetupStep类来保存,并将这个类放到session中,从而实现分步骤的安装。而第二个问题这里采用配置方式来决定,用户可以定制自己的安装程序用什么方式来安装。
异常处理:这里只做了一个简单的异常处理类,暂时不做过多的处理,只便于日后扩展用。
3、实现
ISetupConfig接口
package
com.easyjf.setup;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
/** */
/**
* 安装的配置接口,返回配置信息,如:总步骤数、是否验证上一步等
* @author Administrator
*
*/
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
interface
ISetupConfig
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 配置文件名
*/
static final String configFileName = "";
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回当前安装的软件名
* @return
*/
String getProjectName();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回当前安装的软件的介绍
* @return
*/
String getIntro();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回当前软件的安装协议
* @return
*/
String getProtocol();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回相关版权信息
* @return
*/
String getCopyRight();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回模板页名称
* @return
*/
String getPageName();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回安装数据存放在session中的标识
* @return
*/
String getSessionTag();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 返回总的步骤数
* @return
*/
int getTotalStepNum();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 是否开启验证
* @return
*/
boolean validatable();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 安装方式,每一步分步执行还是到最后一步一次性执行
* @return
*/
boolean setupByStep();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 初始化需要安装的step
*/
void init();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 根据步骤数获取步骤
* @param i
* @return
*/
SetupStep getSetupStep(int i);
}
这个接口的主要功能是获取配置信息,以使安装程序可配置,从而达到通用的目的。
然后我们须有有一个类来保存安装是填写的安装信息,比如数据库连接字符串之类、网站基本配置数据等。这里我们设计了一个类,这个类存放每一步前台提交进来的数据,同时要执行这一步的安装操作,如:修改配置文件、保存网站配置信息等等。由于不同的步骤要执行的安装操作不同,因此我将这个类设计为一个抽象类,将具体的安装操作留给子类来实现。代码如下:
package
com.easyjf.setup;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
import
java.util.Map;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
abstract
class
SetupStep
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 标识这个步骤是第几步
*/
private int step = 1;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 保存前台提交进来的数据
*/
private Map data;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 该步骤的上一步
*/
private SetupStep lastStep;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 该步骤的下一步
*/
private SetupStep nextStep;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 用于判断该步骤是否成功执行
*/
private boolean isOk;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public boolean isOk() ...{
return isOk;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setOk(boolean isOk) ...{
this.isOk = isOk;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public int getStep() ...{
return step;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setStep(int step) ...{
this.step = step;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public SetupStep getLastStep() ...{
return lastStep;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setLastStep(SetupStep lastStep) ...{
this.lastStep = lastStep;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public SetupStep getNextStep() ...{
return nextStep;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setNextStep(SetupStep nextStep) ...{
this.nextStep = nextStep;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public boolean isCuurentStep(int step)...{
return this.step==step;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public Map getData() ...{
return data;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setData(Map data) ...{
this.data = data;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public boolean isEndStep()...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(this.nextStep==null)...{
return true;
}
return false;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public boolean check()...{
return isOk;
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public boolean checkTheLastStep()...{
return this.lastStep.isOk;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 执行安装操作
* @return
*/
public abstract boolean doSetup();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
/** *//**
* 执行完全安装操作,用于将所有操作放到最后一步来执行的情况
* @return
*/
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public boolean doFullSetup()...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(!this.getLastStep().isOk)...{
return this.getLastStep().doFullSetup();
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
return doSetup();
}
}
}
这个类可以通过getLastStep()和getNextStep()方法来获取该步骤的上一步和下一步,从而将每一步衔接起来。doSetup()方法是一个抽象方法,预留给子类来实现,因为每一步的安装操作都可能不一样,因此需要让子类来做。
接下来是安装程序的action类。这个类和普通action类一样,只是调用SetupStep来执行安装操作。
package
com.easyjf.setup;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
import
java.util.HashMap;
import
java.util.Map;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
import
com.easyjf.web.ActionContext;
import
com.easyjf.web.Module;
import
com.easyjf.web.Page;
import
com.easyjf.web.WebForm;
import
com.easyjf.web.core.AbstractCmdAction;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
abstract
class
SetupAction
extends
AbstractCmdAction
...
{
private ISetupConfig config;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setConfig(ISetupConfig config) ...{
this.config = config;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
@Override
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public Page doInit(WebForm form, Module module) ...{
// TODO Auto-generated method stub
SetupStep setupStep = getStep(1);
ActionContext.getContext().getRequest().getSession(). setAttribute(config.getSessionTag(), setupStep);
return page("setup");
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public final Page doNext(WebForm form, Module module)...{
SetupStep lastStep = (SetupStep)ActionContext.getContext().getRequest(). getSession().getAttribute(config.getSessionTag());
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(lastStep == null)...{
return this.doInit(form, module);
}
SetupStep currentStep = getStep(lastStep.getStep()+1);
currentStep.setLastStep(lastStep);
currentStep.setData((Map)form.toPo(new HashMap()));
checkTheLastSetup(lastStep);
setup(currentStep);
return page(config.getPageName()+lastStep.getStep()+1);
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public Page doLast(WebForm form, Module module)...{
SetupStep currentStep = (SetupStep)ActionContext.getContext().getRequest(). getSession().getAttribute("setupStep");
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(currentStep == null)...{
return this.doInit(form, module);
}
SetupStep lastStep = currentStep.getLastStep();
form.addPo(lastStep.getData());
ActionContext.getContext().getRequest().getSession(). setAttribute(config.getSessionTag(), lastStep);
return page(config.getPageName()+lastStep.getStep());
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
protected SetupStep getStep(int num)...{
return config.getSetupStep(num);
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
protected void checkTheLastSetup(SetupStep lastStep)...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(!lastStep.isOk())...{
throw new SetupException("上一步没有执行或没有执行成功,请先执行上一步操作!");
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
protected void setup(SetupStep step)...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(config.getTotalStepNum()==step.getStep())...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(!step.doFullSetup())...{
throw new SetupException("安装失败!");
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
clean();
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
}else...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(config.setupByStep())...{
step.doSetup();
ActionContext.getContext().getRequest().getSession(). setAttribute(config.getSessionTag(), step);
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
protected void clean()...{
}
}
doNext(WebForm form, Module module)方法执行下一步,doLast(WebForm form, Module module)方法执行上一步,setup(SetupStep step)方法执行安装操作,clean()方法执行安装完成之后的清理工作,这里暂时没有实现,在下一篇中再来完善。
最后是异常处理类了,这里只是实现了一个简单的异常处理,主要是便于以后扩展用。
package
com.easyjf.setup;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
class
SetupException
extends
RuntimeException
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public SetupException(String msg)...{
super(msg);
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public SetupException(String msg, java.lang.Throwable trw) ...{
super(msg, trw);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
今天到此为止,还剩下ISetupConfig接口、SetupStep中的doSetup()方法、SetupAction中的clean()方法没有实现,下一篇再来实现,同时添加一些其它的功能,如验证等。
代码下载:src.rar