Browse Source

JFinal 1.1.5 Release :)

JamesZhan 13 years ago
parent
commit
be4d93bd07
59 changed files with 499 additions and 385 deletions
  1. 8 0
      src/com/jfinal/config/Constants.java
  2. 34 0
      src/com/jfinal/core/ActionKey.java
  3. 18 1
      src/com/jfinal/core/ActionMapping.java
  4. 1 1
      src/com/jfinal/core/ActionRender.java
  5. 1 0
      src/com/jfinal/core/Config.java
  6. 1 1
      src/com/jfinal/core/Const.java
  7. 18 0
      src/com/jfinal/core/Controller.java
  8. 2 1
      src/com/jfinal/core/ModelInjector.java
  9. 2 2
      src/com/jfinal/core/TypeConverter.java
  10. 0 60
      src/com/jfinal/ext/handler/DownloadHandler.java
  11. 2 0
      src/com/jfinal/ext/handler/UrlSkipHandler.java
  12. 2 2
      src/com/jfinal/ext/interceptor/Restful.java
  13. 21 21
      src/com/jfinal/ext/interceptor/SessionInViewInterceptor.java
  14. 3 2
      src/com/jfinal/ext/render/CaptchaRender.java
  15. 3 2
      src/com/jfinal/ext/render/JsonWithContentTypeRender.java
  16. 2 3
      src/com/jfinal/log/ILoggerFactory.java
  17. 1 1
      src/com/jfinal/log/JdkLoggerFactory.java
  18. 1 1
      src/com/jfinal/log/Log4jLoggerFactory.java
  19. 11 4
      src/com/jfinal/log/Logger.java
  20. 1 1
      src/com/jfinal/log/NullLoggerFactory.java
  21. 2 1
      src/com/jfinal/plugin/activerecord/ActiveRecordException.java
  22. 2 2
      src/com/jfinal/plugin/activerecord/ActiveRecordPlugin.java
  23. 97 0
      src/com/jfinal/plugin/activerecord/CaseInsensitiveContainerFactory.java
  24. 0 53
      src/com/jfinal/plugin/activerecord/CaseInsensitiveMapFactory.java
  25. 26 20
      src/com/jfinal/plugin/activerecord/Db.java
  26. 7 4
      src/com/jfinal/plugin/activerecord/DbKit.java
  27. 27 0
      src/com/jfinal/plugin/activerecord/IContainerFactory.java
  28. 0 9
      src/com/jfinal/plugin/activerecord/IMapFactory.java
  29. 22 15
      src/com/jfinal/plugin/activerecord/Model.java
  30. 1 1
      src/com/jfinal/plugin/activerecord/Record.java
  31. 1 1
      src/com/jfinal/plugin/activerecord/TableInfo.java
  32. 0 4
      src/com/jfinal/plugin/activerecord/dialect/AnsiSqlDialect.java
  33. 16 1
      src/com/jfinal/plugin/activerecord/dialect/Dialect.java
  34. 0 4
      src/com/jfinal/plugin/activerecord/dialect/MysqlDialect.java
  35. 45 8
      src/com/jfinal/plugin/activerecord/dialect/OracleDialect.java
  36. 0 4
      src/com/jfinal/plugin/activerecord/dialect/PostgreSqlDialect.java
  37. 0 4
      src/com/jfinal/plugin/activerecord/dialect/Sqlite3Dialect.java
  38. 3 1
      src/com/jfinal/plugin/druid/DruidStatViewHandler.java
  39. 0 67
      src/com/jfinal/render/CPI.java
  40. 1 1
      src/com/jfinal/render/Error404Exception.java
  41. 4 3
      src/com/jfinal/render/Error404Render.java
  42. 1 1
      src/com/jfinal/render/Error500Exception.java
  43. 4 3
      src/com/jfinal/render/Error500Render.java
  44. 2 2
      src/com/jfinal/render/FileRender.java
  45. 30 8
      src/com/jfinal/render/FreeMarkerRender.java
  46. 4 4
      src/com/jfinal/render/HtmlRender.java
  47. 4 4
      src/com/jfinal/render/JavascriptRender.java
  48. 30 15
      src/com/jfinal/render/JsonRender.java
  49. 4 3
      src/com/jfinal/render/JspRender.java
  50. 3 13
      src/com/jfinal/render/NullRender.java
  51. 2 2
      src/com/jfinal/render/Redirect301Render.java
  52. 2 2
      src/com/jfinal/render/RedirectRender.java
  53. 3 2
      src/com/jfinal/render/Render.java
  54. 1 1
      src/com/jfinal/render/RenderException.java
  55. 9 1
      src/com/jfinal/render/RenderFactory.java
  56. 4 4
      src/com/jfinal/render/TextRender.java
  57. 8 12
      src/com/jfinal/render/VelocityRender.java
  58. 1 1
      src/com/jfinal/util/JsonBuilder.java
  59. 1 1
      src/com/jfinal/validate/ValidateException.java

+ 8 - 0
src/com/jfinal/config/Constants.java

@@ -19,6 +19,8 @@ package com.jfinal.config;
 import java.io.File;
 import java.util.Locale;
 import com.jfinal.core.Const;
+import com.jfinal.log.ILoggerFactory;
+import com.jfinal.log.Logger;
 import com.jfinal.render.IMainRenderFactory;
 import com.jfinal.render.RenderFactory;
 import com.jfinal.render.ViewType;
@@ -286,6 +288,12 @@ final public class Constants {
 		this.viewType = ViewType.OTHER;
 		RenderFactory.setMainRenderFactory(mainRenderFactory);
 	}
+	
+	public void setLoggerFactory(ILoggerFactory loggerFactory) {
+		if (loggerFactory == null)
+			throw new IllegalArgumentException("loggerFactory can not be null.");
+		Logger.setLoggerFactory(loggerFactory);
+	}
 }
 
 

+ 34 - 0
src/com/jfinal/core/ActionKey.java

@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2011-2012, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.core;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * ActionKey is used to configure actionKey for method of controller.
+ */
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface ActionKey {
+	String value();
+}
+

+ 18 - 1
src/com/jfinal/core/ActionMapping.java

@@ -71,7 +71,24 @@ final class ActionMapping {
 					Interceptor[] actionInters = interceptorBuilder.buildActionInterceptors(defaultInters, controllerInters, controllerClass, methodInters, method);
 					String controllerKey = entry.getKey();
 					
-					if (methodName.equals("index")) {
+					ActionKey ak = method.getAnnotation(ActionKey.class);
+					if (ak != null) {
+						String actionKey = ak.value().trim();
+						if ("".equals(actionKey))
+							throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
+						
+						if (!actionKey.startsWith(SLASH))
+							actionKey = SLASH + actionKey;
+						
+						if (actionMapping.containsKey(actionKey)) {
+							warnning(actionKey, controllerClass, method);
+							continue;
+						}
+						
+						Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));
+						actionMapping.put(actionKey, action);
+					}
+					else if (methodName.equals("index")) {
 						String actionKey = controllerKey;
 						
 						Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));

+ 1 - 1
src/com/jfinal/core/ActionRender.java

@@ -21,9 +21,9 @@ import com.jfinal.render.Render;
 /**
  * ActionRender
  */
