NutzWk 企业级WEB后台开发框架开源了
NutzWk
https://github.com/Wizzercn/NutzWk
基于Nutz的开源企业级开发框架。
文件编码全部为UTF-8,可以导入Eclispe、IDEA中,jdk7,tomcat 6/7. 创建空的数据库,首次启动项目会自动初始化数据.
本框架已成功应用于XX省交通厅网络问政平台、XX省交通厅CMS内容管理系统、XX公司舆情监测管理中心等项目。
使用条款: 1、个人开源,可以任意修改使用; 2、商业使用,必须更改后台菜单布局、CSS样式、界面颜色等元素(既:不可使用原始界面用于商业项目)。
Nutz:代码生成器开源了
项目地址: https://github.com/Wizzercn/NutzCodematic
Nutz 代码生成器:
可以生成Pojo类、Action类;
支持MySQL、Oracle、MS SQL2000、ODBC等;
classes是在jdk1.7下编译,可以直接双击 run.bat 运行。
迟迟没放出来的原因是代码写的太烂……
JAVA:网易微博模拟登陆
网易微博登陆验证,第一次请求使用BASE64加密、第二次请求使用MD5+RSA加密,比较变态,于是使用JAVA+JS相结合的方式,调用其JS方法得到加密字符串。
/core1.7.0.js 是经过处理的,删掉几行在JAVA引用中会报错的浏览器对象。
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.velocity.util.StringUtils;
import org.nutz.lang.Files;
import org.nutz.lang.util.ClassTools;
import org.nutz.repo.Base64;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by Wizzer on 14-7-7.
*/
public class Netease {
static String index_url = "http://t.163.com/session";
static String login1_url = "http://reg.163.com/services/httpLoginExchgKeyNew";
static String login2_url = "http://reg.163.com/httpLoginVerifyNew.jsp";
static String status_url = "http://t.163.com/share/check/status";
UrlUtil urlUtil = new UrlUtil();
public static void main(String[] args) {
CookieStore cookieStore = new Netease().login("email", "password");
}
public CookieStore login(String userid, String password) {
try {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(login1_url + "?rnd=" + Base64.encodeToString(userid.getBytes(), true) + "&jsonp=setLoginStatus");
get.setHeader("Accept", "*/*");
get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36");
HttpResponse response = client.execute(get);
int code = response.getStatusLine().getStatusCode();
if (code == 200) {
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = "", res = "";
while (null != (line = reader.readLine())) {
res += line;
}
System.out.println("res:::" + res);
if (res.contains("200")) {
String[] str = StringUtils.split(urlUtil.getStr(res, "setLoginStatus(\"", "\")"), "\\n");
String o = str[1], h = str[2];
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine se = sem.getEngineByName("javascript");
se.eval(getJs());
String jiami = "";
if (se instanceof Invocable) {
Invocable invoke = (Invocable) se;
jiami = invoke.invokeFunction("getCode",
password, o, h).toString();
System.out.println("jiami = " + jiami);
}
DefaultHttpClient client2 = new DefaultHttpClient();
client2.setCookieStore(client.getCookieStore());
HttpGet get2 = new HttpGet(login2_url + "?rcode=" + jiami + "&product=t&jsonp=setLoginStatus&savelogin=0&username=" + userid);
get2.setHeader("Accept", "*/*");
get2.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36");
HttpResponse response2 = client2.execute(get2);
int code2 = response2.getStatusLine().getStatusCode();
if (code2 == 200) {
InputStream in2 = response2.getEntity().getContent();
BufferedReader reader2 = new BufferedReader(new InputStreamReader(in2));
String line2 = "", res2 = "";
while (null != (line2 = reader2.readLine())) {
res2 += line2;
}
System.out.println("res2:::" + res2);
if (res.contains("200")) {
return client2.getCookieStore();
}
}
}
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private String getJs() {
String jscontent = Files.read(ClassTools.getClassLoader().getResource("").getPath() + "netease" + "/core1.7.0.js");
jscontent += "function getCode(p,o,h){\n" +
"\t\t\t\tvar l=new RSAKey();\n" +
"\t\t\t\tl.setPublic(h,o);\n" +
"\t\t\t\treturn l.encrypt(getMd5(p));\t\t\t\t\n" +
" }";
return jscontent;
}
}
Nutz:下载文件(输出文件)
HSSFWorkbook wb = new HSSFWorkbook(); OutputStream out = new FileOutputStream(“x.xls”); wb.write(out); out.close(); return new File(“x.xls”);
ByteArrayOutputStream out = new ……..;
wb………
out.close();
ByteArrayInputStream in = new ………..(out.toByte…);
return in;
Nutz:单机负载均衡或启动多实例注意事项
1、上传文件的文件池路径问题
upload.js
filePool : { type : “cn.xuetang.common.file.FilePool”, args : [“/temp/”, 2000] }
将路径改到项目的路径下
public class FilePool extends NutFilePool {
public FilePool(String homePath, long size) { super(webinfPath(homePath), size); }
private static final String webinfPath(String str) { return Mvcs.getServletContext().getRealPath(“/WEB-INF”)+str; }
}
2、Nutz大字段缓存的问题
.nutz/tmp/dao
将 org.nutz.dao.jdbc.nutz_jdbc_experts.js 拷贝到项目类路径下,修改相应的配置,文件夹不冲突即可。
Linux:后台执行Java类
编写执行文件 job.sh
#!/bin/sh CLASSPATH=classes:lib/druid-1.0.1.jar:lib/htmlparser-1.6.jar:lib/log4j-1.2.17.jar:lib/mysql-connector-java-5.1.26-bin.jar:lib/nutz-1.b.50.jar:lib/ojdbc14.jar:lib/quartz-2.2.1.jar:lib/quartz-jobs-2.2.1.jar:lib/slf4j-api-1.6.6.jar:lib/slf4j-log4j12-1.6.6.jar:lib/sqljdbc4.jar:lib/commons-pool2-2.2.jar: #export CLASSPATH java -classpath $CLASSPATH cn.xuetang.job.MyJob &
赋予job.sh 执行权限
chmod a+x job.sh
后台执行,并把标准输出重定向,关闭终端仍在执行
nohup bash /home/work/ImageTask/job.sh > success.log 2>error.log &
输出在当前控制台,关闭该终端时,将退出启动的进程
/home/work/ImageTask/job.sh
每两分钟启动一次,关闭终端不影响任务
crontab -e
/2 * /home/work/ImageTask/job.sh > success.log 2>error.log &
Nutz:结合文件池实现网络下载文件,压缩成zip后下载
入口函数: @At public void downImage(@Param(“tvid”) int tvid, HttpServletResponse resp, HttpServletRequest req) {
}
使用Nutz文件池:
Globals.FILE_POOL= new NutFilePool(“~/tmp/myfiles”, 10);
源码:
int i = 0;
File f = Globals.FILE_POOL.createFile(".zip");
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f.getAbsolutePath()));
for (Weixin_image image : list) {
i++;
String filename = Strings.sNull(bbinfo.get(image.getUid())) + "_" + Strings.sNull(userinfo.get(image.getUid())) + "_" + i+".jpg";
String picurl = image.getPicurl();
if (!Strings.isBlank(image.getImage_url())) {
picurl = image.getImage_url();
}
URL url = new URL(picurl);
try {
URLConnection conn = url.openConnection();
InputStream inStream = conn.getInputStream();
byte[] buffer = new byte[1204];
out.putNextEntry(new ZipEntry(filename));
while ((byteread = inStream.read(buffer)) != -1) {
bytesum += byteread;
out.write(buffer, 0, byteread);
}
out.closeEntry();
inStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
out.close();
resp.setHeader("Content-Length", "" + f.length());
resp.setHeader("Content-Disposition", "attachment; filename=\"" + tvShow.getPlay_name() + ".zip\"");
Streams.writeAndClose(resp.getOutputStream(), Streams.fileIn(f));
Nutz:结合Jedis实现Redis消息订阅和缓存队列(支持自动重连)
仅作参考。 编辑配置Nutz redis配置文件:
var ioc = {
jedisConfig : {
type : 'cn.xuetang.common.redis.JedisConfig',
fields : {
maxTotal : 200,
maxIdle : 10,
maxWaitMillis:10001,
testOnBorrow:true,
redisUrl:'127.0.0.1',
redisPort:6379,
redisTimeout:1000
}
}
}
package cn.xuetang.common.redis;
/**
* Created by Wizzer on 14-4-8.
*/
public class JedisConfig {
private int maxTotal;
private int maxIdle;
private int maxWaitMillis;
private boolean testOnBorrow;
private String redisUrl;
private int redisPort;
private int redisTimeout;//redis断开后自动重新连接间隔时间
public int getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaxWaitMillis() {
return maxWaitMillis;
}
public void setMaxWaitMillis(int maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public String getRedisUrl() {
return redisUrl;
}
public void setRedisUrl(String redisUrl) {
this.redisUrl = redisUrl;
}
public int getRedisPort() {
return redisPort;
}
public void setRedisPort(int redisPort) {
this.redisPort = redisPort;
}
public int getRedisTimeout() {
return redisTimeout;
}
public void setRedisTimeout(int redisTimeout) {
this.redisTimeout = redisTimeout;
}
}
Nutz入口类加载配置文件,config路径:
@Modules(scanPackage=true)
@Ok("raw")
@Fail("http:500")
@IocBy(type=ComboIocProvider.class,args={
"*org.nutz.ioc.loader.json.JsonLoader","config",
"*org.nutz.ioc.loader.annotation.AnnotationIocLoader","cn.xuetang"})
@SetupBy(value=StartSetup.class)
@UrlMappingBy(value=UrlMappingSet.class)
public class MainModule {
}
初始化配置参数,新建订阅消息处理线程:
public static JedisConfig REDIS_CONFIG;
//声明全局的redis连接池
public static ShardedJedisPool SHARDEDJEDIS_POOL=null;
public static JedisPool JEDIS_POOL=null;
public static void InitRedisConfig() {//初始化redis
REDIS_CONFIG = Mvcs.ctx.getDefaultIoc().get(JedisConfig.class);
JedisPoolUtil jedisPoolUtil=new JedisPoolUtil();
SHARDEDJEDIS_POOL = jedisPoolUtil.getShardedJedisPool();
JEDIS_POOL = jedisPoolUtil.getJedisPool();
}
new Thread(Mvcs.getIoc().get(ImageTask.class)).start();
创建Jedis连接池工具类:
package cn.xuetang.common.redis;
import cn.xuetang.common.config.Globals;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedisPool;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Wizzer on 14-4-8.
*/
public class MyJedis {
private String redisUrl;
private int redisPort;
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(Globals.REDIS_CONFIG.getMaxTotal());
jedisPoolConfig.setMaxIdle(Globals.REDIS_CONFIG.getMaxIdle());
jedisPoolConfig.setMaxWaitMillis(Globals.REDIS_CONFIG.getMaxWaitMillis());
jedisPoolConfig.setTestOnBorrow(Globals.REDIS_CONFIG.isTestOnBorrow());
return jedisPoolConfig;
}
public JedisShardInfo jedisShardInfo(){
return new JedisShardInfo(redisUrl, redisPort);
}
public JedisPool jedisPool(){
return new JedisPool(jedisPoolConfig(),redisUrl, redisPort);
}
public ShardedJedisPool shardedJedisPool(){
this.redisUrl=Globals.REDIS_CONFIG.getRedisUrl();
this.redisPort=Globals.REDIS_CONFIG.getRedisPort();
List jedisList = new ArrayList();
jedisList.add(jedisShardInfo());
return new ShardedJedisPool(jedisPoolConfig(), jedisList);
}
}
实现自己的监听类:
package cn.xuetang.common.redis;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import redis.clients.jedis.JedisPubSub;
/**
* 订阅监听类
* Created by Wizzer on 14-4-8.
*/
public class MyJedisListenter extends JedisPubSub {
private final static Log log = Logs.get();
// 取得订阅的消息后的处理
public void onMessage(String channel, String message) {
log.info(channel + "=" + message);
}
// 初始化订阅时候的处理
public void onSubscribe(String channel, int subscribedChannels) {
log.info(channel + "=" + subscribedChannels);
}
// 取消订阅时候的处理
public void onUnsubscribe(String channel, int subscribedChannels) {
log.info(channel + "=" + subscribedChannels);
}
// 初始化按表达式的方式订阅时候的处理
public void onPSubscribe(String pattern, int subscribedChannels) {
log.info(pattern + "=" + subscribedChannels);
}
// 取消按表达式的方式订阅时候的处理
public void onPUnsubscribe(String pattern, int subscribedChannels) {
log.info(pattern + "=" + subscribedChannels);
}
// 取得按表达式的方式订阅的消息后的处理
public void onPMessage(String pattern, String channel, String message) {
log.info(pattern + "=" + channel + "=" + message);
}
}
获取单例Jedis连接池:
package cn.xuetang.common.redis;
import cn.xuetang.common.config.Globals;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ShardedJedisPool;
import java.util.Date;
/**
* Created by Wizzer on 14-4-8.
*/
public class JedisPoolUtil {
private final static Log log = Logs.get();
public synchronized ShardedJedisPool getShardedJedisPool() {
if (Globals.SHARDEDJEDIS_POOL == null) {
MyJedis myJedis=new MyJedis();
Globals.SHARDEDJEDIS_POOL = myJedis.shardedJedisPool();
}
return Globals.SHARDEDJEDIS_POOL;
}
public synchronized JedisPool getJedisPool() {
if (Globals.JEDIS_POOL== null) {
MyJedis myJedis=new MyJedis();
Globals.JEDIS_POOL = myJedis.jedisPool();
}
return Globals.JEDIS_POOL;
}
}
数据入列同时发布订阅消息:
ShardedJedis shardedJedis=Globals.SHARDEDJEDIS_POOL.getResource();
txt.put("appid", appInfo.getId());
shardedJedis.lpush("image", Json.toJson(txt));
Jedis jedis=Globals.JEDISPOOL.getResource();
jedis.publish("newimage","true");
处理订阅消息的类:
package cn.xuetang.common.task;
import cn.xuetang.common.action.BaseAction;
import cn.xuetang.common.config.Globals;
import cn.xuetang.common.redis.MyJedisListenter;
import cn.xuetang.common.util.DateUtil;
import cn.xuetang.modules.baby.bean.Baby_image;
import cn.xuetang.modules.baby.bean.Baby_info;
import cn.xuetang.modules.user.bean.User_conn_wx;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ShardedJedis;
import java.util.*;
/**
* Created by Wizzer on 14-3-31.
*/
@IocBean
public class ImageTask extends BaseAction implements Runnable {
@Inject
protected Dao dao;
private final static Log log = Logs.get();
public void run() {
try {
log.info("ImageTask start!");
final JedisPoolUtil jedisPoolUtil = new JedisPoolUtil();
boolean isEnable = false;
while (!isEnable) {
try {
jedisPoolUtil.getShardedJedisPool().getResource();
isEnable = true;
} catch (Exception e) {
log.info("The redis connection is not successful,wait " + Globals.REDIS_CONFIG.getRedisTimeout() + "ms try again.");
}
try {
wait(Globals.REDIS_CONFIG.getRedisTimeout());
} catch (Exception e) {
}
}
final ShardedJedis shardedJedis = jedisPoolUtil.getShardedJedisPool().getResource();
final Jedis jedis = jedisPoolUtil.getJedisPool().getResource();
MyJedisListenter listenter = new MyJedisListenter() {
@Override
public void onMessage(String channel, String message) {
//业务代码具体实现
while (shardedJedis.exists("image")) {
String data = shardedJedis.rpop("image");
}
}
};
jedis.subscribe(listenter, "newimage");
} catch (Exception e) {
log.error(e);
}
}
}
Nutz:POST数据的常用方法
Request req = Request.create("http://127.0.0.1/api/user/sendtext?mykey=" + mykey, Request.METHOD.POST);
JsonFormat jsonFormat=new JsonFormat();
jsonFormat.setAutoUnicode(true);//防止中文乱码,以unicode编码,根据需要设置
req.setData(Json.toJson(map,jsonFormat));
req.getHeader().set("Content-Type","application/octet-stream");//设置以字节流传送数据
Response resp = Sender.create(req).send();
if (resp.isOK()) {
log.info(resp.getContent());
}
基于Nutz的CMS内容管理系统大功告成
Nutz:结合quartz-2.2.1实现定时任务
StartSetup 项目启动时新建线程:
package com.hits.core;
import com.hits.modules.cms.task.LoadTask;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.NutConfig;
import org.nutz.mvc.Setup;
import com.hits.common.config.Globals;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
/**
* 类描述: 创建人:Wizzer 联系方式:www.wizzer.cn 创建时间:2013-11-26 下午2:11:13
*/
public class StartSetup implements Setup {
private final static Log log = Logs.get();
@Override
public void destroy(NutConfig config) {
}
@Override
public void init(NutConfig config) {
try {
//初始化Quartz任务
Globals.SCHEDULER = StdSchedulerFactory.getDefaultScheduler();
new Thread(config.getIoc().get(LoadTask.class)).start();
} catch (SchedulerException e) {
log.error(e);
} catch (Exception e) {
log.error(e);
}
}
}
LoadTask.java 从任务表中加载任务,定时执行类方法:
package com.hits.modules.cms.task;
import com.hits.common.action.BaseAction;
import com.hits.common.config.Globals;
import com.hits.modules.cms.task.bean.Cms_task;
import org.apache.commons.lang.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.integration.quartz.NutQuartzJobFactory;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.*;
/**
* Created by Wizzer on 14-3-4.
*/
@IocBean
public class LoadTask extends BaseAction implements Runnable {
@Inject
protected Dao dao;
private final static Log log = Logs.get();
public void run() {
List tasks = daoCtl.list(dao, Cms_task.class, Cnd.where("is_enable", "=", 0));
Globals.SCHEDULER.setJobFactory(new NutQuartzJobFactory());
for (int i = 0; i < tasks.size(); i++) {
Cms_task task = tasks.get(i);
try {
Map map = new HashMap();
if (task.getTask_type() == 2 || task.getTask_type() == 3) {
map.put("site_id", task.getSite_id());
map.put("channel_id", task.getParam_value());
} else if (task.getTask_type() == 1) {
map.put("site_id", task.getSite_id());
}
JobBuilder jobBuilder = JobBuilder.newJob(getClassByTask(task.getJob_class()));
jobBuilder.setJobData(getJobDataMap(map));
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
if (StringUtils.isNotBlank(task.getTask_code())) {
jobBuilder.withIdentity(task.getTask_code(), Scheduler.DEFAULT_GROUP);
triggerBuilder.withIdentity(task.getTask_code(), Scheduler.DEFAULT_GROUP);
} else {
UUID uuid = UUID.randomUUID();
jobBuilder.withIdentity(uuid.toString(), Scheduler.DEFAULT_GROUP);
triggerBuilder.withIdentity(uuid.toString(), Scheduler.DEFAULT_GROUP);
task.setTask_code(uuid.toString());
daoCtl.update(dao, task);
}
triggerBuilder.withSchedule(getCronScheduleBuilder(getCronExpressionFromDB(task)));
//调度任务
Globals.SCHEDULER.scheduleJob(jobBuilder.build(), triggerBuilder.build());
} catch (SchedulerException e) {
log.error(e);
} catch (ClassNotFoundException e) {
log.error(e);
} catch (Exception e) {
log.error(e);
}
}
if(tasks.size()>0){
Globals.SCHEDULER.start();
}
}
public static CronScheduleBuilder getCronScheduleBuilder(String cronExpression) {
return CronScheduleBuilder.cronSchedule(cronExpression);
}
public String getCronExpressionFromDB(Cms_task task) {
if (task.getExecycle() == 2) {
return task.getCron_expression();
} else {
int execycle = task.getTask_interval_unit();
String excep = "";
if (execycle == 5) {//月
excep = "0 " + task.getMinute() + " " + task.getHour() + " " + task.getDay_of_month() + " * ?";
} else if (execycle == 4) {//周
excep = "0 " + task.getMinute() + " " + task.getHour() + " " + " ? " + " * " + task.getDay_of_week();
} else if (execycle == 3) {//日
excep = "0 " + task.getMinute() + " " + task.getHour() + " " + " * * ?";
} else if (execycle == 2) {//时
excep = "0 0 */" + task.getInterval_hour() + " * * ?";
} else if (execycle == 1) {//分
excep = "0 */" + task.getInterval_minute() + " * * * ?";
}
return excep;
}
}
/**
* @param params 任务参数
* @return
*/
private JobDataMap getJobDataMap(Map params) {
JobDataMap jdm = new JobDataMap();
Set keySet = params.keySet();
Iterator it = keySet.iterator();
while (it.hasNext()) {
String key = it.next();
jdm.put(key, params.get(key));
}
return jdm;
}
/**
* @param taskClassName 任务执行类名
* @return
* @throws ClassNotFoundException
*/
@SuppressWarnings("unchecked")
private Class getClassByTask(String taskClassName) throws ClassNotFoundException {
return Class.forName(taskClassName);
}
}
quartz-2.2.1 配置文件:
# Default Properties file for use by StdSchedulerFactory # to create a Quartz Scheduler Instance, if a different # properties file is not explicitly specified. # # 跳过版本检查 # org.quartz.scheduler.skipUpdateCheck=true org.quartz.scheduler.instanceName = DefaultQuartzScheduler org.quartz.scheduler.rmi.export = false org.quartz.scheduler.rmi.proxy = false org.quartz.scheduler.wrapJobExecutionInUserTransaction = false # 用NutIoc接管Quartz的JobFactory,实现用户需要的注入功能 # org.quartz.scheduler.jobFactory.class=org.nutz.integration.quartz.NutQuartzJobFactory org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true org.quartz.jobStore.misfireThreshold = 60000 # 使用内存JobStore # org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
NutQuartzJobFactory.java 下载地址:
https://github.com/nutzam/nutzmore/blob/master/src/org/nutz/integration/quartz/NutQuartzJobFactory.java
Nutz:使用第三方JS控件实现多文件上传的方法
适用于 Nutz+SWFUpload、Nutz+plupload 、Ueditor 等控件文件上传,验证用户身份。
/**
* 验证用户帐号,保存文件
*
* @param tmpFile
* @param filetype
* @param file_password
* @param file_username
* @param errCtx
* @return
*/
@At
@Ok("raw")
@AdaptBy(type = UploadAdaptor.class, args = "ioc:upload")
public JSONObject uploadOneSave(@Param("Filedata") TempFile tmpFile, @Param("ueditor") String ueditor1, @Param("filetype") String filetype, @Param("title") String title, @Param("file_password") String file_password, @Param("file_username") String file_username, AdaptorErrorContext errCtx) {
boolean ueditor = false;//是否是百度编辑器,编辑器对应的JS要做相应的修改
if ("true".equals(StringUtil.null2String(ueditor1)))
ueditor = true;
System.out.println("ueditor::::::::::"+ueditor);
JSONObject js = new JSONObject();
if (errCtx != null) {
if (errCtx.getAdaptorErr() != null) {
if (ueditor) {
js.put("state", errorMsg(errCtx.getAdaptorErr()));
} else {
js.put("error", errorMsg(errCtx.getAdaptorErr()));
js.put("msg", "");
}
System.out.println("js1::::::::::"+js.toString());
return js;
}
for (Throwable e : errCtx.getErrors()) {
if (e != null) {
if (ueditor) {
js.put("state", errorMsg(e));
} else {
js.put("error", errorMsg(e));
js.put("msg", "");
}
return js;
}
}
}
if ("".equals(StringUtil.null2String(file_username)) || "".equals(StringUtil.null2String(file_password))) {
if (ueditor) {
js.put("state", "错误:请配置文件服务器用户名及密码!");
} else {
js.put("error", "错误:请配置文件服务器用户名及密码!");
js.put("msg", "");
}
return js;
}
Ioc ioc = new NutIoc(new JsonLoader("config/fileserver.json"));
String u = ioc.get(FileServer.class, "fileserver").getUsername();
String p = ioc.get(FileServer.class, "fileserver").getPassword();
if (!u.equals(file_username) || !p.equals(DecodeUtil.Decrypt(file_password))) {
if (ueditor) {
js.put("state", "错误:文件服务器用户名或密码不正确!");
} else {
js.put("error", "错误:文件服务器用户名或密码不正确!");
js.put("msg", "");
}
return js;
}
if (tmpFile == null || tmpFile.getFile().length() < 10) {
if (ueditor) {
js.put("state", "错误:文件大小不可小于10B!");
} else {
js.put("error", "错误:文件大小不可小于10B!");
js.put("msg", "");
}
return js;
}
String filename = tmpFile.getMeta().getFileLocalName();
File file = tmpFile.getFile();
String suffixname = Files.getSuffixName(file).toLowerCase();
String ss = FileType.getSuffixname(upload, filetype);
if (!ss.contains(suffixname)) {
if (ueditor) {
js.put("state", "错误:不允许的文件扩展名,允许:" + ss);
} else {
js.put("error", "错误:不允许的文件扩展名,允许:" + ss);
js.put("msg", "");
}
return js;
}
long len = tmpFile.getFile().length();
filename = filename.substring(0, filename.lastIndexOf(".")) + "." + suffixname;
String date = DateUtil.getToday();
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
String fname = uuid + "." + Files.getSuffixName(file).toLowerCase();
String dest = webPath(date, fname, suffixname);
try {
Files.move(file, new File(dest));
} catch (IOException e) {
e.printStackTrace();
if (ueditor) {
js.put("state", "错误:文件服务器IO异常!");
} else {
js.put("error", "错误:文件服务器IO异常!");
js.put("msg", "");
}
return js;
}
JSONObject fs = new JSONObject();
if (ueditor) {
js.put("state", "SUCCESS");
js.put("original", filename);
js.put("url", "/upload/" + FileType.getFileType(upload, suffixname) + "/" + date + "/" + fname);
js.put("title", title);
} else {
fs.put("filename", filename);
fs.put("filepath", "/upload/" + FileType.getFileType(upload, suffixname) + "/" + date + "/" + fname);
fs.put("filesize", StringUtil.getFileSize(len, 2));
js.put("error", "");
js.put("msg", fs);
}
return js;
}
/**
* 获取上传路径,根据文件类型+日期生成路径
*
* @param date
* @param fname
* @param suffixname
* @return
*/
public String webPath(String date, String fname, String suffixname) {
String newfilepath = Mvcs.getServletContext().getRealPath(
"/upload/" + FileType.getFileType(upload, suffixname) + "/" + date
+ "/");
Files.createDirIfNoExists(newfilepath);
return newfilepath + "\\" + fname;
}
/**
* 根据异常提示错误信息
*
* @param t
* @return
*/
private String errorMsg(Throwable t) {
if (t == null || t.getClass() == null) {
return "错误:未知system错误!";
} else {
String className = t.getClass().getSimpleName();
if (className.equals("UploadUnsupportedFileNameException")) {
String name = upload.getContext().getNameFilter();
return "错误:无效的文件扩展名,支持的扩展名:" + name.substring(name.indexOf("(") + 1, name.lastIndexOf(")")).replace("|", ",");
} else if (className.equals("UploadUnsupportedFileTypeException")) {
return "错误:不支持的文件类型!";
} else if (className.equals("UploadOutOfSizeException")) {
return "错误:文件超出" + StringUtil.getFileSize(upload.getContext().getMaxFileSize(), 2) + "MB";
} else if (className.equals("UploadStopException")) {
return "错误:上传中断!";
} else {
return "错误:未知错误!";
}
}
}
项目部署根目录增加:crossdomain.xml 文件,解决上传控件跨域上传的问题(应用和文件服务器分开部署):
Nutz:上传文件的配置项扩展,实现对文件类型的限制及分类
upload.json 配置文件,增加扩展配置项extOption,内容可以由用户自定义,返回一个Map对象。
我要实现功能是,用户在相册里只允许上传图片文件、视频里只允许上传视频格式的文件等,
结合上传控件和JS,实现在页面级和代码级的文件类型分类限制、分类保存等功能。
Nutz issue 提交需求不被接纳所以只好自己实现了,大家如需要的话可以提issue,希望以后Nutz能内置:
https://github.com/nutzam/nutz/issues/568
upload.json:
var ioc = {
upload : {
type : "org.nutz.mvc.upload.UploadAdaptor",
args : [{refer : "uploadCtx"}]
},
uploadCtx : {
type : "org.nutz.mvc.upload.UploadingContext",
args : [{refer: "filePool"}],
fields : {
ignoreNull : true,
maxFileSize : 10485760,
nameFilter : ".+(jpg|gif|png|jpeg|doc|docx|xls|xlsx|ppt|pptx|wps|pdf|txt|chm|mp3|mp4|3gp|rm|swf|flv|asf|wmv|wma|z|zip|rar|ios|jar)",
extOption: {
"images":"jpg,gif,png,jpeg",
"document":"doc,docx,xls,xlsx,ppt,pptx,wps,pdf,txt,chm",
"music":"mp3",
"video":"mp4,3gp,rm,swf,flv,asf,wmv,wma",
"archive":"z,zip,rar,ios,jar"
}
}
},
filePool : {
type : "com.hits.common.file.FilePool",
args : ["/temp/", 2000]
}
};
改写Nutz源码:
org.nutz.mvc.upload.UploadingContext 类,增加代码:
/**
* 一个扩展配置项,用户可自定义内容
*/
private Map extOption;
public Map getExtOption() {
return extOption;
}
public UploadingContext setExtOption(String extOption) {
this.extOption = Json.fromJsonAsMap(String.class,extOption);
return this;
}
增加一个调用类:
FileType.java
/**
* 类描述:
* 创建人:Wizzer
* 联系方式:www.wizzer.cn
* 创建时间:2013-12-25 下午1:13:30
* @version
*/
public class FileType {
/**
* 根据文件名获取文件类型,默认返回other
* @param upload
* @param suffixname
* @return
*/
public static String getFileType(UploadAdaptor upload,String suffixname) {
Map hm=upload.getContext().getExtOption();
String str = StringUtil.null2String(suffixname).toLowerCase();
Set<Map.Entry> set = hm.entrySet();
for (Iterator<Map.Entry> it = set.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if(entry.getValue().toLowerCase().indexOf(str)>-1)
return entry.getKey();
}
return "other";
}
/**
* 根据文件类型获取文件后缀名,默认返回nameFilter配置
* @param upload
* @param filetype
* @return
*/
public static String getSuffixname(UploadAdaptor upload,String filetype) {
Map hm=upload.getContext().getExtOption();
String str = StringUtil.null2String(filetype).toLowerCase();
if(!"".equals(str)){
Set<Map.Entry> set = hm.entrySet();
for (Iterator<Map.Entry> it = set.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if(entry.getKey().toLowerCase().equals(str))
return entry.getValue();
}
}
String name=upload.getContext().getNameFilter();
return name.substring(name.indexOf("(")+1,name.lastIndexOf(")")).replace("|", ",");
}
}
使用示例:
@Inject
protected UploadAdaptor upload;
@At
@Ok("->:/private/file/uploadOne.html")
public void uploadOne(@Param("filetype") String filetype,HttpServletRequest req){
req.setAttribute("filetype", filetype);
req.setAttribute("allowExtensions", FileType.getSuffixname(upload, filetype));
}
如上所述,在限制文件上传类型的同时,还可以将文件分类保存在不同类型的目录下。
Nutz:通过URL路径查找对应的类及入口函数
由于Nutz是零配置的,所以通过URL找到处理类以及跳转的页面,就显得很麻烦,不方便维护。
于是,我在大神兽的指导下,实现如下功能:在项目启动时,将URL路径、类、方法、以及跳转页面写入项目中的一个文件中,方便查看。
@UrlMappingBy(value=UrlMappingSet.class)
public class MainModule {
}
在Nutz入口类,加入 @UrlMappingBy。
UrlMappingSet.java 实现在 /WEB-INF/ 目录下生成 paths.txt 文件,记录路径,文件格式如下:
UrlMappingSet.java 源码:
package com.hits.core;
import java.io.File;
import java.lang.reflect.Method;
import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.nutz.mvc.ActionChainMaker;
import org.nutz.mvc.ActionInfo;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.NutConfig;
import org.nutz.mvc.impl.UrlMappingImpl;
/**
* 类描述: 创建人:Wizzer 联系方式:www.wizzer.cn 创建时间:2013-12-19 下午10:36:17
*
* @version
*/
public class UrlMappingSet extends UrlMappingImpl {
private static int count = 0;
public void add(ActionChainMaker maker, ActionInfo ai, NutConfig config) {
super.add(maker, ai, config);
printActionMappingThis(ai);
}
protected void printActionMappingThis(ActionInfo ai) {
String[] paths = ai.getPaths();
StringBuilder sb = new StringBuilder();
if (null != paths && paths.length > 0) {
sb.append(paths[0]);
for (int i = 1; i < paths.length; i++)
sb.append(",").append(paths[i]);
} else {
throw Lang.impossible();
}
sb.append("\n");
// 打印方法名
Method method = ai.getMethod();
String str;
if (null != method)
str = String.format("%-30s : %-10s", Lang.simpleMetodDesc(method),
method.getReturnType().getSimpleName());
else
throw Lang.impossible();
sb.append("\t" + ai.getModuleType().getName());
sb.append("\n\r");
sb.append("\t" + str);
sb.append("\n");
String filepath = Mvcs.getServletContext().getRealPath("/WEB-INF/")
+ "/paths.txt";
File f = new File(filepath);
if (count == 0) {
Files.write(f, sb.toString());
} else {
Files.appendWrite(f, sb.toString());
}
count++;
}
}
Nutz:自定义取值区间(可分页查询)
使用示例:
/**
你可以自定义取数据区间的值,如下:
offset=0,count=5 取0到5的记录
offset=5,count=10 取5到15的记录
page=3, offset=15,count=20 取15到35的记录
**/
int offset=0;
int count=5;
ExplicitPager pager=new ExplicitPager(offset,count);
QueryResult file=daoCtl.listPage(dao, Oa_doc_file.class, cri,pager);
ExplicitPager 类源码:
package com.hits.common.page;
import org.nutz.dao.pager.Pager;
/**
* 类描述:
* 创建人:Wizzer
* 联系方式:www.wizzer.cn
* 创建时间:2013-12-12 下午3:16:21
* @version
*/
public class ExplicitPager extends Pager {
private static final long serialVersionUID = 1L;
int offset;
int count;
public ExplicitPager(int offset, int count) {
super();
this.offset = offset;
this.count = count;
}
public void setOffset( int offset) {
this.offset = offset;
}
public void setSelectCount( int count) {
this.count = count;
}
@Override
public int getPageSize() {
return count;
}
@Override
public int getOffset() {
return offset;
}
}
分页源代码(可参考上一篇文章,实现自定义SQL多表分页区间取值):
/**
* 根据查询条件分页,返回封装好的QueryResult对象
*
* @param dao
* @param obj
* @param cnd
* @param pager
* @return
*/
public QueryResult listPage(Dao dao, Class obj, Condition cnd,Pager pager) {
List list = dao.query(obj, cnd, pager);
pager.setRecordCount(dao.count(obj, cnd));// 记录数需手动设置
return new QueryResult(list, pager);
}
/**
* 根据自定义SQL分页,返回封装好的QueryResult对象
* @param dao
* @param sql
* @param pager
* @return
*/
public
QueryResult listPageSql(Dao dao, Sql sql, Pager pager) {
if(pager==null)
return null;
pager.setRecordCount(Daos.queryCount(dao, sql.getSourceSql()));// 记录数需手动设置
sql.setPager(pager);
sql.setCallback(Sqls.callback.records());
dao.execute(sql);
return new QueryResult(sql.getList(Map.class), pager);
}
Nutz:自定义SQL多表多字段分页查询方法
easyui.datagrid 调用示例:
@At
@Ok("raw")
public String test(@Param("page") int curPage, @Param("rows") int pageSize){
Sql sql=Sqls.create("select a.name,b.loginname From sys_unit a,sys_user b where a.id=b.unitid");
return daoCtl.listPageJsonSql(dao, sql, curPage, pageSize);
}
源代码:
/**
* 根据自定义SQL分页,返回封装好的QueryResult对象
* @param dao
* @param sql
* @param curPage
* @param pageSize
* @return
*/
public QueryResult listPageSql(Dao dao, Sql sql, int curPage,int pageSize) {
Pager pager = dao.createPager(curPage, pageSize);
pager.setRecordCount(Daos.queryCount(dao, sql.getSourceSql()));// 记录数需手动设置
sql.setPager(pager);
sql.setCallback(Sqls.callback.records());
dao.execute(sql);
return new QueryResult(sql.getList(Map.class), pager);
}
/**
* 根据自定义SQL分页,返回封装好的 Easyui.datagrid JSON
* @param dao
* @param sql
* @param curPage
* @param pageSize
* @return
*/
public String listPageJsonSql(Dao dao, Sql sql, int curPage, int pageSize) {
Pager pager = dao.createPager(curPage, pageSize);
pager.setRecordCount(Daos.queryCount(dao, sql.toString()));// 记录数需手动设置
sql.setPager(pager);
sql.setCallback(Sqls.callback.records());
dao.execute(sql);
Map jsonobj = new HashMap();
jsonobj.put("total", pager.getRecordCount());
jsonobj.put("rows", sql.getList(Map.class));
return Json.toJson(jsonobj);
}
Nutz开发日志
计划基于Nutz开发一套全新的系统管理-权限框架。界面演示:/?p=2586 之前尝试Nutz做过一个demo,但有很多不足,比如没有做防SQL注入、不能兼容多数据库、列表没有实现Ajax等。
基于多年来开发过程接触的各式各样的框架,想整一套开发方便UI简洁的框架,要求有如下功能: 1.开发简便、框架结构清晰 2.性能优越、兼容主流数据库、兼容各版本浏览器 3.UI简洁、开发简单、主流JS插件集成、全Ajax 4.集成权限框架、单点登陆、登陆日志、操作日志 5.工作流引擎、表单自定义 6.代码生成器
JS引入: jquery-1.10.2.min.js ueditor-1.2.6.1 My97Datepicker-4.8 Beta4 zDialog-2.0 ztree-3.5.15
框架引入: Nutz-1.49.r2 log4j-1.2.17 json-1.0 c3p0-0.9.1.2 classes12 mysql-connector-java-5.1.14 ojdbc14 slf4j-api-1.6.1 slf4j-log4j12-1.6.1 sqljdbc4 velocity-1.7 velocity-tools-2.0 velocity-tools-generic-2.0 velocity-tools-view-2.0 json-lib-2.4-jdk15 ezmorph-1.0.6 commons-beanutils-1.7.0 commons-chain-1.1 commons-collections-3.2 commons-digester-1.8 commons-lang-2.2 commons-logging-1.1 commons-validator-1.3.1 待更新。
在Eclipse中处理图片,需要引入两个包: import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; 报错: Access restriction: The type JPEGImageEncoder is not accessible due to restriction on required library C:\Java\jre1.6.0_07\lib\rt.jar
此时解决办法: Eclipse默认把这些受访问限制的API设成了ERROR。只要把Windows-Preferences-Java-Complicer-Errors/Warnings里面的Deprecated and restricted API中的Forbidden references(access rules)选为Warning就可以编译通过。
Nutz开发说明:
您一定了解SSH,还有Strust1+Velocity框架
您一定了解 SSH ,还有 Strust1+Velocity 框架。前者配置文件实在是太多,多层架构,开发一个功能模块涉及很多类、很多关系,一方面上手非常难另外开发效率也不高。后者随着技术发展,对前端技术支持不太友好,比如 Ajax 、 JS 框架等,需要中间跳转页面进行输出,再者也需要配置。
根据项目开发中实际遇到的各种问题,研究各种框架优缺点之后,决定选择 Nutz 国产开源框架作为基础来构建本开发框架。选择 Nutz 有如下优点:
1、 URL“零”配置
您再也不用为繁琐的配置文件烦恼了, Nutz-Mvc 使用全注解的方式来封装 URL ,使用简单的 @At 在类或方法上实现 URL 映射。
2、灵活的多数据源支持
通过 Nutz.Ioc 您只要在数据库配置文件 datasource.json 中配置对象,如 dao1 、 dao2 ,那么在 Action 类中,
@Inject
protected Dao dao1 ;
@Inject
protected Dao dao2 ;
即可使用 daoCtl 的方法直接操作这两个数据库。 daoCtl 里集成了常用的添加、修改、查询、分页等方法。
3、功能强大的视图
Nutz-Mvc 使用 @Ok @Fail 注解,来实现执行成功或失败后的输出,可以外部重定向、内部重定向、返回字符串、返回 JSON 等等,总之您想要什么结果就能给您返回什么,再也不用使用单独的 Action 类来封装 JSON 数据了,您想要随时都可以有。
4、功能强大的数据库操作
Nutz.Dao 封装了常用的数据库操作,减少自定义 SQL 的编写,提高开发效率和安全性,通过工具类
Cnd.where(“loginname”, “=”, user.getLoginname())
和
Sql sql = Sqls.create("DELETE FROM $table WHERE name=@name");
sql.vars().set(“table”,”t_abc”);
sql.params().set(“name”,”Peter”);
中的 sql.params().set() 方法,可以有效的防止 SQL 注入。
更多使用方法请参考本目录下的 nutz 开发手册。
5、 Velocity
对于大家常用的 Velocity 模板语言,本框架也进行了整合。
req.setAttribute(“menulist”, menulist);
将对象输出到页面,在页面上即可直接使用模板语言输出。
在 UI 表现层上面,本次框架也进行了调整,原则上支持纯 Ajax 开发。后台集成 jQuery 、 EasyUI 、 zDialog 、 EasyUI.DataGrid 、 ztree 、 My97DatePicker 等 JS 组件。
1、兼容主流浏览器
您再也不用考虑浏览器兼容性问题,只要您的 JS 全部使用 jQuery 来编写,那么还用担心什么呢,本框架目前在 IE6/IE7/IE8/IE9/IE10/IE11 、 Chrome 、 FrieFox 、 360 测试通过。
2、 zDialog的使用更加简便
通过使用 Ajax 以及对 zDialog 的优化,您再也不需要写中间页面进行页面跳转了。您可以在弹出框的父页面进行弹出表单的提交,在父页面写弹出框按钮的事件。
通过 $DW.$(“#form1”).serialize(); 获取弹出窗口表单内容
$DW.$Z(“name”).focus(); 操作弹出框内容,赋值等
$D.close(); 关闭弹出窗口
让我们看一个完成的示例:
function addSave(){
if($DW.Verify.hasError()){
return;
}
jQuery.ajax({
type: ‘POST’,
url : “/private/sys/unit/addSave” ,
data : $DW.$(“#form1”).serialize(),
success : function (res) {
if(res!=””){
$D.close();
Dialog.alert(“ 添加成功! “);
initTree();
view(res);
}else{
Dialog.alert(“ 添加失败! “);
}
return false;
},
fail : function(res) {
Dialog.alert(“ 系统错误 ?!”);
}
});
}
3、 Ajax分页支持
集成 jQuery.Easyui.DataGrid ,从后台封装的 JSON 数据格式,到前台展示,分页查询从来没有如此简便过。
daoCtl.getListPageJson() 封装了 easyui.datagrid 分页所需格式数据,更不用您手动写烦人的 JSON 了。
4、权限控制到按钮
您只需要在 JS 中使用
Page.initBtn();
$!initBtn
即可控制页面按钮是否可用,当然,在此之前,您需要在资源菜单管理中添加按钮 ID ,在角色管理中对用户分配按钮权限。
Nutz:Java开发框架搭建基本完成
一直想拥有一个开发快捷、界面美观、用户体验好的java开发框架,网罗不来只好自己动手,现在框架基础功能已完成,界面还需要美工优化一下。(以下图片点击查看全图)

1、系统采用国产开源Nutz框架,实现项目的“零配置”(这可比SSH轻松多了),Nutz在一个类中可以随意定义访问地址,返回String、JSON、对象或跳转到具体页面,也可以利用Velocity打印结果; 2、系统集成泽元CMS1.0系统中的弹出窗口及页面验证、控制JS,并结合jquery easyui实现标签页; 3、系统无缝集成jQuery,jquery和nutz真是天作之合,可方便的实现ajax功能,另外你也可以使用velocity在页面输出内容,比较灵活; 4、已完成系统管理模块,数据表设计合理,权限控制到按钮级,可按机构建用户角色等。
下面是一段简单的Nutz类实现,Nutz更多介绍可以访问官网:http://nutzam.com/
@IocBean
@At("/private")//访问路径
@Filters({ @By(type = GlobalsFilter.class) })
public class LoginAction extends BaseAction {
@At //访问路径,不指定则为方法名称,即:/private/logout
@Ok(">>:/private/login")//外连接,跳转到 /private/login
public void logout(HttpSession session) {
session.removeAttribute("userSession");
}
@At("/login")
@Ok("->:/private/login.html")//内连接,输出到 /private/login.html
public void login(HttpServletRequest req) {
req.setAttribute("menulist", "helloworld");//页面可以用velocity语言输出
}
@At
@Ok("raw") //返回字符串类型,也可以是json、对象等
public int Online(@Param("loginname") String loginname,HttpSession session) {
Sys_user user = (Sys_user) session.getAttribute("userSession");
if(user==null){
return -2;
}
if (loginname != null&&!"".equals(loginname)) {
OnlineUtil.addUser(loginname, String.valueOf(1));
}
return OnlineUtil.getOnlineCount(String.valueOf(1));
}
}





