/** * 本原子,每次唤醒,都去运行 schedule, <br> * schedule 负责为每个 handle 启动新线程运行,以便本原子运行时间精确 * * @author zozoh([email protected]) */ public class TimerRunnerAtom extends AbstractAtom { private static final Log log = Logs.get(); @Override protected long exec() { // 首先以间隔 1000 ms 的时间,自旋等待 schedule 的准备完成 while (!factory.schedule().isReady() && !factory.schedule().isStop()) { if (log.isDebugEnabled()) log.debug("check schedule ..."); synchronized (factory.schedule()) { try { factory.schedule().wait(1000); } catch (InterruptedException e) { throw Lang.wrapThrow(e); } } } return factory.schedule().runSlot(Calendar.getInstance(), log); } public static final String NAME = "SCHD.run"; @Override public String getName() { return NAME; } }
/** * 统计每天/每小时的独立活跃用户数 * * @author wendal */ public class DailyUniqueUsersProcessor extends AbstractProcessor implements RedisKey { protected JedisPool pool; private static final Log log = Logs.get(); public void process(ActionContext ac) throws Throwable { try { if (pool == null) pool = Mvcs.getIoc().get(JedisPool.class); int uid = Toolkit.uid(); if (uid > 0) { try (Jedis jedis = pool.getResource()) { Pipeline pipe = jedis.pipelined(); pipe.setbit(RKEY_ONLINE_DAY + Toolkit.today_yyyyMMdd(), uid, true); pipe.setbit(RKEY_ONLINE_HOUR + Toolkit.today_yyyyMMddHH(), uid, true); pipe.sync(); } } } catch (Exception e) { if (e instanceof JedisConnectionException) { log.debug("jedis is down? ignore error"); } else { log.debug("something wrong? ignore error", e); } } doNext(ac); } }
/** * 一个 Web 服务的启动器 * * @author zozoh([email protected]) */ public class WebLauncher { private static final Log log = Logs.get(); /** * 启动的主函数,接受一个参数,为 web 服务器的配置文件路径。如果没有这个参数,默认在 classpath 下寻找 "web.properties" 文件。 * * <p>这个文件遵循 Nutz 多行属性文件规范,同时必须具备如下的键: * * <ul> * <li>"app-root" - 应用的根路径,比如 "~/workspace/git/danoo/strato/domain/ROOT" * <li>"app-port" - 应用监听的端口,比如 8080 * <li>"app-rs" - 应用静态资源的地址前缀,比如 "http://localhost/strato",或者 "/rs" 等 * <li>"app-classpath" - 应用的类路径,可多行 * <li>"admin-port" - 应用的管理端口,比如 8081 * </ul> * * 这个文件的例子,请参看源码 conf 目录下的 web.properties * * @param args 接受一个参数作为 web 服务器的配置文件路径 */ public static void main(String[] args) { String path = Strings.sBlank(Lang.first(args), Webs.CONF_PATH); log.infof("launch by '%s'", path); final WebServer server = new WebServer(new WebConfig(path)); server.run(); log.info("Server is down!"); } }
@IocBean public class SeckenAuthService { private static Log log = Logs.get(); @Inject Dao dao; @Inject Secken secken; @OnEvent("get_auth_qr") public void getAuthQr(SocketIOClient client, Object data, AckRequest ackRequest) { NutMap re = new NutMap(); try { // TODO 可配置 SeckenResp resp = secken .getAuth(1, "https://nutz.cn/secken/callback/" + R.UU32(client.getSessionId())) .check(); String url = resp.qrcode_url(); re.put("ok", true); re.put("url", url); } catch (Exception e) { log.debug("获取洋葱授权二维码识别", e); re.put("msg", "获取洋葱授权二维码识别"); } client.sendEvent("new_auth_qr", re); } @OnConnect public void welcome(SocketIOClient client) { client.sendEvent("login", ""); } }
/** * @author Wizzer.cn * @time 2012-9-13 上午10:54:04 */ public class UserLoginFilter implements ActionFilter { private final Log log = Logs.get(); @Override public View match(ActionContext context) { Sys_user user = (Sys_user) context.getRequest().getSession().getAttribute("userSession"); if (user == null) { ServerRedirectView view = new ServerRedirectView("/include/error/nologin.jsp"); return view; } Hashtable<String, String> btnmap = user.getBtnmap(); if (btnmap != null) { String initBtn = ""; String bts = btnmap.get( Strings.sNull( context.getRequest().getRequestURI().replace(Globals.APP_BASE_NAME, ""))); if (bts != null && bts.indexOf(",") > 0) { String[] tb = StringUtils.split(bts, ","); for (int i = 0; i < tb.length; i++) { initBtn += "$Z(\"" + tb[i] + "\").enable();\r\n"; } } context.getRequest().setAttribute("initBtn", initBtn); } context.getRequest().setAttribute("curuser", user); return null; } }
public class EjectByGetter implements Ejecting { private static final Log log = Logs.get(); private Method getter; public EjectByGetter(Method getter) { this.getter = getter; } public Object eject(Object obj) { try { return null == obj ? null : getter.invoke(obj); } catch (InvocationTargetException e) { throw new FailToGetValueException("getter=" + getter, e); } catch (Exception e) { if (log.isInfoEnabled()) log.info("Fail to value by getter", e); throw Lang.makeThrow( "Fail to invoke getter %s.'%s()' because [%s]: %s", getter.getDeclaringClass().getName(), getter.getName(), Lang.unwrapThrow(e), Lang.unwrapThrow(e).getMessage()); } } }
public void destroy(NutConfig nc) { // Trace. Trace.removeReceiver(receiver); try { receiver.close(); } catch (IOException ex) { Logs.get().error("MainSetup close receiver error", ex); } }
public NutMap wx_minfo(String info, HttpServletRequest req) throws Exception { String uri = myWxApi.getCompleteUri(req); // 获取完整uri,否则jsticket无法使用 Logs.get().debugf("request url=%s", uri); String decinfo = BlueDes.decrypt(info); HashMap<String, String> minfo = Json.fromJson(HashMap.class, decinfo); return wx_init(uri, minfo); }
@At("/Tmp1") @IocBean(fields = {"dao"}) public class Tmp1Module extends EntityService<Tmp1> { private static final Log log = Logs.get(); @At public Object list(@Param("page") int page, @Param("rows") int rows) { if (rows < 1) rows = 10; Pager pager = dao().createPager(page, rows); List<Tmp1> list = dao().query(Tmp1.class, null, pager); Map<String, Object> map = new HashMap<String, Object>(); if (pager != null) { pager.setRecordCount(dao().count(Tmp1.class)); map.put("pager", pager); } map.put("list", list); return map; } @At public boolean add(@Param("..") Tmp1 obj) { try { dao().insert(obj); return true; } catch (Throwable e) { if (log.isDebugEnabled()) log.debug("E!!", e); return false; } } @At public boolean delete(@Param("..") Tmp1 obj) { try { dao().delete(obj); return true; } catch (Throwable e) { if (log.isDebugEnabled()) log.debug("E!!", e); return false; } } @At public boolean update(@Param("..") Tmp1 obj) { try { dao().update(obj); return true; } catch (Throwable e) { if (log.isDebugEnabled()) log.debug("E!!", e); return false; } } }
/** * 处理跨屏登陆的入口授权 * * @author wendal */ public class CrossScreenAuthentication extends FormAuthenticationFilter { private static final Log log = Logs.get(); protected long timeout = 60; protected SysLogService sysLogService; public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if ("GET".equals(req.getMethod()) && !Strings.isBlank(req.getParameter("token"))) { String token = req.getParameter("token"); try { token = Toolkit._3DES_decode(CrossScreen.csKEY, Toolkit.hexstr2bytearray(token)); NutMap map = Json.fromJson(NutMap.class, token); Long t = map.getLong("t", -1); if (System.currentTimeMillis() - t > timeout * 1000) { resp.sendError(403); // TODO 提示token已经过期 return false; } Integer uid = (Integer) map.get("uid"); if (uid != null) { // 有登陆用户 Toolkit.doLogin(new CrossScreenUserToken(uid), uid); if (sysLogService == null) { try { sysLogService = Mvcs.ctx().getDefaultIoc().get(SysLogService.class); } catch (Throwable e) { } } sysLogService.async(SysLog.c("method", "用户登陆", null, uid, "用户通过跨屏二维码登陆")); } resp.sendRedirect(map.getString("url")); return false; } catch (Exception e) { log.debug("bad token?", e); resp.sendError(502); return false; } } else { resp.sendError(403); return false; } } public void setTimeout(long timeout) { this.timeout = timeout; } }
/** * UserAPI 测试 * * @author 凡梦星尘([email protected]) * @since 2.0 */ @FixMethodOrder(MethodSorters.JVM) public class UserAPITest extends APITestSupport { private static final Log log = Logs.get(); private UserAPI ua; @Before public void init() { log.info("====== UserAPITest ====== "); super.init(); ua = WechatAPIImpl.create(mpAct); } @Test public void testGetFollowerList() { log.info("====== UserAPI#getFollowerList ====== "); FollowList fl = ua.getFollowerList(null); assertNotNull(fl); log.info(fl); } @Test public void testGetFollower() { log.info("====== UserAPI#getFollower ====== "); Follower f = ua.getFollower(openId, null); assertNotNull(f); log.info(f); } @Test public void testUpdateRemark() { log.info("====== UserAPI#updateRemark ====== "); boolean flag = ua.updateRemark(openId, "Youself"); assertTrue(flag); log.info(flag); } @Test public void testGetFollowers() { log.info("====== UserAPI#getFollowers ====== "); List<Follower2> getfs = new ArrayList<Follower2>(); getfs.add(new Follower2(openId)); getfs.add(new Follower2(_cr.get("openId2"))); List<Follower> fs = ua.getFollowers(getfs); assertNotNull(fs); assertEquals(2, fs.size()); log.info(fs); } }
public class BeetlViewMaker2 extends BeetlViewMaker { public static final Log log = Logs.get(); public BeetlViewMaker2() throws IOException { super(); } public void init() { try { super.init(); } catch (Exception e) { throw new RuntimeException(e); } // 添加全局变量 Map<String, Object> share = groupTemplate.getSharedVars(); if (share == null) { share = new NutMap(); groupTemplate.setSharedVars(share); } Ioc ioc = Mvcs.getIoc(); share.put("ioc", ioc); PropertiesProxy conf = ioc.get(PropertiesProxy.class, "conf"); share.put("conf", conf.toMap()); if (!conf.getBoolean("cdn.enable", false) || Strings.isBlank(conf.get("cdn.urlbase"))) { share.put("cdnbase", ""); } else { share.put("cdnbase", conf.get("cdn.urlbase")); MarkdownFunction.cdnbase = conf.get("cdn.urlbase"); } } @Override public View make(Ioc ioc, String type, String value) { if ("beetl".equals(type)) return new BeetlView(render, value) { @Override public void render(HttpServletRequest req, HttpServletResponse resp, Object obj) throws Throwable { Stopwatch sw = Stopwatch.begin(); super.render(req, resp, obj); sw.stop(); log.debug("render time=" + sw.getDuration()); } }; return null; } }
/** * 检验优惠券和活动是否有效的任务 * * @author 凡梦星尘([email protected]) * @since 2015/5/4 * @version 1.0.0 */ @IocBean public class CheckValidJob implements Job { private static final Log log = Logs.get(); @Inject private TrailService trailService; @Inject private CouponService couponService; @Override public void execute(JobExecutionContext jec) throws JobExecutionException { trailService.makeInvalid(); couponService.makeInvalid(); } }
/** * @author Kerbores([email protected]) * @project thunder-web * @file Test.java * @description 测试任务 * @time 2016年3月8日 上午10:51:26 */ @IocBean @Scheduled(cron = "0 */45 * * * ? ") public class TestTask implements Job { private static Log LOG = Logs.getLog(TestTask.class); /* * (non-Javadoc) * * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) */ @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { LOG.debug("running..."); } }
/** @author <a href="mailto:[email protected]">Json Shen</a> 2010-7-18 下午02:44:08 */ public class MyUrlMapImpl extends UrlMappingImpl { private static final Log log = Logs.getLog(MyUrlMapImpl.class); /** 添加URL时初始化权限 */ @Override public void add(ActionChainMaker maker, ActionInfo ai, NutConfig config) { super.add(maker, ai, config); Method method = ai.getMethod(); Authority a = method.getAnnotation(Authority.class); if (null == a) { return; } String id = a.value(); String module = a.module(); String desc = a.desc(); boolean isDefault = a.isDefault(); // 在这里可以把权限写入数据库以及其它操作,此处为运行demo简单只写日志. log.infof("AuthID=%s Module=%s Default=%s Desc=%s", id, module, isDefault, desc); } }
public class SystemSetup implements Setup { private static final Log log = Logs.get(); private static final int INTERVAL = 30 * 1000; // 在线用户的未读消息刷新间隔时间 30秒 private static Timer timer = new Timer("schedule Runner"); // 建立调度任务线程 @Override public void init(NutConfig config) { Ioc ioc = config.getIoc(); final Dao dao = ioc.get(null, "dao"); timer.schedule( new TimerTask() { @Override public void run() {} }, 1000, INTERVAL); if (log.isInfoEnabled()) { log.info("系统初始化完毕"); } } @Override public void destroy(NutConfig config) { if (timer != null) { timer.cancel(); timer = null; if (log.isInfoEnabled()) { log.info("schedule Runner 已关闭"); } } } }
@ZBusConsumer(mq = "email") @IocBean(name = "emailService") public class EmailServiceImpl implements EmailService, MessageHandler { private static final Log log = Logs.get(); @Inject("refer:$ioc") protected Ioc ioc; public boolean send(String to, String subject, String html) { try { HtmlEmail email = ioc.get(HtmlEmail.class); email.setSubject(subject); email.setHtmlMsg(html); email.addTo(to); email.buildMimeMessage(); email.sendMimeMessage(); return true; } catch (Throwable e) { log.info("send email fail", e); return false; } } public boolean isSupport(Object event) { return event instanceof Email; } public void handle(Message msg, Session sess) throws IOException { Email email = Json.fromJson(Email.class, msg.getBodyString()); try { email.send(); } catch (EmailException e) { e.printStackTrace(); } } }
/** * 一个创建 Castor 的工厂类。它的使用方式是: * * <pre> * Castors.me().cast(obj, fromType, toType); * </pre> * * @author zozoh([email protected]) * @author Wendal([email protected]) */ public class Castors { private static final Log log = Logs.get(); /** @return 单例 */ public static Castors me() { return one; } /** @return 一个新的 Castors 实例 */ public static Castors create() { return new Castors(); } /** 如何抽取对象的类型级别 */ private TypeExtractor extractor; /** Castor 的配置 */ private Object setting; /** * 设置转换的配置 * * <p>配置对象所有的共有方法都会被遍历。只要这个方法有一个且只有一个参数,并且该参数 是一个 org.nutz.castor.Castor 接口的实现类。那么就会被认为是对该种 Castor * 的一个 配置。 * * <p>当初始化这个 Castor 之前,会用这个方法来设置一下你的 Castor (如果你的 Castor 需要配置的话) * * @param obj 配置对象。可以是任意的 Java 对象。 */ public synchronized Castors setSetting(Object obj) { if (obj != null) { setting = obj; this.reload(); } return this; } /** * 设置自定义的对象类型提取器逻辑 * * @param te 类型提取器 */ public synchronized Castors setTypeExtractor(TypeExtractor te) { extractor = te; return this; } private Castors() { setting = new DefaultCastorSetting(); reload(); } private void reload() { HashMap<Class<?>, Method> settingMap = new HashMap<Class<?>, Method>(); for (Method m1 : setting.getClass().getMethods()) { Class<?>[] pts = m1.getParameterTypes(); if (pts.length == 1 && Castor.class.isAssignableFrom(pts[0])) settingMap.put(pts[0], m1); } this.map = new HashMap<Integer, Castor<?, ?>>(); ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); classes.addAll(defaultCastorList); for (Class<?> klass : classes) { try { if (Modifier.isAbstract(klass.getModifiers())) continue; if (!Castor.class.isAssignableFrom(klass)) continue; fillMap(klass, settingMap); } catch (Throwable e) { if (log.isWarnEnabled()) log.warnf("Fail to create castor [%s] because: %s", klass, e.getMessage()); } } if (log.isDebugEnabled()) log.debugf("Using %s castor for Castors", map.size()); } public void addCastor(Class<?> klass) { try { fillMap(klass, new HashMap<Class<?>, Method>()); } catch (Throwable e) { throw Lang.wrapThrow(Lang.unwrapThrow(e)); } } /** * 填充 map .<br> * 在map中使用hash值来做为key来进行存储 * * @param klass * @param settingMap * @throws InstantiationException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException */ private void fillMap(Class<?> klass, HashMap<Class<?>, Method> settingMap) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Castor<?, ?> castor = (Castor<?, ?>) klass.newInstance(); if (!map.containsKey(castor.hashCode())) { map.put(castor.hashCode(), castor); } Method m = settingMap.get(castor.getClass()); if (null == m) { for (Entry<Class<?>, Method> entry : settingMap.entrySet()) { Class<?> cc = entry.getKey(); if (cc.isAssignableFrom(klass)) { m = settingMap.get(cc); break; } } } if (null != m) m.invoke(setting, castor); } /** First index is "from" (source) The second index is "to" (target) */ // private Map<String, Map<String, Castor<?, ?>>> map; private Map<Integer, Castor<?, ?>> map; /** * 转换一个 POJO 从一个指定的类型到另外的类型 * * @param src 源对象 * @param fromType 源对象类型 * @param toType 目标类型 * @param args 转换时参数。有些 Castor 可能需要这个参数,比如 Array2Map * @return 目标对象 * @throws FailToCastObjectException 如果没有找到转换器,或者转换失败 */ @SuppressWarnings({"unchecked", "rawtypes"}) public <F, T> T cast(Object src, Class<F> fromType, Class<T> toType, String... args) throws FailToCastObjectException { if (null == src) { // 原生数据的默认值 if (toType.isPrimitive()) { if (toType == int.class) return (T) Integer.valueOf(0); else if (toType == long.class) return (T) Long.valueOf(0L); else if (toType == byte.class) return (T) Byte.valueOf((byte) 0); else if (toType == short.class) return (T) Short.valueOf((short) 0); else if (toType == float.class) return (T) Float.valueOf(.0f); else if (toType == double.class) return (T) Double.valueOf(.0); else if (toType == boolean.class) return (T) Boolean.FALSE; else if (toType == char.class) return (T) Character.valueOf(' '); throw Lang.impossible(); } // 是对象,直接返回 null return null; } if (fromType == toType || toType == null || fromType == null) return (T) src; if (fromType.getName().equals(toType.getName())) return (T) src; if (toType.isAssignableFrom(fromType)) return (T) src; Mirror<?> from = Mirror.me(fromType, extractor); if (from.canCastToDirectly(toType)) // Use language built-in cases return (T) src; Castor c = find(from, toType); if (null == c) throw new FailToCastObjectException( String.format( "Can not find castor for '%s'=>'%s' in (%d) because:\n%s", fromType.getName(), toType.getName(), map.size(), "Fail to find matched castor")); try { return (T) c.cast(src, toType, args); } catch (FailToCastObjectException e) { throw e; } catch (Exception e) { throw new FailToCastObjectException( String.format( "Fail to cast from <%s> to <%s> for {%s} because:\n%s:%s", fromType.getName(), toType.getName(), src, e.getClass().getSimpleName(), e.getMessage()), Lang.unwrapThrow(e)); } } /** * 获取一个转换器 * * @param from 源类型 * @param to 目标类型 * @return 转换器 */ public <F, T> Castor<F, T> find(Class<F> from, Class<T> to) { return find(Mirror.me(from), to); } @SuppressWarnings("unchecked") private <F, T> Castor<F, T> find(Mirror<F> from, Class<T> toType) { Mirror<T> to = Mirror.me(toType, extractor); Class<?>[] fets = from.extractTypes(); Class<?>[] tets = to.extractTypes(); for (Class<?> ft : fets) { for (Class<?> tt : tets) { if (map.containsKey(Castor.fetchHash(ft, tt))) { return (Castor<F, T>) map.get(Castor.fetchHash(ft, tt)); } } } return null; } /** * 转换一个 POJO 到另外的类型 * * @param src 源对象 * @param toType 目标类型 * @return 目标对象 * @throws FailToCastObjectException 如果没有找到转换器,或者转换失败 */ public <T> T castTo(Object src, Class<T> toType) throws FailToCastObjectException { return cast(src, null == src ? null : src.getClass(), toType); } /** * 判断一个类型是否可以被转换成另外一个类型 * * <p>判断的依据就是看是否可以直接被转型,以及能不能找到一个专有的转换器 * * @param fromType 起始类型 * @param toType 目标类型 * @return 是否可以转换 */ public boolean canCast(Class<?> fromType, Class<?> toType) { if (Mirror.me(fromType).canCastToDirectly(toType)) return true; Castor<?, ?> castor = this.find(fromType, toType); return !(castor instanceof Object2Object); } /** * 将一个 POJO 转换成字符串 * * @param src 源对象 * @return 字符串 */ public String castToString(Object src) { try { return castTo(src, String.class); } catch (FailToCastObjectException e) { return String.valueOf(src); } } private static List<Class<?>> defaultCastorList = new ArrayList<Class<?>>(100); static { defaultCastorList.add(org.nutz.castor.castor.String2Character.class); defaultCastorList.add(org.nutz.castor.castor.Calendar2String.class); defaultCastorList.add(org.nutz.castor.castor.Calendar2Timestamp.class); defaultCastorList.add(org.nutz.castor.castor.Collection2Collection.class); defaultCastorList.add(org.nutz.castor.castor.Collection2Object.class); defaultCastorList.add(org.nutz.castor.castor.File2String.class); defaultCastorList.add(org.nutz.castor.castor.String2Timestamp.class); defaultCastorList.add(org.nutz.castor.castor.Map2Object.class); defaultCastorList.add(org.nutz.castor.castor.Datetime2String.class); defaultCastorList.add(org.nutz.castor.castor.Array2String.class); defaultCastorList.add(org.nutz.castor.castor.String2SqlTime.class); defaultCastorList.add(org.nutz.castor.castor.Number2Calendar.class); defaultCastorList.add(org.nutz.castor.castor.SqlDate2Timestamp.class); defaultCastorList.add(org.nutz.castor.castor.Object2Mirror.class); defaultCastorList.add(org.nutz.castor.castor.String2Collection.class); defaultCastorList.add(org.nutz.castor.castor.Datetime2Calendar.class); defaultCastorList.add(org.nutz.castor.castor.Class2String.class); defaultCastorList.add(org.nutz.castor.castor.Number2String.class); defaultCastorList.add(org.nutz.castor.castor.Number2Enum.class); defaultCastorList.add(org.nutz.castor.castor.Array2Object.class); defaultCastorList.add(org.nutz.castor.castor.Timestamp2Calendar.class); defaultCastorList.add(org.nutz.castor.castor.Timestamp2String.class); defaultCastorList.add(org.nutz.castor.castor.Datetime2SqlTime.class); defaultCastorList.add(org.nutz.castor.castor.TimeZone2String.class); defaultCastorList.add(org.nutz.castor.castor.String2Boolean.class); defaultCastorList.add(org.nutz.castor.castor.Datetime2SqlDate.class); defaultCastorList.add(org.nutz.castor.castor.Object2List.class); defaultCastorList.add(org.nutz.castor.castor.SqlDate2String.class); defaultCastorList.add(org.nutz.castor.castor.Timestamp2SqlDate.class); defaultCastorList.add(org.nutz.castor.castor.Map2String.class); defaultCastorList.add(org.nutz.castor.castor.DateTimeCastor.class); defaultCastorList.add(org.nutz.castor.castor.Boolean2String.class); defaultCastorList.add(org.nutz.castor.castor.String2Class.class); defaultCastorList.add(org.nutz.castor.castor.String2Pattern.class); defaultCastorList.add(org.nutz.castor.castor.String2Map.class); defaultCastorList.add(org.nutz.castor.castor.Mirror2Class.class); defaultCastorList.add(org.nutz.castor.castor.SqlTime2String.class); defaultCastorList.add(org.nutz.castor.castor.Object2Object.class); defaultCastorList.add(org.nutz.castor.castor.String2TimeZone.class); defaultCastorList.add(org.nutz.castor.castor.Character2Number.class); defaultCastorList.add(org.nutz.castor.castor.String2Number.class); defaultCastorList.add(org.nutz.castor.castor.Number2Boolean.class); defaultCastorList.add(org.nutz.castor.castor.Timestamp2SqlTime.class); defaultCastorList.add(org.nutz.castor.castor.Timestamp2Long.class); defaultCastorList.add(org.nutz.castor.castor.String2Calendar.class); defaultCastorList.add(org.nutz.castor.castor.String2Email.class); defaultCastorList.add(org.nutz.castor.castor.Calendar2Datetime.class); defaultCastorList.add(org.nutz.castor.castor.Enum2String.class); defaultCastorList.add(org.nutz.castor.castor.Collection2Map.class); defaultCastorList.add(org.nutz.castor.castor.Array2Array.class); defaultCastorList.add(org.nutz.castor.castor.Number2Datetime.class); defaultCastorList.add(org.nutz.castor.castor.Map2Array.class); defaultCastorList.add(org.nutz.castor.castor.Array2Collection.class); defaultCastorList.add(org.nutz.castor.castor.String2Set.class); defaultCastorList.add(org.nutz.castor.castor.String2Mirror.class); defaultCastorList.add(org.nutz.castor.castor.Mirror2String.class); defaultCastorList.add(org.nutz.castor.castor.SqlTime2Timestamp.class); defaultCastorList.add(org.nutz.castor.castor.Object2Class.class); defaultCastorList.add(org.nutz.castor.castor.Object2Map.class); defaultCastorList.add(org.nutz.castor.castor.String2File.class); defaultCastorList.add(org.nutz.castor.castor.Boolean2Number.class); defaultCastorList.add(org.nutz.castor.castor.Map2Collection.class); defaultCastorList.add(org.nutz.castor.castor.String2SqlDate.class); defaultCastorList.add(org.nutz.castor.castor.String2Array.class); defaultCastorList.add(org.nutz.castor.castor.String2Object.class); defaultCastorList.add(org.nutz.castor.castor.Number2Number.class); defaultCastorList.add(org.nutz.castor.castor.Class2Mirror.class); defaultCastorList.add(org.nutz.castor.castor.String2Datetime.class); defaultCastorList.add(org.nutz.castor.castor.Calendar2Long.class); defaultCastorList.add(org.nutz.castor.castor.Number2Character.class); defaultCastorList.add(org.nutz.castor.castor.Collection2String.class); defaultCastorList.add(org.nutz.castor.castor.Datetime2Long.class); defaultCastorList.add(org.nutz.castor.castor.Array2Map.class); defaultCastorList.add(org.nutz.castor.castor.Number2Timestamp.class); defaultCastorList.add(org.nutz.castor.castor.Collection2Array.class); defaultCastorList.add(org.nutz.castor.castor.Enum2Number.class); defaultCastorList.add(org.nutz.castor.castor.Object2String.class); defaultCastorList.add(org.nutz.castor.castor.Timestamp2Datetime.class); defaultCastorList.add(org.nutz.castor.castor.Datetime2Timpestamp.class); defaultCastorList.add(org.nutz.castor.castor.Map2Enum.class); defaultCastorList.add(org.nutz.castor.castor.String2Enum.class); } private static Castors one = new Castors(); }
/** * 用户权限模块,菜单管理控制器类 * * @author 刘立立 */ @IocBean public class RoleAction { private static final Log log = Logs.get(); @Inject("refer:roleServiceImpl") public RoleServiceImpl roleService; @Inject("refer:menusServiceImpl") public MenusServiceImpl menusService; /** * 获取所有角色信息 * * @param request * @param response * @return */ // @RequestMapping(value = "queryAllRoleList", method = { RequestMethod.POST }) public String queryAllRoleList(HttpServletRequest request, HttpServletResponse response) { int page = Integer.parseInt(request.getParameter("page")); // 当前页数 int rows = Integer.parseInt(request.getParameter("rows")); // 每页多少行 int startNum = page * rows - rows; // 分页查询开始位置 String roleJsonStr = ""; Datagrid datagrid = new Datagrid(); int roleTotalCount = roleService.queryRoleTotalCount(); // 统计角色数量 List<Role> roleList = roleService.quryAllRoleList(startNum, rows); // 查询角色列表 datagrid.setRows(roleList); datagrid.setTotal(roleTotalCount); try { // 将查询的角色集合list转换成json格式字符串 roleJsonStr = Json.toJson(datagrid); // JsonUtils.objectToJackson(datagrid, Datagrid.class); PrintWriter out = response.getWriter(); out.print(roleJsonStr); out.flush(); out.close(); request.setAttribute("roleJsonStr", roleJsonStr); } catch (Exception e) { log.error("处理json数据报错:" + e.getStackTrace()); } log.info(roleJsonStr); return null; } /** * 新增角色 * * @param request * @param response * @return */ // @RequestMapping(value = "saveRole", method = { RequestMethod.POST }) public String saveRole( HttpServletRequest request, HttpServletResponse response, Role role, String selectAuthIds) { String msg = ""; try { roleService.saveRole(request, role, selectAuthIds); // 保存角色 msg = "1"; // 保存角色成功 } catch (Exception e) { msg = "0"; // 保存角色失败 log.error("保存角色失败:" + e.getStackTrace()); } // ResponseUtils.renderText(response, msg); return null; } /** * 通过id获取角色 * * @param request * @param id * @return */ // @RequestMapping(value = "getRoleById", method = { RequestMethod.GET }) public String getRoleById(HttpServletRequest request, String id) { Role role = roleService.getRoleById(id); request.setAttribute("role", role); return "role/role-input"; } /** * @param request * @param id * @return */ // @RequestMapping(value = "getRoleByCode", method = { RequestMethod.GET }) public String getRoleByCode(HttpServletRequest request, String id) { Role role = roleService.getRoleById(id); request.setAttribute("role", role); return "role/roleDetails"; } /** * 删除角色-支持批量删除 * * @param response * @param ids * @return */ // @RequestMapping(value = "deleteRoles", method = { RequestMethod.GET }) public String deleteRoles(HttpServletResponse response, String ids) { String msg = ""; try { roleService.deleteRoles(ids); msg = "1"; } catch (Exception e) { msg = "0"; log.error("删除角色失败:" + e.getStackTrace()); } // ResponseUtils.renderText(response, msg); return null; } /** * 根据菜单名称获取菜单 * * @param response * @param roleName * @return */ // @RequestMapping(value="getRoleByRoleName",method={RequestMethod.POST}) public String getRoleByRoleName( HttpServletRequest request, HttpServletResponse response, String roleName) { int page = Integer.parseInt(request.getParameter("page")); // 当前页数 int rows = Integer.parseInt(request.getParameter("rows")); // 每页多少行 int startNum = page * rows - rows; // 分页查询开始位置 List<Role> roles = roleService.getRoleByRoleName(roleName, startNum, rows); int rolesTotalCountByRoleName = roleService.getRolesTotalCountByRoleName(roleName); Datagrid datagrid = new Datagrid(); datagrid.setRows(roles); datagrid.setTotal(rolesTotalCountByRoleName); String rolesJsonStr = ""; try { rolesJsonStr = Json.toJson( datagrid); // JsonUtils.objectToJackson(datagrid, Datagrid.class);// 将对象转换成json字符串 PrintWriter out = response.getWriter(); out.print(rolesJsonStr); out.flush(); out.close(); } catch (Exception e) { log.error("处理json数据报错:" + e.getStackTrace()); } return null; } /** * 加载权限树 * * @param request * @param response * @return */ // @RequestMapping(value = "loadAllAuthTree", method = { RequestMethod.POST }) public String loadAllAuthTree(HttpServletRequest request, HttpServletResponse response) { String id = request.getParameter("id"); List<Authority> checkedAuthList = roleService.getCheckedAuthIds(id); String authTreeJson = menusService.createAuthTree(checkedAuthList); log.info(authTreeJson); PrintWriter out; try { out = response.getWriter(); out.print(authTreeJson); out.flush(); out.close(); } catch (IOException e) { log.error("处理json数据报错:" + e.getStackTrace()); } return null; } /** * 验证用户名是否存在 * * @param roleName * @return */ // @RequestMapping(value = "validataRoleName", method = { RequestMethod.POST }) public void validataRoleName(HttpServletResponse response, String roleName) { int i = roleService.valiedateRoleName(roleName); String ok = i + ""; try { PrintWriter out = response.getWriter(); out.write(ok); out.flush(); out.close(); } catch (IOException e) { log.error("处理数据报错:" + e.getStackTrace()); } } }
/** * 遍历 RersultSet * * @author zozoh([email protected]) */ public abstract class ResultSetLooping { private static Log log = Logs.get(); /** 当结果集大于指定数量时,输出警告日志,默认-1不启用 */ public static int WARN_BIG_SIZE = -1; /** 当结果集大于指定大小时,报错,默认-1不启用 */ public static int ERROR_BIG_SIZE = -1; /** 可以在SqlConext中设置key来实现单独配置 */ public static String KEY_WARN_SIZE = "sql-result-size-warn"; /** 可以在SqlConext中设置key来实现单独配置 */ public static String KEY_ERROR_SIZE = "sql-result-size-error"; protected List<Object> list; private int index; public ResultSetLooping() { list = new LinkedList<Object>(); index = -1; } public void doLoop(ResultSet rs, SqlContext context) throws SQLException { Pager pager = context.getPager(); if (null == rs) return; int warnSize = (context.attr(KEY_WARN_SIZE) == null ? WARN_BIG_SIZE : ((Number) (context.attr(KEY_WARN_SIZE))).intValue()); int errorSize = (context.attr(KEY_ERROR_SIZE) == null ? ERROR_BIG_SIZE : ((Number) (context.attr(KEY_ERROR_SIZE))).intValue()); boolean warnBigResult = log.isWarnEnabled() && warnSize > 0; boolean errorBigResult = errorSize > 0; /** * 如果没有设置 Pager 或者 rs 的类型是 ResultSet.TYPE_FORWARD_ONLY,那么<br> * 无法利用 游标的滚动 来计算结果集合大小。这比较高效,但是如果使用者希望得到页数量,<br> * 需要为 Pager 另行计算 总体的结果集大小。 * * <p>一般的,为特殊数据建立的 Pager,生成的 ResultSet 类型应该是 TYPE_FORWARD_ONLY */ if (null == pager || ResultSet.TYPE_FORWARD_ONLY == rs.getType() || pager.getPageNumber() <= 0) { // 根据 Pager 设定 Fetch Size // by wendal: 设置与否,影响不大的,而且旧版本的Oracle会出问题,故,注释掉了 // if (null != pager && pager.getPageSize() > 0) // rs.setFetchSize(pager.getPageSize()); // 循环调用 while (rs.next()) { createObject(++index, rs, context, -1); if (warnBigResult && index > warnSize) { warnBigResult = false; this.warnBig(rs, context, index, warnSize); } if (errorBigResult && index > errorSize) { errorBigResult = false; this.errorBig(rs, context, index, errorSize); } } } /** * 如果进行到了这个分支,则表示,整个查询的 Pager 是不区分数据库类型的。 <br> * 并且 ResultSet 的游标是可以来回滚动的。 * * <p>所以我就会利用游标的滚动,为你计算整个结果集的大小。比较低效,在很小<br> * 数据量的时候 还是比较有用的 */ else if (rs.last()) { // 设置结果集合的 FetchSize if (pager.getPageSize() <= 0) rs.setFetchSize(Pager.DEFAULT_PAGE_SIZE); else if (pager.getPageSize() > Pager.MAX_FETCH_SIZE) rs.setFetchSize(Pager.MAX_FETCH_SIZE); else rs.setFetchSize(pager.getPageSize()); // 开始循环 int rowCount = rs.getRow(); LoopScope ls = LoopScope.eval(pager, rowCount); if (rs.absolute(ls.start + 1)) for (int i = ls.start; i < ls.max; i++) { createObject(++index, rs, context, rowCount); if (!rs.next()) break; if (warnBigResult && index > warnSize) { warnBigResult = false; this.warnBig(rs, context, index, warnSize); } if (errorBigResult && index > errorSize) { errorBigResult = false; this.errorBig(rs, context, index, errorSize); } } } } /** @return 当前获取的 List */ public List<Object> getList() { return list; } /** * 获得最后一次回调被调用时的下标。 index 的值初始为 -1,每次调用回调前都会自增 * * @return 当前循环的下标,下标由 0 开始 */ public int getIndex() { return index; } /** * 子类需要实现的函数 * * @param index 当前下标 * @param rs 结果集 * @param context Sql 上下文 * @param rowCount 总记录数,如果是原生分页语句,则会为 -1 * @return 是否成功的创建了对象 */ protected abstract boolean createObject( int index, ResultSet rs, SqlContext context, int rowCount); protected void warnBig(ResultSet rs, SqlContext ctx, int index, int warnSize) { log.warnf("BIG Result, pager=%s", ctx.getPager()); } protected void errorBig(ResultSet rs, SqlContext ctx, int index, int errorSize) { throw new DaoException("result size bigger than limit=" + errorSize); } }
public class DubboConfigureReader { private static final Log log = Logs.get(); protected Map<String, Object> maps = new HashMap<>(); public static Map<String, NutMap> read(String path) { Map<String, NutMap> maps = new LinkedHashMap<>(); Document doc = Xmls.xml(DubboConfigureReader.class.getClassLoader().getResourceAsStream(path)); doc.normalizeDocument(); Element top = doc.getDocumentElement(); NodeList list = top.getChildNodes(); int count = list.getLength(); for (int i = 0; i < count; i++) { Node node = list.item(i); if (node instanceof Element) { Element ele = (Element) node; String eleName = ele.getNodeName(); if (!eleName.startsWith("dubbo:")) continue; // 跳过非dubbo节点 String typeName = eleName.substring("dubbo:".length()); NutMap attrs = toAttrMap(ele.getAttributes()); log.debug("found " + typeName); String genBeanName = ele.getAttribute("id"); if (Strings.isBlank(genBeanName)) { if ("protocol".equals(typeName)) genBeanName = "dubbo"; else { genBeanName = ele.getAttribute("interface"); if (Strings.isBlank(genBeanName)) { try { genBeanName = Class.forName( "com.alibaba.dubbo.config." + Strings.upperFirst(typeName) + "Config") .getName(); } catch (ClassNotFoundException e) { throw Lang.wrapThrow(e); } } } if (maps.containsKey(genBeanName)) { int _count = 2; while (true) { String key = genBeanName + "_" + _count; if (maps.containsKey(key)) { _count++; continue; } genBeanName += "_" + _count; break; } } } attrs.put("_typeName", typeName); maps.put(genBeanName, attrs); } } return maps; } public static NutMap toAttrMap(NamedNodeMap attrs) { NutMap map = new NutMap(); int len = attrs.getLength(); for (int j = 0; j < len; j++) { Attr attr = (Attr) attrs.item(j); map.put(attr.getName(), attr.getValue()); } return map; } }
/** * 支持简单的懒加载机制的AnnotationEntityMaker * * @author wendal([email protected]) */ public class LazyAnnotationEntityMaker extends AnnotationEntityMaker { private static final Log log = Logs.get(); private Dao dao; public LazyAnnotationEntityMaker( DataSource datasource, JdbcExpert expert, EntityHolder holder, Dao dao) { super(datasource, expert, holder); this.dao = dao; } protected <T> NutEntity<T> _createNutEntity(Class<T> type) { return new LazyNutEntity<T>(type); } @Override public <T> Entity<T> make(Class<T> type) { LazyNutEntity<T> en = (LazyNutEntity<T>) super.make(type); en.init(); return en; } class LazyNutEntity<T> extends NutEntity<T> { public LazyNutEntity(Class<T> type) { super(type); } @SuppressWarnings({"rawtypes", "unchecked"}) public void init() { List<LinkField> lfs = new ArrayList<LinkField>(); lfs.addAll(ones.getAll()); lfs.addAll(manys.getAll()); lfs.addAll(manymanys.getAll()); if (lfs.isEmpty()) return; if (log.isDebugEnabled()) log.debug("Found links , enable lazy!! -->" + type); Mirror<T> mirror = Mirror.me(type); List<InterceptorPair> interceptorPairs = new ArrayList<InterceptorPair>(); // 准备拦截器 for (LinkField lf : lfs) { String fieldName = lf.getName(); try { Method setter = mirror.getSetter(mirror.getField(fieldName)); LazyMethodInterceptor lmi = new LazyMethodInterceptor(setter, fieldName); interceptorPairs.add( new InterceptorPair( lmi, MethodMatcherFactory.matcher( "^(get|set)" + Strings.upperFirst(fieldName) + "$"))); } catch (Throwable e) { if (log.isWarnEnabled()) log.warn("Not setter found for LazyLoading ?!", e); } } // 生成Aop化的类 ClassAgent agent = new AsmClassAgent(); for (InterceptorPair interceptorPair : interceptorPairs) agent.addInterceptor( interceptorPair.getMethodMatcher(), interceptorPair.getMethodInterceptor()); Class lazyClass = agent.define(DefaultClassDefiner.defaultOne(), type); // 检查对象的创建方法 BornContext<T> bc = Borns.evalByArgTypes(type, ResultSet.class); if (null == bc) this.bornByDefault = Mirror.me(lazyClass).getBorningByArgTypes(); else this.bornByRS = bc.getBorning(); } } class LazyMethodInterceptor implements MethodInterceptor { private WeakHashMap<Object, LazyStatus> status = new WeakHashMap<Object, LazyAnnotationEntityMaker.LazyStatus>(); private Method setter; private String fieldName; public LazyMethodInterceptor(Method setter, String fieldName) { this.setter = setter; this.fieldName = fieldName; } public void filter(InterceptorChain chain) throws Throwable { LazyStatus stat = status.get(chain.getCallingObj()); if (stat == null) { if (!chain.getCallingMethod().equals(setter)) { dao.fetchLinks(chain.getCallingObj(), fieldName); // 这里会触发setter被调用 } status.put(chain.getCallingObj(), LazyStatus.NO_NEED); // 如果setter被调用,那么也就不再需要懒加载了 } chain.doChain(); } } enum LazyStatus { CAN_FETCH, FETCHED, NO_NEED } }
/** * 定期将未读的消息发送成 Email * * @author zozoh([email protected]) */ @IocBean(name = "t_msg_mail") public class MesssageMailMaker implements TimerHandler { private static final Log log = Logs.get(); /** 注入: 服务类工厂 */ @Inject("refer:serviceFactory") private ZTaskFactory factory; @Override public String doHandle(String name, Timering ing) { final Map<String, List<Message>> map = new HashMap<String, List<Message>>(); // 分组 factory .messages() .each( null, "!R!N:", null, 0, new Each<Message>() { public void invoke(int index, Message msg, int length) { List<Message> list = map.get(msg.getOwner()); if (null == list) { list = new LinkedList<Message>(); map.put(msg.getOwner(), list); } list.add(msg); } }); if (log.isDebugEnabled()) log.debugf("MailMaker: found %d users %s", map.size(), Strings.dup('*', 40)); // 循环为每个用户发送通知邮件 for (String unm : map.keySet()) { User u = factory.users().get(unm); if (null == u || null == u.getEmail()) continue; List<Message> list = map.get(unm); if (null == list || list.isEmpty()) { continue; } if (log.isDebugEnabled()) log.debugf(" - MailMaker: will send to '%s'", unm); Date now = Times.now(); // 准备消息 StringBuilder sb = new StringBuilder(); sb.append("Message in zTask @ " + Times.sDT(now) + " : \n\n"); for (Message msg : list) { sb.append(" [").append(Times.sDT(msg.getCreateTime())).append("] - "); sb.append(msg.getText()); sb.append("\n"); } sb.append("\n\n"); Message firstMsg = list.get(0); String subject = String.format( "[zTask:%d] %s ", list.size(), firstMsg.getText().replaceAll("[\\[]?[0-9a-f]{24}[\\]]?", "")); if (log.isDebugEnabled()) log.debugf(" - MailMaker: mail is: '%s':\n%s", subject, sb); // 设置收件人 MailObj mo = ZTasks.textMail(subject, sb.toString()); mo.setTos(Lang.array(u.getName())); if (log.isDebugEnabled()) log.debugf(" - MailMaker: mail to @%s(%s)", u.getName(), u.getEmail()); // 加入队列 factory.mails().joinMail(mo); if (log.isDebugEnabled()) log.debugf(" - MailMaker: after join, queue(%d)", factory.mails().count()); // 设置通知状态 for (Message msg : list) factory.messages().setNotified(msg, now); if (log.isDebugEnabled()) { log.debugf(" - MailMaker: set noti for %d messages", list.size()); log.debugf(" - MailMaker: %s -- NEXT LOOP --", Strings.dup('-', 28)); } } // 返回成功 return "OK:" + map.size() + " users"; } }
/** * 执行上传时一些必要的配置信息 * * @author zozoh([email protected]) */ public class UploadingContext { private static final Log log = Logs.get(); public static UploadingContext create(String poolPath) { return create(new NutFilePool(poolPath)); } public static UploadingContext create(FilePool pool) { return new UploadingContext(pool); } public UploadingContext(String poolPath) { this(new NutFilePool(poolPath, 2000)); } public UploadingContext(FilePool pool) { charset = Encoding.UTF8; bufferSize = 8192; setFilePool(pool); } /** 默认为 UTF-8,上传字节流的编码 */ private String charset; /** 临时文件池 */ private FilePool filePool; /** 缓冲,默认 8192 */ private int bufferSize; /** 是否忽略空文件,默认为 false */ private boolean ignoreNull; /** * 如果大于0,对于每个上传的文件,都判断,如果超过了这个大小,则拒绝继续上传 * * <p>单位为字节 */ private int maxFileSize; /** 一个正则表达式,描述了可以允许的文件名 */ private String nameFilter; /** 一个正则表达式,描述了可以允许的文件内容类型 */ private String contentTypeFilter; public String getCharset() { return charset; } public UploadingContext setCharset(String charset) { this.charset = charset; return this; } public FilePool getFilePool() { return filePool; } public UploadingContext setFilePool(FilePool pool) { if (!(pool instanceof SynchronizedFilePool)) pool = new SynchronizedFilePool(pool); this.filePool = pool; return this; } public int getBufferSize() { return bufferSize; } public UploadingContext setBufferSize(int bufferSize) { this.bufferSize = bufferSize; if (bufferSize < 128 && log.isWarnEnabled()) { log.warn("Uploading buffer is less than 128!! Auto-fix to 128!! 8192 will be much better!!"); this.bufferSize = 128; } return this; } public boolean isIgnoreNull() { return ignoreNull; } public UploadingContext setIgnoreNull(boolean ignoreNull) { this.ignoreNull = ignoreNull; return this; } public int getMaxFileSize() { return maxFileSize; } public UploadingContext setMaxFileSize(int maxFileSize) { this.maxFileSize = maxFileSize; return this; } public String getNameFilter() { return nameFilter; } public UploadingContext setNameFilter(String nameFilter) { this.nameFilter = nameFilter; return this; } public boolean isNameAccepted(String name) { if (null == nameFilter || Strings.isBlank(name) || "\"\"".equals(name)) // 用户不选择文件时,文件名会是"" 两个双引号 return true; return Pattern.matches(nameFilter, name.toLowerCase()); } public String getContentTypeFilter() { return contentTypeFilter; } public UploadingContext setContentTypeFilter(String contentTypeFilter) { this.contentTypeFilter = contentTypeFilter; return this; } public boolean isContentTypeAccepted(String contentType) { if (null == contentTypeFilter || Strings.isBlank(contentType)) return true; return Pattern.matches(contentTypeFilter, contentType.toLowerCase()); } }
/** * 本适配器专门处理 HTTP 文件上传(1.b.44及之后的版本,兼容Html5的流式上传)。 它支持多文件,多参数上传。具体的做法是将 HTTP 上传的所有内容 * 包括文件以及名值对都预先缓存下来。其中,文件缓存在磁盘上,名值对缓存在内存中。 * * <p>因此,本适配器构造的时候,需要四个参数: * * <ol> * <li>临时文件存放的目录 * <li>数据缓冲区大小,建议设置为8192 * <li>HTTP 请求的编码方式。 * <li>临时文件的最大数量 * </ol> * * 本适配器提供了四个构造函数,最简单的一个只有一个参数,需要你提供一个临时文件目录,缓冲区大小默认为8192, 临时文件数目默认的为 "2000",HTTP 请求的编码方式为 "UTF-8", * * <p>为了能让入口函数了解 HTTP 请求的更多信息,本适配器入口函数声明更多的参数类型: * * <ul> * <li>java.io.File : 指向已上传至临时目录的文件对象 * <li>org.nutz.mvc.upload.FieldMeta : 描述了一个上传参数的更多属性 * <li>org.nutz.mvc.upload.TempFile : 组合了 File 和 FieldMeta * </ul> * * 当然,这三种参数,都是需要你在入口函数的参数列表里声明 '@Param' 注解,用来告诉本适配器,你的参数 具体取自请求中的哪一个参数。 * * <p><b>Html5流式上传(实验性)的注意事项: 参数名默认是filedata,除非req.getHeader("Content-Disposition")中有描述另外的name</b> * * @author zozoh([email protected]) * @author wendal([email protected]) * @see org.nutz.mvc.annotation.Param */ public class UploadAdaptor extends PairAdaptor { private static final Log log = Logs.get(); private UploadingContext context; public UploadAdaptor() throws IOException { context = new UploadingContext(File.createTempFile("nutz", null).getParent()); } public UploadAdaptor(UploadingContext context) { this.context = context; } public UploadAdaptor(String path) { context = new UploadingContext(path); } public UploadAdaptor(String path, int buffer) { this(path); context.setBufferSize(buffer); } public UploadAdaptor(String path, int buffer, String charset) { this(path); context.setBufferSize(buffer); context.setCharset(charset); } public UploadAdaptor(String path, int buffer, String charset, int poolSize) { context = new UploadingContext(new NutFilePool(path, poolSize)); context.setBufferSize(buffer); context.setCharset(charset); } public UploadAdaptor(String path, int buffer, String charset, int poolSize, int maxFileSize) { context = new UploadingContext(new NutFilePool(path, poolSize)); context.setBufferSize(buffer); context.setCharset(charset); context.setMaxFileSize(maxFileSize); } public UploadingContext getContext() { return context; } @Override public Object[] adapt( ServletContext sc, HttpServletRequest req, HttpServletResponse resp, String[] pathArgs) { // 临时 if (!Mvcs.getActionContext().getMethod().toGenericString().equals(method.toGenericString())) { throw new IllegalArgumentException( String.format( "Method miss match: expect %s but %s. using Ioc? set singleton=false, pls", method, Mvcs.getActionContext().getMethod())); } return super.adapt(sc, req, resp, pathArgs); } protected ParamInjector evalInjectorBy(Type type, Param param) { // TODO 这里的实现感觉很丑, 感觉可以直接用type进行验证与传递 // TODO 这里将Type的影响局限在了 github issue #30 中提到的局部范围 Class<?> clazz = Lang.getTypeClass(type); if (clazz == null) { if (log.isWarnEnabled()) log.warnf("!!Fail to get Type Class : type=%s , param=%s", type, param); return null; } // Map if (Map.class.isAssignableFrom(clazz)) return new MapSelfInjector(); if (null == param) return super.evalInjectorBy(type, null); String paramName = param.value(); // File if (File.class.isAssignableFrom(clazz)) return new FileInjector(paramName); // FileMeta if (FieldMeta.class.isAssignableFrom(clazz)) return new FileMetaInjector(paramName); // TempFile if (TempFile.class.isAssignableFrom(clazz)) return new TempFileInjector(paramName); // InputStream if (InputStream.class.isAssignableFrom(clazz)) return new InputStreamInjector(paramName); // Reader if (Reader.class.isAssignableFrom(clazz)) return new ReaderInjector(paramName); // List if (List.class.isAssignableFrom(clazz)) return new MapListInjector(paramName); if (TempFile[].class.isAssignableFrom(clazz)) { return new TempFileArrayInjector(paramName); } // Other return super.evalInjectorBy(type, param); } public Map<String, Object> getReferObject( ServletContext sc, HttpServletRequest request, HttpServletResponse response, String[] pathArgs) { try { if (!"POST".equals(request.getMethod()) && !"PUT".equals(request.getMethod())) { String str = "Not POST or PUT, Wrong HTTP method! --> " + request.getMethod(); throw Lang.makeThrow(IllegalArgumentException.class, str); } // 看看是不是传统的上传 String contentType = request.getContentType(); if (contentType == null) { throw Lang.makeThrow(IllegalArgumentException.class, "Content-Type is NULL!!"); } if (contentType.contains("multipart/form-data")) { // 普通表单上传 if (log.isDebugEnabled()) log.debug("Select Html4 Form upload parser --> " + request.getRequestURI()); Uploading ing = new FastUploading(); return ing.parse(request, context); } if (contentType.contains("application/octet-stream")) { // Html5 // 流式上传 if (log.isDebugEnabled()) log.debug("Select Html5 Stream upload parser --> " + request.getRequestURI()); Uploading ing = new Html5Uploading(); return ing.parse(request, context); } // 100%是没写enctype='multipart/form-data' if (contentType.contains("application/x-www-form-urlencoded")) { log.warn("Using form upload ? You forgot this --> enctype='multipart/form-data' ?"); } throw Lang.makeThrow(IllegalArgumentException.class, "Unknow Content-Type : " + contentType); } catch (UploadException e) { throw Lang.wrapThrow(e); } finally { Uploads.removeInfo(request); } } }
public class NutQuartzCronJobFactory { private static final Log log = Logs.get(); protected PropertiesProxy conf; protected Scheduler scheduler; public void init() throws Exception { String prefix = "cron."; for (String key : conf.getKeys()) { if (key.length() < prefix.length() + 1 || !key.startsWith(prefix)) continue; String name = key.substring(prefix.length()); if ("pkgs".equals(name)) { log.debug("found cron job packages = " + conf.get(key)); for (String pkg : Strings.splitIgnoreBlank(conf.get(key), ",")) { addPackage(pkg); } continue; } String cron = conf.get(key); log.debugf("job define name=%s cron=%s", name, cron); Class<?> klass = null; if (name.contains(".")) { klass = Lang.loadClass(name); } else { klass = Lang.loadClass(getClass().getPackage().getName() + ".job." + name); } cron(cron, klass); } } public void addPackage(String pkg) { for (Class<?> klass : Scans.me().scanPackage(pkg)) { Scheduled scheduled = klass.getAnnotation(Scheduled.class); if (scheduled != null) { try { add(klass, scheduled); } catch (SchedulerException e) { throw new RuntimeException(e); } } } } @SuppressWarnings("unchecked") public void add(Class<?> klass, Scheduled scheduled) throws SchedulerException { String name = klass.getName(); if (!Strings.isBlank(scheduled.cron())) { try { log.debugf("job define name=%s cron=%s", name, scheduled.cron()); cron(scheduled.cron(), klass); return; } catch (SchedulerException e) { throw new RuntimeException(e); } } if (scheduled.fixedRate() > 0) { log.debugf( "job define name=%s fixedRate=%s count=%s initialDelay=%s", name, scheduled.fixedRate(), scheduled.count(), scheduled.initialDelay()); SimpleScheduleBuilder schedule = SimpleScheduleBuilder.simpleSchedule(); if (scheduled.fixedRate() > 0) schedule.withIntervalInSeconds(scheduled.fixedRate()); if (scheduled.count() > 0) { schedule.withRepeatCount(scheduled.count()); } else { schedule.repeatForever(); } TriggerBuilder<SimpleTrigger> trigger = TriggerBuilder.newTrigger().withIdentity(name).withSchedule(schedule); if (scheduled.initialDelay() > 0) trigger.startAt(new Date(System.currentTimeMillis() + scheduled.initialDelay() * 1000)); JobDetail job = JobBuilder.newJob((Class<? extends Job>) klass).withIdentity(name).build(); scheduler.scheduleJob(job, trigger.build()); } } @SuppressWarnings("unchecked") public void cron(String cron, Class<?> klass) throws SchedulerException { String name = klass.getName(); JobDetail job = JobBuilder.newJob((Class<? extends Job>) klass).withIdentity(name).build(); CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(name) .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); scheduler.scheduleJob(job, trigger); } }
/** * 拦截器链,持有被调用方法的信息 * * @author wendal([email protected]) */ public class InterceptorChain { protected Method callingMethod; protected int methodIndex; protected Object args[]; protected AopCallback callingObj; protected Object returnValue; protected List<MethodInterceptor> miList; private static Log LOG = Logs.get(); private boolean invoked = false; private int currentMI = 0; public InterceptorChain( int methodIndex, Object obj, Method method, List<MethodInterceptor> miList, Object[] args) { this.methodIndex = methodIndex; this.callingObj = (AopCallback) obj; this.callingMethod = method; this.args = args; this.miList = miList; } /** * 继续执行下一个拦截器,如果已经没有剩下的拦截器,则执行原方法 * * @return 拦截器链本身 * @throws Throwable 下层拦截器或原方法抛出的一切异常 */ public InterceptorChain doChain() throws Throwable { if (currentMI == miList.size()) invoke(); else { currentMI++; miList.get(currentMI - 1).filter(this); } return this; } /** * 执行原方法,一般情况下不应该直接被调用 * * @throws Throwable 原方法抛出的一切异常 */ public void invoke() throws Throwable { if (invoked) LOG.warnf("!! Calling Method more than once! Method --> %s", callingMethod.toString()); else invoked = true; this.returnValue = callingObj._aop_invoke(methodIndex, args); } /** * 获取返回值 * * @return 返回值 */ public Object getReturn() { return returnValue; } /** 正在被调用的Method */ public Method getCallingMethod() { return callingMethod; } /** 方法调用的参数数组,如果你要改变参数,那么必须保证参数类型与方法参数兼容. */ public Object[] getArgs() { return args; } public void setReturnValue(Object returnValue) { this.returnValue = returnValue; } public AopCallback getCallingObj() { return callingObj; } public boolean isInvoked() { return invoked; } /** 获取当前的方法拦截器列表,注意,这个列表是不可修改的.如果需要修改,那么请通过{@link #setMethodInterceptors(List)} */ public List<MethodInterceptor> getMethodInterceptors() { return Collections.unmodifiableList(miList); } /** 设置当前调用的方法拦截器列表,注意,这个set只对当前方法调用有效. */ public void setMethodInterceptors(List<MethodInterceptor> miList) { this.miList = miList; } }
/** * 提供一个简易的 TCP 的 socket 监听服务器 * * @author zozoh([email protected]) */ public class TcpServer implements Runnable { private static final Log log = Logs.get(); private boolean stop; private int port; private ServerSocket listener; private SocketHandler handler; public TcpServer(int port, SocketHandler handler) { this.port = port; this.handler = handler; } protected void listen(Socket socket) { SocketHandler handler = getHandler(); // 处理请求 try { handler.handle(socket); } // 仅仅是关闭连接 catch (SocketClosed e) { } // 停止服务 catch (ServerStopped e) { stop = true; } // 处理异常 catch (Throwable e) { handler.whenError(socket, e); } // 确保关闭 finally { if (!socket.isClosed()) try { socket.close(); } catch (IOException e) { throw Lang.wrapThrow(e); } } } public void run() { // ----------------------------------------- 建立 log.infof("start TcpServer [%s] @ %d", Thread.currentThread().getName(), port); try { listener = new ServerSocket(port); } catch (IOException e1) { throw Lang.wrapThrow(e1); } // ----------------------------------------- 循环 log.infof("TcpServer listen @ %d", port); while (!stop) { log.info("before accept ..."); Socket socket = null; try { socket = listener.accept(); } catch (IOException e) { log.fatalf("Fail to accept %s @ %d , System.exit!", Thread.currentThread().getName(), port); System.exit(0); } log.info("do listen ..."); listen(socket); log.infof( "done for listen [%s:%d], stop=%b", socket.getInetAddress().getHostName(), socket.getPort(), stop); } // ----------------------------------------- 关闭 try { listener.close(); log.infof("TcpServer shutdown @ %d", port); } catch (IOException e) { throw Lang.wrapThrow(e); } } public SocketHandler getHandler() { return handler; } public void setHandler(SocketHandler handler) { this.handler = handler; } public void stop() { stop = true; } }
/** * Nutz内核初始化完成后的操作 * * @author wendal */ public class MainSetup implements Setup { private static final Log log = Logs.get(); @SuppressWarnings("serial") public void init(NutConfig nc) { NutShiro.DefaultLoginURL = "/admin/logout"; // 检查环境 if (!Charset.defaultCharset().name().equalsIgnoreCase(Encoding.UTF8)) { log.warn("This project must run in UTF-8, pls add -Dfile.encoding=UTF-8 to JAVA_OPTS"); } // 获取Ioc容器及Dao对象 Ioc ioc = nc.getIoc(); // 加载freemarker自定义标签 自定义宏路径 ioc.get(Configuration.class) .setAutoImports( new HashMap<String, String>(2) { { put("p", "/ftl/pony/index.ftl"); put("s", "/ftl/spring.ftl"); } }); ioc.get(FreeMarkerConfigurer.class, "mapTags"); Dao dao = ioc.get(Dao.class); // 为全部标注了@Table的bean建表 Daos.createTablesInPackage(dao, getClass().getPackage().getName() + ".bean", false); // 获取配置对象 PropertiesProxy conf = ioc.get(PropertiesProxy.class, "conf"); // 初始化SysLog,触发全局系统日志初始化 ioc.get(SysLogService.class); // 初始化默认根用户 User admin = dao.fetch(User.class, "admin"); if (admin == null) { UserService us = ioc.get(UserService.class); admin = us.add("admin", "123456"); } // 初始化游客用户 User guest = dao.fetch(User.class, "guest"); if (guest == null) { UserService us = ioc.get(UserService.class); guest = us.add("guest", "123456"); UserProfile profile = dao.fetch(UserProfile.class, guest.getId()); profile.setNickname("游客"); dao.update(profile, "nickname"); } // 获取NutQuartzCronJobFactory从而触发计划任务的初始化与启动 ioc.get(NutQuartzCronJobFactory.class); // 权限系统初始化 AuthorityService as = ioc.get(AuthorityService.class); as.initFormPackage("net.wendal.nutzbook"); as.checkBasicRoles(admin); // 检查一下Ehcache CacheManager 是否正常. CacheManager cacheManager = ioc.get(CacheManager.class); log.debug("Ehcache CacheManager = " + cacheManager); // CachedNutDaoExecutor.DEBUG = true; // 启用FastClass执行入口方法 Mvcs.disableFastClassInvoker = false; // 设置Markdown缓存 if (cacheManager.getCache("markdown") == null) cacheManager.addCache("markdown"); Markdowns.cache = cacheManager.getCache("markdown"); if (conf.getBoolean("cdn.enable", false) && !Strings.isBlank(conf.get("cdn.urlbase"))) { MarkdownFunction.cdnbase = conf.get("cdn.urlbase"); } } public void destroy(NutConfig conf) { Markdowns.cache = null; // 非mysql数据库,或多webapp共享mysql驱动的话,以下语句删掉 try { Mirror.me(Class.forName("com.mysql.jdbc.AbandonedConnectionCleanupThread")) .invoke(null, "shutdown"); } catch (Throwable e) { } // 解决quartz有时候无法停止的问题 try { conf.getIoc().get(Scheduler.class).shutdown(true); } catch (Exception e) { } } }
/** * 对于所有数据库的抽象实现 * * @author zozoh([email protected]) */ public abstract class AbstractJdbcExpert implements JdbcExpert { private static final Log log = Logs.get(); private static String DEFAULT_COMMENT_TABLE = "comment on table $table is '$tableComment'"; private static String DEFAULT_COMMENT_COLUMN = "comment on column $table.$column is '$columnComment'"; protected Set<String> keywords; /** 提供给子类使用的配置文件对象 */ protected JdbcExpertConfigFile conf; // ==================================================================== // 构造函数:子类需要将重载 public AbstractJdbcExpert(JdbcExpertConfigFile conf) { this.conf = conf; } // ==================================================================== // 下面为子类默认实现几个接口函数 public void setupEntityField(Connection conn, Entity<?> en) { Statement stat = null; ResultSet rs = null; ResultSetMetaData rsmd = null; try { // 获取数据库元信息 stat = conn.createStatement(); rs = stat.executeQuery(createResultSetMetaSql(en)); rsmd = rs.getMetaData(); // 循环字段检查 for (MappingField mf : en.getMappingFields()) { try { int ci = Daos.getColumnIndex(rsmd, mf.getColumnName()); // 是否只读,如果人家已经是指明是只读了,那么就不要自作聪明得再从数据库里验证了 // if (!mf.isReadonly() && rsmd.isReadOnly(ci)) // mf.setAsReadonly(); // 是否非空 if (ResultSetMetaData.columnNoNulls == rsmd.isNullable(ci)) mf.setAsNotNull(); // 枚举类型在数据库中的值 if (mf.getTypeMirror().isEnum()) { if (Daos.isIntLikeColumn(rsmd, ci)) { mf.setColumnType(ColType.INT); } else { mf.setColumnType(ColType.VARCHAR); } } } catch (Exception e) { // TODO 需要log一下不? } } } catch (Exception e) { if (log.isDebugEnabled()) log.debugf("Table '%s' doesn't exist!", en.getViewName()); } // Close ResultSet and Statement finally { Daos.safeClose(stat, rs); } } public ValueAdaptor getAdaptor(MappingField ef) { Mirror<?> mirror = ef.getTypeMirror(); // 为数字型枚举的特殊判断 if (mirror.isEnum() && ColType.INT == ef.getColumnType()) return Jdbcs.Adaptor.asEnumInt; // 用普通逻辑返回适配器 return Jdbcs.getAdaptor(mirror); } public Pojo createPojo(SqlType type) { return new NutPojo().setSqlType(type); } public boolean dropEntity(Dao dao, Entity<?> en) { String tableName = en.getTableName(); String viewName = en.getViewName(); try { dropRelation(dao, en); if (!tableName.equals(viewName) && dao.exists(viewName)) { dao.execute(Sqls.create("DROP VIEW " + viewName)); } dao.execute(Sqls.create("DROP TABLE " + tableName)); } catch (Exception e) { return false; } return true; } public Map<String, Object> getConf() { return this.conf.getConfig(); } // ==================================================================== // 下面是提供给子类使用的一些帮助函数 protected String createResultSetMetaSql(Entity<?> en) { return "SELECT * FROM " + en.getViewName() + " where 1!=1"; } public void createRelation(Dao dao, Entity<?> en) { final List<Sql> sqls = new ArrayList<Sql>(5); for (LinkField lf : en.visitManyMany(null, null, null)) { ManyManyLinkField mm = (ManyManyLinkField) lf; if (dao.exists(mm.getRelationName())) continue; String sql = "CREATE TABLE " + mm.getRelationName() + "("; sql += mm.getFromColumnName() + " " + evalFieldType(mm.getHostField()) + ","; sql += mm.getToColumnName() + " " + evalFieldType(mm.getLinkedField()); sql += ")"; sqls.add(Sqls.create(sql)); } dao.execute(sqls.toArray(new Sql[sqls.size()])); } protected void dropRelation(Dao dao, Entity<?> en) { final List<Sql> sqls = new ArrayList<Sql>(5); for (LinkField lf : en.visitManyMany(null, null, null)) { ManyManyLinkField mm = (ManyManyLinkField) lf; if (!dao.exists(mm.getRelationName())) continue; sqls.add(Sqls.create("DROP TABLE " + mm.getRelationName())); } dao.execute(sqls.toArray(new Sql[sqls.size()])); } public String evalFieldType(MappingField mf) { if (mf.getCustomDbType() != null) return mf.getCustomDbType(); switch (mf.getColumnType()) { case CHAR: return "CHAR(" + mf.getWidth() + ")"; case BOOLEAN: return "BOOLEAN"; case VARCHAR: return "VARCHAR(" + mf.getWidth() + ")"; case TEXT: return "TEXT"; case BINARY: return "BLOB"; case TIMESTAMP: return "TIMESTAMP"; case DATETIME: return "DATETIME"; case DATE: return "DATE"; case TIME: return "TIME"; case INT: // 用户自定义了宽度 if (mf.getWidth() > 0) return "INT(" + mf.getWidth() + ")"; // 用数据库的默认宽度 return "INT"; case FLOAT: // 用户自定义了精度 if (mf.getWidth() > 0 && mf.getPrecision() > 0) { return "NUMERIC(" + mf.getWidth() + "," + mf.getPrecision() + ")"; } // 用默认精度 if (mf.getTypeMirror().isDouble()) return "NUMERIC(15,10)"; return "FLOAT"; case PSQL_ARRAY: return "ARRAY"; case PSQL_JSON: case MYSQL_JSON: return "JSON"; // TODO 这里不用加 default 么 } throw Lang.makeThrow( "Unsupport colType '%s' of field '%s' in '%s' ", mf.getColumnType(), mf.getName(), mf.getEntity().getType().getName()); } protected static List<DaoStatement> wrap(String... sqls) { List<DaoStatement> sts = new ArrayList<DaoStatement>(sqls.length); for (String sql : sqls) if (!Strings.isBlank(sql)) sts.add(Sqls.create(sql)); return sts; } protected static List<DaoStatement> wrap(List<String> sqls) { List<DaoStatement> sts = new ArrayList<DaoStatement>(sqls.size()); for (String sql : sqls) if (!Strings.isBlank(sql)) sts.add(Sqls.create(sql)); return sts; } protected static String gSQL(String ptn, String table, String field) { CharSegment cs = new CharSegment(ptn); cs.set("T", table).set("F", field); return cs.toString(); } protected String getDefaultValue(MappingField mf) { return mf.getDefaultValue(null).replaceAll("@", "@@"); } protected List<Sql> createIndexs(Entity<?> en) { List<Sql> sqls = new ArrayList<Sql>(); StringBuilder sb = new StringBuilder(); List<EntityIndex> indexs = en.getIndexes(); for (EntityIndex index : indexs) { sb.setLength(0); if (index.isUnique()) sb.append("Create UNIQUE Index "); else sb.append("Create Index "); if (index.getName().contains("$")) sb.append(TableName.render(new CharSegment(index.getName()))); else sb.append(index.getName()); sb.append(" ON ").append(en.getTableName()).append("("); for (EntityField field : index.getFields()) { if (field instanceof MappingField) { MappingField mf = (MappingField) field; sb.append(mf.getColumnNameInSql()).append(','); } else { throw Lang.makeThrow( DaoException.class, "%s %s is NOT a mapping field, can't use as index field!!", en.getClass(), field.getName()); } } sb.setCharAt(sb.length() - 1, ')'); sqls.add(Sqls.create(sb.toString())); } return sqls; } public void addComment(Dao dao, Entity<?> en) { addComment(dao, en, null, null); } public void addComment(Dao dao, Entity<?> en, String commentTable, String commentColumn) { if (!en.hasTableComment() && !en.hasColumnComment()) { return; } List<Sql> sqls = new ArrayList<Sql>(); // 表注释 if (en.hasTableComment()) { Sql tableCommentSQL = Sqls.create(Strings.isBlank(commentTable) ? DEFAULT_COMMENT_TABLE : commentTable); tableCommentSQL .vars() .set("table", en.getTableName()) .set("tableComment", en.getTableComment()); sqls.add(tableCommentSQL); } // 字段注释 if (en.hasColumnComment()) { for (MappingField mf : en.getMappingFields()) { if (mf.hasColumnComment()) { Sql columnCommentSQL = Sqls.create(Strings.isBlank(commentColumn) ? DEFAULT_COMMENT_COLUMN : commentColumn); columnCommentSQL .vars() .set("table", en.getTableName()) .set("column", mf.getColumnName()) .set("columnComment", mf.getColumnComment()); sqls.add(columnCommentSQL); } } } // 执行创建语句 dao.execute(sqls.toArray(new Sql[sqls.size()])); } public void formatQuery(DaoStatement daoStatement) { if (daoStatement == null) return; SqlContext ctx = daoStatement.getContext(); if (ctx == null || ctx.getPager() == null) return; if (daoStatement instanceof Pojo) formatQuery((Pojo) daoStatement); else if (daoStatement instanceof Sql) formatQuery((Sql) daoStatement); else throw Lang.noImplement(); } public abstract void formatQuery(Pojo pojo); public void formatQuery(Sql sql) { throw Lang.noImplement(); } public Pojo fetchPojoId(Entity<?> en, MappingField idField) { String autoSql = "SELECT MAX($field) AS $field FROM $view"; Pojo autoInfo = new SqlFieldMacro(idField, autoSql); autoInfo.setEntity(en); return autoInfo; } public boolean isSupportAutoIncrement() { return true; } public String makePksName(Entity<?> en) { String name = en.getType().getAnnotation(PK.class).name(); if (Strings.isBlank(name)) { StringBuilder sb = new StringBuilder(); for (MappingField mf : en.getPks()) { sb.append("_").append(mf.getColumnName()); } sb.setLength(sb.length() - 1); return sb.toString(); } return name; } public void addDefaultValue(StringBuilder sb, MappingField mf) { if (!mf.hasDefaultValue()) return; if (mf.getTypeMirror().isNumber()) sb.append(" DEFAULT ").append(getDefaultValue(mf)); else sb.append(" DEFAULT '").append(getDefaultValue(mf)).append('\''); } public boolean addColumnNeedColumn() { return true; } public boolean supportTimestampDefault() { return true; } public void setKeywords(Set<String> keywords) { this.keywords = keywords; } public Set<String> getKeywords() { return keywords; } public String wrapKeywork(String columnName, boolean force) { if (force || keywords.contains(columnName.toUpperCase())) return "`" + columnName + "`"; return null; } public boolean isSupportGeneratedKeys() { return true; } }