-@SuppressWarnings("serial")
 final class ActionRender extends Render {
 	
+	private static final long serialVersionUID = 3712913909977013446L;
 	private String actionUrl;
 	
 	public ActionRender(String actionUrl) {

+ 1 - 0
src/com/jfinal/core/Config.java

@@ -90,6 +90,7 @@ class Config {
 	}
 	
 	private static void initLoggerFactory() {
+		Logger.init();
 		log = Logger.getLogger(Config.class);
 		JFinalFilter.initLogger();
 	}

+ 1 - 1
src/com/jfinal/core/Const.java

@@ -24,7 +24,7 @@ import com.jfinal.render.ViewType;
  */
 public interface Const {
 	
-	String JFINAL_VERSION = "1.1.4";
+	String JFINAL_VERSION = "1.1.5";
 	
 	ViewType DEFAULT_VIEW_TYPE = ViewType.FREE_MARKER;
 	

+ 18 - 0
src/com/jfinal/core/Controller.java

@@ -884,6 +884,24 @@ public abstract class Controller {
 	}
 	
 	/**
+	 * Render with json text.
+	 * <p>
+	 * Example: renderJson("{\"message\":\"Please input password!\"}");
+	 */
+	public void renderJson(String jsonText) {
+		render = renderFactory.getJsonRender(jsonText);
+	}
+	
+	/**
+	 * Render json with object.
+	 * <p>
+	 * Example: renderJson(new User().set("name", "JFinal").set("age", 18));
+	 */
+	public void renderJson(Object object) {
+		render = renderFactory.getJsonRender(object);
+	}
+	
+	/**
 	 * Render with text. The contentType is: "text/plain".
 	 */
 	public void renderText(String text) {

+ 2 - 1
src/com/jfinal/core/ModelInjector.java

@@ -105,9 +105,10 @@ final class ModelInjector {
 	}
 }
 
-@SuppressWarnings("serial")
 class ModelInjectException extends RuntimeException {
 	
+	private static final long serialVersionUID = 867623224283092808L;
+	
 	public ModelInjectException(Throwable cause) {
 		super(cause);
 	}

+ 2 - 2
src/com/jfinal/core/TypeConverter.java

@@ -52,7 +52,7 @@ final class TypeConverter {
 		// 以上两种情况无需转换,直接返回, 注意, 本方法不接受null为 s 参数(经测试永远不可能传来null, 因为无输入传来的也是"")
 		
 		Object result = null;
-		// mysql type: int, integer, tinyint, smallint, mediumint
+		// mysql type: int, integer, tinyint(n) n > 1, smallint, mediumint
 		if (clazz == Integer.class || clazz == int.class) {
 			result = Integer.parseInt(s);
 		}
@@ -99,7 +99,7 @@ final class TypeConverter {
         else if (clazz == Float.class) {
         	result = Float.parseFloat(s);
 		}
-		// mysql type: bit
+		// mysql type: bit, tinyint(1)
         else if (clazz == Boolean.class) {
         	result = Boolean.parseBoolean(s);
 		}

+ 0 - 60
src/com/jfinal/ext/handler/DownloadHandler.java

@@ -1,60 +0,0 @@
-/**
- * Copyright (c) 2011-2012, James Zhan 詹波 (jfinal@126.com).
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jfinal.ext.handler;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import com.jfinal.handler.Handler;
-import com.jfinal.util.StringKit;
-
-/**
- * 处理文件下载调用 action 时无扩展名的问题
- * 1: actionKey + 下载文件扩展名如: actionKey.rar
- * 2: add(new DwonloadHandler("download"));
- * 3: actionKey.rar?download
- */
-public class DownloadHandler extends Handler {
-	
-	private String downloadPara;
-	
-	public DownloadHandler() {
-		downloadPara = "download";
-	}
-	
-	public DownloadHandler(String downloadPara) {
-		if (StringKit.isBlank(downloadPara))
-			throw new IllegalArgumentException("Parameter can not be blank.");
-		this.downloadPara = downloadPara;
-	}
-	
-	public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
-		if (request.getParameter(downloadPara) != null) {
-			int index = target.lastIndexOf(".");
-			if (index != -1) {
-				nextHandler.handle(target.substring(0, index), request, response, isHandled);
-				return ;
-			}
-		}
-		
-		nextHandler.handle(target, request, response, isHandled);
-	}
-}
-
-
-
-
-

+ 2 - 0
src/com/jfinal/ext/handler/UrlSkipHandler.java

@@ -25,6 +25,8 @@ import com.jfinal.util.StringKit;
 /**
  * Skip the excluded url request from browser.
  * The skiped url will be handled by next Filter after JFinalFilter
+ * <p>
+ * Example: me.add(new UrlSkipHandler(".+\\.\\w{1,4}", false));
  */
 public class UrlSkipHandler extends Handler {
 	

+ 2 - 2
src/com/jfinal/ext/interceptor/Restful.java

@@ -28,8 +28,8 @@ import com.jfinal.core.Controller;
  */
 public class Restful implements Interceptor {
 	
-	@SuppressWarnings("serial")
-	private Set<String> set = new HashSet<String>() {{
+	private Set<String> set = new HashSet<String>() {
+		private static final long serialVersionUID = 2717581127375143508L;{
 		// add edit 与  JFinal 原有规则相同
 		add("show");
 		add("save");

+ 21 - 21
src/com/jfinal/ext/interceptor/SessionInViewInterceptor.java

@@ -56,81 +56,81 @@ public class SessionInViewInterceptor implements Interceptor {
 	}
 }
 
-@SuppressWarnings({"deprecation", "serial", "rawtypes"})
+@SuppressWarnings({"rawtypes", "deprecation"})
 class JFinalSession extends HashMap implements HttpSession {
-	
-	private HttpSession s;
+	private static final long serialVersionUID = -6148316613614087335L;
+	private HttpSession session;
 	
 	public JFinalSession(HttpSession session) {
-		this.s = session;
+		this.session = session;
 	}
 	
 	public Object getAttribute(String key) {
-		return s.getAttribute(key);
+		return session.getAttribute(key);
 	}
 	
 	public Enumeration getAttributeNames() {
-		return s.getAttributeNames();
+		return session.getAttributeNames();
 	}
 	
 	public long getCreationTime() {
-		return s.getCreationTime();
+		return session.getCreationTime();
 	}
 	
 	public String getId() {
-		return s.getId();
+		return session.getId();
 	}
 	
 	public long getLastAccessedTime() {
-		return s.getLastAccessedTime();
+		return session.getLastAccessedTime();
 	}
 	
 	public int getMaxInactiveInterval() {
-		return s.getMaxInactiveInterval();
+		return session.getMaxInactiveInterval();
 	}
 	
 	public ServletContext getServletContext() {
-		return s.getServletContext();
+		return session.getServletContext();
 	}
 	
 	public javax.servlet.http.HttpSessionContext getSessionContext() {
-		return s.getSessionContext();
+		return session.getSessionContext();
 	}
 	
 	public Object getValue(String key) {
-		return s.getValue(key);
+		return session.getValue(key);
 	}
 	
 	public String[] getValueNames() {
-		return s.getValueNames();
+		return session.getValueNames();
 	}
 	
 	public void invalidate() {
-		s.invalidate();
+		session.invalidate();
 	}
 	
 	public boolean isNew() {
-		return s.isNew();
+		return session.isNew();
 	}
 	
 	public void putValue(String key, Object value) {
-		s.putValue(key, value);
+		session.putValue(key, value);
 	}
 	
 	public void removeAttribute(String key) {
-		s.removeAttribute(key);
+		session.removeAttribute(key);
 	}
 	
 	public void removeValue(String key) {
-		s.removeValue(key);
+		session.removeValue(key);
 	}
 	
 	public void setAttribute(String key, Object value) {
-		s.setAttribute(key, value);
+		session.setAttribute(key, value);
 	}
 	
 	public void setMaxInactiveInterval(int maxInactiveInterval) {
-		s.setMaxInactiveInterval(maxInactiveInterval);
+		session.setMaxInactiveInterval(maxInactiveInterval);
 	}
 }
 

+ 3 - 2
src/com/jfinal/ext/render/CaptchaRender.java

@@ -30,9 +30,9 @@ import com.jfinal.core.Controller;
 import com.jfinal.render.Render;
 import com.jfinal.util.StringKit;
 
-@SuppressWarnings("serial")
 public class CaptchaRender extends Render {
 	
+	private static final long serialVersionUID = -916701543933591834L;
 	private static final int WIDTH = 85, HEIGHT = 20;
 	private static final String[] strArr = {"3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"};
 	
@@ -65,7 +65,8 @@ public class CaptchaRender extends Render {
 			throw new RuntimeException(e);
 		}
 		finally {
-			try {sos.close();} catch (IOException e) {e.printStackTrace();}
+			if (sos != null)
+				try {sos.close();} catch (IOException e) {e.printStackTrace();}
 		}
 	}
 

+ 3 - 2
src/com/jfinal/ext/render/JsonWithContentTypeRender.java

@@ -28,9 +28,9 @@ import com.jfinal.util.JsonBuilder;
 /**
  * JsonRenderWithContentType
  */
-@SuppressWarnings("serial")
 public class JsonWithContentTypeRender extends Render {
 	
+	private static final long serialVersionUID = 3672646716279300085L;
 	private String key;
 	private Object value;
 	private String[] attrs;
@@ -67,7 +67,8 @@ public class JsonWithContentTypeRender extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 	

+ 2 - 3
src/com/jfinal/log/ILoggerFactory.java

@@ -19,10 +19,9 @@ package com.jfinal.log;
 /**
  * ILoggerFactory.
  */
-interface ILoggerFactory {
+public interface ILoggerFactory {
 	
 	Logger getLogger(Class<?> clazz);
 	
 	Logger getLogger(String name);
-	
-}
+}

+ 1 - 1
src/com/jfinal/log/JdkLoggerFactory.java

@@ -19,7 +19,7 @@ package com.jfinal.log;
 /**
  * JdkLoggerFactory.
  */
-class JdkLoggerFactory implements ILoggerFactory {
+public class JdkLoggerFactory implements ILoggerFactory {
 	
 	public Logger getLogger(Class<?> clazz) {
 		return new JdkLogger(clazz);

+ 1 - 1
src/com/jfinal/log/Log4jLoggerFactory.java

@@ -19,7 +19,7 @@ package com.jfinal.log;
 /**
  * Log4jLoggerFactory.
  */
-class Log4jLoggerFactory implements ILoggerFactory {
+public class Log4jLoggerFactory implements ILoggerFactory {
 	
 	public Logger getLogger(Class<?> clazz) {
 		return new Log4jLogger(clazz);

+ 11 - 4
src/com/jfinal/log/Logger.java

@@ -26,7 +26,12 @@ package com.jfinal.log;
  */
 public abstract class Logger {
 	
-	private static final ILoggerFactory factory = createLoggerFactory();
+	private static ILoggerFactory factory;
+	
+	public static void setLoggerFactory(ILoggerFactory loggerFactory) {
+		if (loggerFactory != null)
+			Logger.factory = loggerFactory;
+	}
 	
 	public static Logger getLogger(Class<?> clazz) {
 		return factory.getLogger(clazz);
@@ -36,13 +41,15 @@ public abstract class Logger {
 		return factory.getLogger(name);
 	}
 	
-	private static ILoggerFactory createLoggerFactory() {
+	public static void init() {
+		if (factory != null)
+			return ;
 		try {
 			Class.forName("org.apache.log4j.Logger");
 			Class<?> log4jLoggerFactoryClass = Class.forName("com.jfinal.log.Log4jLoggerFactory");
-			return (ILoggerFactory)log4jLoggerFactoryClass.newInstance();	// return new Log4jLoggerFactory();
+			factory = (ILoggerFactory)log4jLoggerFactoryClass.newInstance();	// return new Log4jLoggerFactory();
 		} catch (Exception e) {
-			return new JdkLoggerFactory();
+			factory = new JdkLoggerFactory();
 		}
 	}
 	

+ 1 - 1
src/com/jfinal/log/NullLoggerFactory.java

@@ -19,7 +19,7 @@ package com.jfinal.log;
 /**
  * NullLoggerFactory.
  */
-class NullLoggerFactory implements ILoggerFactory {
+public class NullLoggerFactory implements ILoggerFactory {
 	
 	public com.jfinal.log.Logger getLogger(Class<?> clazz) {
 		return INSTANCE;

+ 2 - 1
src/com/jfinal/plugin/activerecord/ActiveRecordException.java

@@ -19,9 +19,10 @@ package com.jfinal.plugin.activerecord;
 /**
  * ActiveRecordException
  */
-@SuppressWarnings("serial")
 public class ActiveRecordException extends RuntimeException {
 	
+	private static final long serialVersionUID = 342820722361408621L;
+	
 	public ActiveRecordException(String message) {
 		super(message);
 	}

+ 2 - 2
src/com/jfinal/plugin/activerecord/ActiveRecordPlugin.java

@@ -57,8 +57,8 @@ public class ActiveRecordPlugin implements IPlugin {
 		return this;
 	}
 	
-	public ActiveRecordPlugin setMapFactory(IMapFactory mapFactory) {
-		DbKit.setMapFactory(mapFactory);
+	public ActiveRecordPlugin setContainerFactory(IContainerFactory containerFactory) {
+		DbKit.setContainerFactory(containerFactory);
 		return this;
 	}
 	

+ 97 - 0
src/com/jfinal/plugin/activerecord/CaseInsensitiveContainerFactory.java

@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2011-2012, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.plugin.activerecord;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class CaseInsensitiveContainerFactory implements IContainerFactory {
+	
+	private boolean toLowerCase = false;
+	
+	public CaseInsensitiveContainerFactory() {
+	}
+	
+	public CaseInsensitiveContainerFactory(boolean toLowerCase) {
+		this.toLowerCase = toLowerCase;
+	}
+	
+	public Map<String, Object> getAttrsMap() {
+		return new CaseInsensitiveMap();
+	}
+	
+	public Map<String, Object> getColumnsMap() {
+		return new CaseInsensitiveMap();
+	}
+	
+	public Set<String> getModifyFlagSet() {
+		return new CaseInsensitiveSet();
+	}
+	
+	private Object convertCase(Object key) {
+		if (key instanceof String)
+			return toLowerCase ? ((String)key).toLowerCase() : ((String)key).toUpperCase();
+		return key;
+	}
+	
+	class CaseInsensitiveSet extends HashSet {
+		
+		private static final long serialVersionUID = 102410961064096233L;
+		
+		public boolean add(Object e) {
+			return super.add(convertCase(e));
+		}
+		
+		public boolean remove(Object e) {
+			return super.remove(convertCase(e));
+		}
+		
+		public boolean contains(Object e) {
+			return super.contains(convertCase(e));
+		}
+	}
+	
+	class CaseInsensitiveMap extends HashMap {
+		
+		private static final long serialVersionUID = 6843981594457576677L;
+		
+		public Object get(Object key) {
+			return super.get(convertCase(key));
+		}
+		
+		public boolean containsKey(Object key) {
+			return super.containsKey(convertCase(key));
+		}
+		
+		public Object put(Object key, Object value) {
+			return super.put(convertCase(key), value);
+		}
+		
+		public void putAll(Map m) {
+			for (Map.Entry e : (Set<Map.Entry>)(m.entrySet()))
+	            put(e.getKey(), e.getValue());
+		}
+		
+		public Object remove(Object key) {
+			return super.remove(convertCase(key));
+		}
+	}
+}
+

+ 0 - 53
src/com/jfinal/plugin/activerecord/CaseInsensitiveMapFactory.java

@@ -1,53 +0,0 @@
-package com.jfinal.plugin.activerecord;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-@SuppressWarnings("unchecked")
-public class CaseInsensitiveMapFactory implements IMapFactory {
-	
-	public Map<String, Object> getAttrsMap() {
-		return new CaseInsensitiveMap();
-	}
-	
-	public Map<String, Object> getColumnsMap() {
-		return new CaseInsensitiveMap();
-	}
-}
-
-@SuppressWarnings({"rawtypes", "unchecked"})
-class CaseInsensitiveMap extends HashMap {
-	
-	private static final long serialVersionUID = -3415001825854442053L;
-	
-	@Override
-	public Object get(Object key) {
-		Object k = (key instanceof String ? ((String)key).toUpperCase() : key);
-		return super.get(k);
-	}
-	
-	@Override
-	public boolean containsKey(Object key) {
-		Object k = (key instanceof String ? ((String)key).toUpperCase() : key);
-		return super.containsKey(k);
-	}
-	
-	@Override
-	public Object put(Object key, Object value) {
-		Object k = (key instanceof String ? ((String)key).toUpperCase() : key);
-		return super.put(k, value);
-	}
-	
-	@Override
-	public void putAll(Map m) {
-		for (Map.Entry e : (Set<Map.Entry>)(m.entrySet()))
-            put(e.getKey(), e.getValue());
-	}
-	
-	@Override
-	public Object remove(Object key) {
-		Object k = (key instanceof String ? ((String)key).toUpperCase() : key);
-		return super.remove(k);
-	}
-}

+ 26 - 20
src/com/jfinal/plugin/activerecord/Db.java

@@ -23,6 +23,7 @@ import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import javax.sql.DataSource;
 import com.jfinal.plugin.activerecord.cache.ICache;
 import static com.jfinal.plugin.activerecord.DbKit.NULL_PARA_ARRAY;
@@ -37,9 +38,11 @@ public class Db {
 		List result = new ArrayList();
 		PreparedStatement pst = conn.prepareStatement(sql);
 		
-		for (int i=0; i<paras.length; i++) {
-			pst.setObject(i + 1, paras[i]);
-		}
+		DbKit.dialect.fillStatement(pst, paras);
+		// for (int i=0; i<paras.length; i++) {
+			// pst.setObject(i + 1, paras[i]);
+		// }
+		
 		ResultSet rs = pst.executeQuery();
 		
 		int colAmount = rs.getMetaData().getColumnCount();
@@ -260,9 +263,10 @@ public class Db {
 	 */
 	static int update(Connection conn, String sql, Object... paras) throws SQLException {
 		PreparedStatement pst = conn.prepareStatement(sql);
-		for (int i=0; i<paras.length; i++) {
-			pst.setObject(i + 1, paras[i]);
-		}
+		DbKit.dialect.fillStatement(pst, paras);
+		// for (int i=0; i<paras.length; i++) {
+			// pst.setObject(i + 1, paras[i]);
+		// }
 		int result = pst.executeUpdate();
 		DbKit.closeQuietly(pst);
 		
@@ -335,9 +339,10 @@ public class Db {
 	
 	static List<Record> find(Connection conn, String sql, Object... paras) throws SQLException {
 		PreparedStatement pst = conn.prepareStatement(sql);
-		for (int i=0; i<paras.length; i++) {
-			pst.setObject(i + 1, paras[i]);
-		}
+		DbKit.dialect.fillStatement(pst, paras);
+		// for (int i=0; i<paras.length; i++) {
+			// pst.setObject(i + 1, paras[i]);
+		// }
 		ResultSet rs = pst.executeQuery();
 		List<Record> result = RecordBuilder.build(rs);
 		DbKit.closeQuietly(rs, pst);
@@ -594,16 +599,17 @@ public class Db {
 		DbKit.dialect.forDbSave(sql, paras, tableName, record);
 		
 		PreparedStatement pst;
-		boolean isSupportAutoIncrementKey = DbKit.dialect.isSupportAutoIncrementKey();
-		if (isSupportAutoIncrementKey)
-			pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
+		if (DbKit.dialect.isOracle())
+			pst = conn.prepareStatement(sql.toString(), new String[]{primaryKey});
 		else
-			pst = conn.prepareStatement(sql.toString());
-		for (int i=0, size=paras.size(); i<size; i++) {
-			pst.setObject(i + 1, paras.get(i));
-		}
+			pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
+			
+		DbKit.dialect.fillStatement(pst, paras);
+		// for (int i=0, size=paras.size(); i<size; i++) {
+			// pst.setObject(i + 1, paras.get(i));
+		// }
 		int result = pst.executeUpdate();
-		if (isSupportAutoIncrementKey)
+		// if (isSupportAutoIncrementKey)
 			record.set(primaryKey, getGeneratedKey(pst));
 		DbKit.closeQuietly(pst);
 		
@@ -858,7 +864,7 @@ public class Db {
 		PreparedStatement pst = conn.prepareStatement(sql);
 		for (int i=0; i<paras.length; i++) {
 			for (int j=0; j<paras[i].length; j++) {
-				pst.setObject(j + 1, paras[i][j]);
+				pst.setObject(j + 1, paras[i][j]);	// TODO use Dialect.fillStatement(...)
 			}
 			pst.addBatch();
 			if (++counter >= batchSize) {
@@ -947,9 +953,9 @@ public class Db {
 		int[] result = new int[size];
 		PreparedStatement pst = conn.prepareStatement(sql);
 		for (int i=0; i<size; i++) {
-			java.util.Map map = isModel ? ((Model)list.get(i)).getAttrs() : ((Record)list.get(i)).getColumns();
+			Map map = isModel ? ((Model)list.get(i)).getAttrs() : ((Record)list.get(i)).getColumns();
 			for (int j=0; j<columnArray.length; j++) {
-				pst.setObject(j + 1, map.get(columnArray[j]));
+				pst.setObject(j + 1, map.get(columnArray[j]));		// TODO use Dialect.fillStatement(...)
 			}
 			pst.addBatch();
 			if (++counter >= batchSize) {

+ 7 - 4
src/com/jfinal/plugin/activerecord/DbKit.java

@@ -21,7 +21,9 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import javax.sql.DataSource;
 import com.jfinal.plugin.activerecord.cache.EhCache;
 import com.jfinal.plugin.activerecord.cache.ICache;
@@ -44,14 +46,15 @@ public final class DbKit {
 	static boolean devMode = false;
 	static Dialect dialect = new MysqlDialect();
 	
-	static IMapFactory mapFactory = new IMapFactory(){
+	static IContainerFactory containerFactory = new IContainerFactory(){
 		public Map<String, Object> getAttrsMap() {return new HashMap<String, Object>();}
 		public Map<String, Object> getColumnsMap() {return new HashMap<String, Object>();}
+		public Set<String> getModifyFlagSet() {return new HashSet<String>();}
 	};
 	
-	static void setMapFactory(IMapFactory mapFactory) {
-		if (mapFactory != null)
-			DbKit.mapFactory = mapFactory;
+	static void setContainerFactory(IContainerFactory containerFactory) {
+		if (containerFactory != null)
+			DbKit.containerFactory = containerFactory;
 	}
 	
 	static void setDevMode(boolean devMode) {

+ 27 - 0
src/com/jfinal/plugin/activerecord/IContainerFactory.java

@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2011-2012, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.plugin.activerecord;
+
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings("rawtypes")
+public interface IContainerFactory {
+	Map getAttrsMap();
+	Map getColumnsMap();
+	Set getModifyFlagSet();
+}

+ 0 - 9
src/com/jfinal/plugin/activerecord/IMapFactory.java

@@ -1,9 +0,0 @@
-package com.jfinal.plugin.activerecord;
-
-import java.util.Map;
-
-@SuppressWarnings("rawtypes")
-public interface IMapFactory {
-	Map getAttrsMap();
-	Map getColumnsMap();
-}

+ 22 - 15
src/com/jfinal/plugin/activerecord/Model.java

@@ -34,7 +34,11 @@ import com.jfinal.plugin.activerecord.cache.ICache;
 import static com.jfinal.plugin.activerecord.DbKit.NULL_PARA_ARRAY;
 
 /**
- * Model
+ * Model.
+ * <p>
+ * A clever person solves a problem.
+ * A wise person avoids it.
+ * A stupid person makes it.
  */
 @SuppressWarnings({"rawtypes", "unchecked"})
 public abstract class Model<M extends Model> implements Serializable {
@@ -44,7 +48,7 @@ public abstract class Model<M extends Model> implements Serializable {
 	/**
 	 * Attributes of this model
 	 */
-	private Map<String, Object> attrs = DbKit.mapFactory.getAttrsMap();	// new HashMap<String, Object>();
+	private Map<String, Object> attrs = DbKit.containerFactory.getAttrsMap();	// new HashMap<String, Object>();
 	
 	/**
 	 * Flag of column has been modified. update need this flag
@@ -55,7 +59,7 @@ public abstract class Model<M extends Model> implements Serializable {
 	
 	private Set<String> getModifyFlag() {
 		if (modifyFlag == null)
-			modifyFlag = new HashSet<String>();
+			modifyFlag = DbKit.containerFactory.getModifyFlagSet();	// new HashSet<String>();
 		return modifyFlag;
 	}
 	
@@ -277,16 +281,18 @@ public abstract class Model<M extends Model> implements Serializable {
 		int result = 0;
 		try {
 			conn = DbKit.getConnection();
-			boolean isSupportAutoIncrementKey = DbKit.dialect.isSupportAutoIncrementKey();
-			if (isSupportAutoIncrementKey)
-				pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
+			if (DbKit.dialect.isOracle())
+				pst = conn.prepareStatement(sql.toString(), new String[]{tableInfo.getPrimaryKey()});
 			else
-				pst = conn.prepareStatement(sql.toString());
-			for (int i=0, size=paras.size(); i<size; i++) {
-				pst.setObject(i + 1, paras.get(i));
-			}
+				pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
+			
+			DbKit.dialect.fillStatement(pst, paras);
+			// for (int i=0, size=paras.size(); i<size; i++) {
+				// pst.setObject(i + 1, paras.get(i));
+			// }
+			
 			result = pst.executeUpdate();
-			if (isSupportAutoIncrementKey)
+			// if (isSupportAutoIncrementKey)
 				getGeneratedKey(pst, tableInfo);	// getGeneratedKey(pst, tableInfo.getPrimaryKey());
 			getModifyFlag().clear();
 			return result >= 1;
@@ -302,7 +308,7 @@ public abstract class Model<M extends Model> implements Serializable {
 	 */
 	private void getGeneratedKey(PreparedStatement pst, TableInfo tableInfo) throws SQLException {
 		String pKey = tableInfo.getPrimaryKey();
-		if (get(pKey) == null) {
+		if (get(pKey) == null || DbKit.dialect.isOracle()) {
 			ResultSet rs = pst.getGeneratedKeys();
 			if (rs.next()) {
 				Class colType = tableInfo.getColType(pKey);
@@ -393,9 +399,10 @@ public abstract class Model<M extends Model> implements Serializable {
 			checkTableName(modelClass, sql);
 		
 		PreparedStatement pst = conn.prepareStatement(sql);
-		for (int i=0; i<paras.length; i++) {
-			pst.setObject(i + 1, paras[i]);
-		}
+		DbKit.dialect.fillStatement(pst, paras);
+		// for (int i=0; i<paras.length; i++) {
+			// pst.setObject(i + 1, paras[i]);
+		// }
 		
 		ResultSet rs = pst.executeQuery();
 		List<M> result = ModelBuilder.build(rs, modelClass);

+ 1 - 1
src/com/jfinal/plugin/activerecord/Record.java

@@ -29,7 +29,7 @@ public class Record implements Serializable {
 	
 	private static final long serialVersionUID = -3254070837297655225L;
 	@SuppressWarnings("unchecked")
-	private Map<String, Object> columns = DbKit.mapFactory.getColumnsMap();	// new HashMap<String, Object>();
+	private Map<String, Object> columns = DbKit.containerFactory.getColumnsMap();	// new HashMap<String, Object>();
 	
 	/**
 	 * Return columns map.

+ 1 - 1
src/com/jfinal/plugin/activerecord/TableInfo.java

@@ -27,7 +27,7 @@ public class TableInfo {
 	private String tableName;
 	private String primaryKey;
 	@SuppressWarnings("unchecked")
-	private Map<String, Class<?>> columnTypeMap = DbKit.mapFactory.getAttrsMap();	//	new HashMap<String, Class<?>>();
+	private Map<String, Class<?>> columnTypeMap = DbKit.containerFactory.getAttrsMap();	//	new HashMap<String, Class<?>>();
 	
 	public String getTableName() {
 		return tableName;

+ 0 - 4
src/com/jfinal/plugin/activerecord/dialect/AnsiSqlDialect.java

@@ -168,10 +168,6 @@ public class AnsiSqlDialect extends Dialect {
 		paras.add(id);
 	}
 	
-	public boolean isSupportAutoIncrementKey() {
-		return true;
-	}
-	
 	/**
 	 * SELECT * FROM subject t1 WHERE (SELECT count(*) FROM subject t2 WHERE t2.id < t1.id AND t2.key = '123') > = 10 AND (SELECT count(*) FROM subject t2 WHERE t2.id < t1.id AND t2.key = '123') < 20 AND t1.key = '123'
 	 */

+ 16 - 1
src/com/jfinal/plugin/activerecord/dialect/Dialect.java

@@ -17,6 +17,7 @@
 package com.jfinal.plugin.activerecord.dialect;
 
 import java.sql.Connection;
+import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Map;
@@ -41,7 +42,9 @@ public abstract class Dialect {
 	public abstract void forDbUpdate(String tableName, String primaryKey, Object id, Record record, StringBuilder sql, List<Object> paras);
 	public abstract void forPaginate(StringBuilder sql, int pageNumber, int pageSize, String select, String sqlExceptSelect);
 	
-	public abstract boolean isSupportAutoIncrementKey();
+	public boolean isOracle() {
+		return false;
+	}
 	
 	public boolean isTakeOverDbPaginate() {
 		return false;
@@ -59,6 +62,18 @@ public abstract class Dialect {
 	public Page takeOverModelPaginate(Class<? extends Model> modelClass, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
 		throw new RuntimeException("You should implements this method in " + getClass().getName());
 	}
+	
+	public void fillStatement(PreparedStatement pst, List<Object> paras) throws SQLException {
+		for (int i=0, size=paras.size(); i<size; i++) {
+			pst.setObject(i + 1, paras.get(i));
+		}
+	}
+	
+	public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException {
+		for (int i=0; i<paras.length; i++) {
+			pst.setObject(i + 1, paras[i]);
+		}
+	}
 }
 
 

+ 0 - 4
src/com/jfinal/plugin/activerecord/dialect/MysqlDialect.java

@@ -161,8 +161,4 @@ public class MysqlDialect extends Dialect {
 		sql.append(sqlExceptSelect);
 		sql.append(" limit ").append(offset).append(", ").append(pageSize);	// limit can use one or two '?' to pass paras
 	}
-	
-	public boolean isSupportAutoIncrementKey() {
-		return true;
-	}
 }

+ 45 - 8
src/com/jfinal/plugin/activerecord/dialect/OracleDialect.java

@@ -16,6 +16,8 @@
 
 package com.jfinal.plugin.activerecord.dialect;
 
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -32,19 +34,27 @@ public class OracleDialect extends Dialect {
 		return "select * from " + tableName + " where rownum = 0";
 	}
 	
+	// insert into table (id,name) values(seq.nextval, ?)
 	public void forModelSave(TableInfo tableInfo, Map<String, Object> attrs, StringBuilder sql, List<Object> paras) {
 		sql.append("insert into ").append(tableInfo.getTableName()).append("(");
 		StringBuilder temp = new StringBuilder(") values(");
+		String pKey = tableInfo.getPrimaryKey();
+		int count = 0;
 		for (Entry<String, Object> e: attrs.entrySet()) {
 			String colName = e.getKey();
 			if (tableInfo.hasColumnLabel(colName)) {
-				if (paras.size() > 0) {
+				if (count++ > 0) {
 					sql.append(", ");
 					temp.append(", ");
 				}
 				sql.append(colName);
-				temp.append("?");
-				paras.add(e.getValue());
+				Object value = e.getValue();
+				if(colName.equalsIgnoreCase(pKey) && value instanceof String && (((String)value).endsWith(".nextval"))) {
+				    temp.append(value);
+				}else{
+				    temp.append("?");
+				    paras.add(value);
+				}
 			}
 		}
 		sql.append(temp.toString()).append(")");
@@ -125,14 +135,21 @@ public class OracleDialect extends Dialect {
 		StringBuilder temp = new StringBuilder();
 		temp.append(") values(");
 		
+		int count = 0;
 		for (Entry<String, Object> e: record.getColumns().entrySet()) {
-			if (paras.size() > 0) {
+			if (count++ > 0) {
 				sql.append(", ");
 				temp.append(", ");
 			}
 			sql.append(e.getKey());
-			temp.append("?");
-			paras.add(e.getValue());
+			
+			Object value = e.getValue();
+			if(value instanceof String && (((String)value).endsWith(".nextval"))) {
+			    temp.append(value);
+			}else{
+				temp.append("?");
+				paras.add(value);
+			}
 		}
 		sql.append(temp.toString()).append(")");
 	}
@@ -162,7 +179,27 @@ public class OracleDialect extends Dialect {
 		sql.append(" where table_alias.rownum_ >= ").append(satrt);
 	}
 	
-	public boolean isSupportAutoIncrementKey() {
-		return false;
+	public boolean isOracle() {
+		return true;
+	}
+	
+	public void fillStatement(PreparedStatement pst, List<Object> paras) throws SQLException {
+		for (int i=0, size=paras.size(); i<size; i++) {
+			Object value = paras.get(i);
+			if (value instanceof java.sql.Date)
+				pst.setDate(i + 1, (java.sql.Date)value);
+			else
+				pst.setObject(i + 1, value);
+		}
+	}
+	
+	public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException {
+		for (int i=0; i<paras.length; i++) {
+			Object value = paras[i];
+			if (value instanceof java.sql.Date)
+				pst.setDate(i + 1, (java.sql.Date)value);
+			else
+				pst.setObject(i + 1, value);
+		}
 	}
 }

+ 0 - 4
src/com/jfinal/plugin/activerecord/dialect/PostgreSqlDialect.java

@@ -153,10 +153,6 @@ public class PostgreSqlDialect extends Dialect {
 		paras.add(id);
 	}
 	
-	public boolean isSupportAutoIncrementKey() {
-		return true;
-	}
-	
 	public void forPaginate(StringBuilder sql, int pageNumber, int pageSize, String select, String sqlExceptSelect) {
 		int offset = pageSize * (pageNumber - 1);
 		sql.append(select).append(" ");

+ 0 - 4
src/com/jfinal/plugin/activerecord/dialect/Sqlite3Dialect.java

@@ -159,8 +159,4 @@ public class Sqlite3Dialect extends Dialect {
 		sql.append(sqlExceptSelect);
 		sql.append(" limit ").append(offset).append(", ").append(pageSize);
 	}
-	
-	public boolean isSupportAutoIncrementKey() {
-		return true;
-	}
 }

+ 3 - 1
src/com/jfinal/plugin/druid/DruidStatViewHandler.java

@@ -59,8 +59,10 @@ public class DruidStatViewHandler extends Handler {
 		}
 	}
 	
-	@SuppressWarnings("serial")
 	class JFinalStatViewServlet extends StatViewServlet {
+		
+		private static final long serialVersionUID = 2898674199964021798L;
+		
 		public boolean isPermittedRequest(HttpServletRequest request) {
 			return auth.isPermitted(request);
 		}

+ 0 - 67
src/com/jfinal/render/CPI.java

@@ -1,67 +0,0 @@
-/**
- * Copyright (c) 2011-2012, James Zhan 詹波 (jfinal@126.com).
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.jfinal.render;
-
-import java.util.Properties;
-import com.jfinal.log.Logger;
-import freemarker.template.Configuration;
-import freemarker.template.TemplateException;
-
-/**
- * Cross Package Invoking for render.
- */
-public abstract class CPI {
-	
-	private static final Logger log = Logger.getLogger(CPI.class);
-	
-	/**
-	 * Set freemarker's property.
-	 * The value of template_update_delay is 5 seconds.
-	 * Example: CPI.setFreeMarkerProperty("template_update_delay", "1600");
-	 */
-	public static void setFreeMarkerProperty(String propertyName, String propertyValue) {
-		try {
-			FreeMarkerRender.getConfiguration().setSetting(propertyName, propertyValue);
-		} catch (TemplateException e) {
-			log.error(e.getMessage(), e);
-		}
-	}
-	
-	public static void setFreeMarkerProperties(Properties properties) {
-		try {
-			FreeMarkerRender.getConfiguration().setSettings(properties);
-		} catch (TemplateException e) {
-			log.error(e.getMessage(), e);
-		}
-	}
-	
-	public static void setJspRenderSupportActiveRecord(boolean supportActiveRecord) {
-		JspRender.setSupportActiveRecord(supportActiveRecord);
-	}
-	
-	public static Configuration getFreeMarkerConfiguration() {
-		return FreeMarkerRender.getConfiguration(); 
-	}
-	
-	public static void setVelocityProperties(Properties properties) {
-		VelocityRender.setProperties(properties);
-	}
-}
-
-
-
-

+ 1 - 1
src/com/jfinal/render/Error404Exception.java

@@ -19,9 +19,9 @@ package com.jfinal.render;
 /**
  * Error404Exception.
  */
-@SuppressWarnings("serial")
 public class Error404Exception extends RuntimeException {
 	
+	private static final long serialVersionUID = 7620194943724436754L;
 	private Render error404Render;
 	
 	public Error404Exception(Render error404Render) {

+ 4 - 3
src/com/jfinal/render/Error404Render.java

@@ -24,9 +24,9 @@ import com.jfinal.core.Const;
 /**
  * Error404Render.
  */
-@SuppressWarnings("serial")
-class Error404Render extends Render {
+public class Error404Render extends Render {
 	
+	private static final long serialVersionUID = 1764764489766904795L;
 	private static final String contentType = "text/html;charset=" + getEncoding();
 	private static final String defaultHtml = "<html><head><title>404 Not Found</title></head><body bgcolor='white'><center><h1>404 Not Found</h1></center><hr><center><a href='http://www.jfinal.com'>JFinal/" + Const.JFINAL_VERSION + "</a></center></body></html>";
 	private Render render;
@@ -61,7 +61,8 @@ class Error404Render extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 }

+ 1 - 1
src/com/jfinal/render/Error500Exception.java

@@ -19,9 +19,9 @@ package com.jfinal.render;
 /**
  * Error500Exception.
  */
-@SuppressWarnings("serial")
 public class Error500Exception extends RuntimeException {
 	
+	private static final long serialVersionUID = -7521710800649772411L;
 	private Render error500Render;
 	
 	public Error500Exception(Render error500Render) {

+ 4 - 3
src/com/jfinal/render/Error500Render.java

@@ -24,9 +24,9 @@ import com.jfinal.core.Const;
 /**
  * Error500Render.
  */
-@SuppressWarnings("serial")
-class Error500Render extends Render {
+public class Error500Render extends Render {
 	
+	private static final long serialVersionUID = 4864834986049401413L;
 	private static final String contentType = "text/html;charset=" + getEncoding();
 	private static final String defaultHtml = "<html><head><title>500 Internal Server Error</title></head><body bgcolor='white'><center><h1>500 Internal Server Error</h1></center><hr><center><a href='http://www.jfinal.com'>JFinal/" + Const.JFINAL_VERSION + "</a></center></body></html>";
 	private Render render;
@@ -61,7 +61,8 @@ class Error500Render extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 }

+ 2 - 2
src/com/jfinal/render/FileRender.java

@@ -29,9 +29,9 @@ import com.jfinal.util.PathUtil;
 /**
  * FileRender.
  */
-@SuppressWarnings("serial")
-class FileRender extends Render {
+public class FileRender extends Render {
 	
+	private static final long serialVersionUID = -627386273750207255L;
 	private File file;
 	private String fileName;
 	private static String fileDownloadPath;

+ 30 - 8
src/com/jfinal/render/FreeMarkerRender.java

@@ -16,27 +16,27 @@
 
 package com.jfinal.render;
 
-import java.io.IOException;
-import java.io.Writer;
+import java.io.PrintWriter;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Properties;
 import javax.servlet.ServletContext;
 import freemarker.template.Configuration;
 import freemarker.template.ObjectWrapper;
 import freemarker.template.Template;
+import freemarker.template.TemplateException;
 import freemarker.template.TemplateExceptionHandler;
 
 /**
  * FreeMarkerRender.
  */
-@SuppressWarnings("serial")
-class FreeMarkerRender extends Render {
+public class FreeMarkerRender extends Render {
 	
+	private static final long serialVersionUID = 3959102981898502071L;
 	private transient static final String encoding = getEncoding();
 	private transient static final String contentType = "text/html; charset=" + encoding;
-	
 	private transient static final Configuration config = new Configuration();
 	
 	public FreeMarkerRender(String view) {
@@ -50,6 +50,27 @@ class FreeMarkerRender extends Render {
 		return config;
 	}
 	
+	/**
+	 * Set freemarker's property.
+	 * The value of template_update_delay is 5 seconds.
+	 * Example: FreeMarkerRender.setProperty("template_update_delay", "1600");
+	 */
+	public static void setProperty(String propertyName, String propertyValue) {
+		try {
+			FreeMarkerRender.getConfiguration().setSetting(propertyName, propertyValue);
+		} catch (TemplateException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	public static void setProperties(Properties properties) {
+		try {
+			FreeMarkerRender.getConfiguration().setSettings(properties);
+		} catch (TemplateException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
     static void init(ServletContext servletContext, Locale locale, int template_update_delay) {
         // Initialize the FreeMarker configuration;
         // - Create a configuration instance
@@ -98,16 +119,17 @@ class FreeMarkerRender extends Render {
 			root.put(attrName, request.getAttribute(attrName));
 		}
 		
-		Writer writer = null;
+		PrintWriter writer = null;
         try {
-			writer = response.getWriter();
 			Template template = config.getTemplate(view);
+			writer = response.getWriter();
 			template.process(root, writer);		// Merge the data-model and the template
 		} catch (Exception e) {
 			throw new RenderException(e);
 		}
 		finally {
-			try {writer.close();} catch (IOException e) {e.printStackTrace();}
+			if (writer != null)
+				writer.close();
 		}
 	}
 }

+ 4 - 4
src/com/jfinal/render/HtmlRender.java

@@ -22,11 +22,10 @@ import java.io.PrintWriter;
 /**
  * HtmlRender.
  */
-@SuppressWarnings("serial")
-class HtmlRender extends Render {
+public class HtmlRender extends Render {
 	
+	private static final long serialVersionUID = -1805855373995133760L;
 	private static final String contentType = "text/html;charset=" + getEncoding();
-	
 	private String text;
 	
 	public HtmlRender(String text) {
@@ -48,7 +47,8 @@ class HtmlRender extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 }

+ 4 - 4
src/com/jfinal/render/JavascriptRender.java

@@ -22,11 +22,10 @@ import java.io.PrintWriter;
 /**
  * JavascriptRender.
  */
-@SuppressWarnings("serial")
-class JavascriptRender extends Render {
+public class JavascriptRender extends Render {
 	
+	private static final long serialVersionUID = 3378793486917573848L;
 	private static final String contentType = "text/javascript;charset=" + getEncoding();
-	
 	private String jsText;
 	
 	public JavascriptRender(String jsText) {
@@ -44,7 +43,8 @@ class JavascriptRender extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 }

+ 30 - 15
src/com/jfinal/render/JsonRender.java

@@ -26,8 +26,9 @@ import com.jfinal.util.JsonBuilder;
 /**
  * JsonRender.
  */
-@SuppressWarnings("serial")
-class JsonRender extends Render {
+public class JsonRender extends Render {
+	
+	private static final long serialVersionUID = 6970249421567251974L;
 	
 	/**
 	 * http://zh.wikipedia.org/zh/MIME
@@ -40,25 +41,41 @@ class JsonRender extends Render {
 	 */
 	private static final String contentType = "application/json;charset=" + getEncoding();
 	
-	private String key;
-	private Object value;
+	private String jsonText;
 	private String[] attrs;
 	
 	public JsonRender() {
 		
 	}
 	
-	public JsonRender(String key, Object value) {
-		this.key = key;
-		this.value = value;
+	@SuppressWarnings("serial")
+	public JsonRender(final String key, final Object value) {
+		if (key == null)
+			throw new IllegalArgumentException("The parameter key can not be null.");
+		this.jsonText = JsonBuilder.mapToJson(new HashMap<String, Object>(){{put(key, value);}}, depth);
 	}
 	
 	public JsonRender(String[] attrs) {
+		if (attrs == null)
+			throw new IllegalArgumentException("The parameter attrs can not be null.");
 		this.attrs = attrs;
 	}
 	
+	public JsonRender(String jsonText) {
+		if (jsonText == null)
+			throw new IllegalArgumentException("The parameter jsonString can not be null.");
+		this.jsonText = jsonText;
+	}
+	
+	public JsonRender(Object object) {
+		if (object == null)
+			throw new IllegalArgumentException("The parameter object can not be null.");
+		this.jsonText = JsonBuilder.toJson(object, depth);
+	}
+	
 	public void render() {
-		String jsonText = buildJsonText();
+		if (jsonText == null)
+			buildJsonText();
 		
 		PrintWriter writer = null;
 		try {
@@ -74,19 +91,17 @@ class JsonRender extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 	
 	private static final int depth = 8;
 	
 	@SuppressWarnings({"rawtypes", "unchecked"})
-	private String buildJsonText() {
+	private void buildJsonText() {
 		Map map = new HashMap();
-		if (key != null) {
-			map.put(key, value);
-		}
-		else if (attrs != null) {
+		if (attrs != null) {
 			for (String key : attrs)
 				map.put(key, request.getAttribute(key));
 		}
@@ -99,7 +114,7 @@ class JsonRender extends Render {
 			}
 		}
 		
-		return JsonBuilder.mapToJson(map, depth);
+		this.jsonText = JsonBuilder.mapToJson(map, depth);
 	}
 }
 

+ 4 - 3
src/com/jfinal/render/JspRender.java

@@ -30,12 +30,13 @@ import com.jfinal.plugin.activerecord.Record;
 /**
  * JspRender.
  */
-@SuppressWarnings({"serial", "rawtypes", "unchecked"})
-class JspRender extends Render {
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class JspRender extends Render {
 	
+	private static final long serialVersionUID = 4979806661797436765L;
 	private transient static boolean isSupportActiveRecord = true;
 	
-	static void setSupportActiveRecord(boolean supportActiveRecord) {
+	public static void setSupportActiveRecord(boolean supportActiveRecord) {
 		JspRender.isSupportActiveRecord = supportActiveRecord;
 	}
 	

+ 3 - 13
src/com/jfinal/render/NullRender.java

@@ -16,22 +16,12 @@
 
 package com.jfinal.render;
 
-
 /**
- * NullRender
+ * NullRender.
  */
-@SuppressWarnings("serial")
-class NullRender extends Render {
-	
-	private static final NullRender me = new NullRender();
-	
-	private NullRender() {
-		
-	}
+public class NullRender extends Render {
 	
-	static final NullRender me() {
-		return me;
-	}
+	private static final long serialVersionUID = -389288806463724481L;
 	
 	/**
 	 * Render nothing

+ 2 - 2
src/com/jfinal/render/Redirect301Render.java

@@ -21,9 +21,9 @@ import javax.servlet.http.HttpServletResponse;
 /**
  * Redirect301Render.
  */
-@SuppressWarnings("serial")
-class Redirect301Render extends Render {
+public class Redirect301Render extends Render {
 	
+	private static final long serialVersionUID = -6822589387282014944L;
 	private String url;
 	private boolean withOutQueryString;
 	

+ 2 - 2
src/com/jfinal/render/RedirectRender.java

@@ -21,9 +21,9 @@ import java.io.IOException;
 /**
  * RedirectRender with status: 302 Found.
  */
-@SuppressWarnings("serial")
-class RedirectRender extends Render {
+public class RedirectRender extends Render {
 	
+	private static final long serialVersionUID = -3120354341585834890L;
 	private String url;
 	private boolean withOutQueryString;
 	

+ 3 - 2
src/com/jfinal/render/Render.java

@@ -19,18 +19,19 @@ package com.jfinal.render;
 import java.io.Serializable;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import com.jfinal.core.Const;
 
 /**
  * Render.
  */
 public abstract class Render implements Serializable {
 	
-	private static final long serialVersionUID = 1617632731853793227L;
+	private static final long serialVersionUID = -6161983268638909080L;
 	protected String view;
 	protected transient HttpServletRequest request;
 	protected transient HttpServletResponse response;
 	
-	private transient static String encoding;
+	private transient static String encoding = Const.DEFAULT_ENCODING;
 	private transient static boolean devMode;
 	
 	static final void init(String encoding, boolean devMode) {

+ 1 - 1
src/com/jfinal/render/RenderException.java

@@ -21,7 +21,7 @@ package com.jfinal.render;
  */
 public class RenderException extends RuntimeException {
 	
-	private static final long serialVersionUID = -4402731944301013633L;
+	private static final long serialVersionUID = -6448434551667513804L;
 	
 	public RenderException() {
 		super();

+ 9 - 1
src/com/jfinal/render/RenderFactory.java

@@ -141,6 +141,14 @@ public class RenderFactory {
 		return new JsonRender(attrs);
 	}
 	
+	public Render getJsonRender(String jsonText) {
+		return new JsonRender(jsonText);
+	}
+	
+	public Render getJsonRender(Object object) {
+		return new JsonRender(object);
+	}
+	
 	public Render getTextRender(String text) {
 		return new TextRender(text);
 	}
@@ -208,7 +216,7 @@ public class RenderFactory {
 	}
 	
 	public Render getNullRender() {
-		return NullRender.me();
+		return new NullRender();
 	}
 	
 	public Render getJavascriptRender(String jsText) {

+ 4 - 4
src/com/jfinal/render/TextRender.java

@@ -22,11 +22,10 @@ import java.io.PrintWriter;
 /**
  * TextRender.
  */
-@SuppressWarnings("serial")
-class TextRender extends Render {
+public class TextRender extends Render {
 	
+	private static final long serialVersionUID = -5264892635310241831L;
 	private static final String defaultContentType = "text/plain;charset=" + getEncoding();
-	
 	private String text;
 	
 	public TextRender(String text) {
@@ -61,7 +60,8 @@ class TextRender extends Render {
 			throw new RenderException(e);
 		}
 		finally {
-			writer.close();
+			if (writer != null)
+				writer.close();
 		}
 	}
 }

+ 8 - 12
src/com/jfinal/render/VelocityRender.java

@@ -16,8 +16,7 @@
 
 package com.jfinal.render;
 
-import java.io.IOException;
-import java.io.Writer;
+import java.io.PrintWriter;
 import java.util.Enumeration;
 import java.util.Map.Entry;
 import java.util.Iterator;
@@ -33,9 +32,10 @@ import org.apache.velocity.exception.ResourceNotFoundException;
 /**
  * VelocityRender.
  */
-@SuppressWarnings({"serial", "unchecked"})
-class VelocityRender extends Render {
+@SuppressWarnings({"unchecked"})
+public class VelocityRender extends Render {
 	
+	private static final long serialVersionUID = 2195369405439638708L;
 	private transient static final String encoding = getEncoding();
 	private transient static final String contentType = "text/html;charset=" + encoding;
 	private transient static final Properties properties = new Properties();
@@ -67,7 +67,7 @@ class VelocityRender extends Render {
 		properties.setProperty(Velocity.OUTPUT_ENCODING, encoding);
 	}
 	
-	static void setProperties(Properties properties) {
+	public static void setProperties(Properties properties) {
 		Set<Entry<Object, Object>> set = properties.entrySet();
 		for (Iterator<Entry<Object, Object>> it=set.iterator(); it.hasNext();) {
 			Entry<Object, Object> e = it.next();
@@ -81,7 +81,7 @@ class VelocityRender extends Render {
 			 notInit = false;
 		 }
 		
-		Writer writer = null;
+		PrintWriter writer = null;
         try {
             /*
              *  Make a context object and populate with the data.  This
@@ -127,12 +127,8 @@ class VelocityRender extends Render {
             throw new RenderException(e);
         }
         finally {
-        	// if (writer != null)
-        		try {
-					writer.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
+        	if (writer != null)
+        		writer.close();
         }
 	}
 }

+ 1 - 1
src/com/jfinal/util/JsonBuilder.java

@@ -79,7 +79,7 @@ public class JsonBuilder {
 			return "null";
 		
         boolean first = true;
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
 		Iterator iter = list.iterator();
         
         sb.append('[');

+ 1 - 1
src/com/jfinal/validate/ValidateException.java

@@ -19,6 +19,6 @@ package com.jfinal.validate;
 /**
  * ValidateException support short circuit implementation.
  */
-@SuppressWarnings("serial")
 class ValidateException extends RuntimeException {
+	private static final long serialVersionUID = 20920496215941871L;
 }