package org.eweb4j.mvc.config;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.eweb4j.cache.ActionConfigBeanCache;
import org.eweb4j.cache.Props;
import org.eweb4j.cache.SingleBeanCache;
import org.eweb4j.config.LogFactory;
import org.eweb4j.mvc.action.annotation.ActionLevel;
import org.eweb4j.mvc.action.annotation.Controller;
import org.eweb4j.mvc.action.annotation.Result;
import org.eweb4j.mvc.action.annotation.ShowValMess;
import org.eweb4j.mvc.action.annotation.Singleton;
import org.eweb4j.mvc.action.annotation.ValField;
import org.eweb4j.mvc.action.annotation.ValMess;
import org.eweb4j.mvc.action.annotation.ValParam;
import org.eweb4j.mvc.action.annotation.ValParamName;
import org.eweb4j.mvc.action.annotation.Validator;
import org.eweb4j.mvc.config.bean.ActionConfigBean;
import org.eweb4j.mvc.config.bean.ResultConfigBean;
import org.eweb4j.mvc.config.bean.ValidatorConfigBean;
import org.eweb4j.mvc.config.creator.ValidatorUtil;
import org.eweb4j.util.FileUtil;
import org.eweb4j.util.ReflectUtil;
import org.eweb4j.util.StringUtil;
public class ActionAnnotationConfig {
/**
* action注解
*
* @param cb
* @return
*/
public static String readAnnotation(List<String> scanPackages) {
String error = null;
try {
if (scanPackages == null)
return error;
for (String scanPackage : scanPackages) {
if (scanPackage == null || scanPackage.length() == 0)
continue;
// 扫描jar包
String jarPath = FileUtil.getJarPath();
scanPackage(jarPath, scanPackage);
String classDir = FileUtil
.getTopClassPath(ActionAnnotationConfig.class);
scanDir(scanPackage, classDir);
String classDir2 = FileUtil.getClassPath("classes");
scanDir(scanPackage, classDir2);
}
} catch (Exception e) {
error = StringUtil.getExceptionString(e);
LogFactory.getMVCLogger("ERROR").write(error);
}
return error;
}
private static void scanDir(String scanPackage, String classDir)
throws Exception {
File dir = null;
if (".".equals(scanPackage)) {
scanPackage = "";
dir = new File(classDir);
} else
dir = new File(classDir + File.separator
+ scanPackage.replace(".", File.separator));
LogFactory.getMVCLogger("INFO").write("scan " + dir);
// 递归文件目录
if (dir.isDirectory())
scanPackage(dir, scanPackage);
}
/**
* 扫描action文件
*
* @param dir
* @param actionPackage
* @throws Exception
*/
private static void scanPackage(File dir, String actionPackage)
throws Exception {
if (!dir.isDirectory())
return;
File[] files = dir.listFiles();
if (files == null || files.length == 0)
return;
for (File f : files) {
if (f.isDirectory())
if (actionPackage.length() == 0)
scanPackage(f, f.getName());
else
scanPackage(f, actionPackage + "." + f.getName());
else if (f.isFile()) {
if (!f.getName().endsWith(".class"))
continue;
StringBuilder sb = new StringBuilder(actionPackage);
int endIndex = f.getName().lastIndexOf(".");
String clsName = sb.append(".")
.append(f.getName().subSequence(0, endIndex))
.toString();
if (clsName == null || "".equals(clsName))
continue;
if (clsName.startsWith("."))
clsName = clsName.substring(1);
handleActionClass(clsName);
}
}
}
/**
* scan package by jars
*
* @param jarsParentDirPath
* @param packageName
* @throws Exception
*/
public static void scanPackage(String jarsParentDirPath, String packageName) {
String path = jarsParentDirPath;
LogFactory.getMVCLogger("INFO").write("scan " + path + " for jars");
File[] ff = new File(path).listFiles();
if (ff == null)
return;
for (File f : ff) {
ZipInputStream zin = null;
ZipEntry entry = null;
try {
zin = new ZipInputStream(new FileInputStream(f));
LogFactory.getMVCLogger("INFO").write(
"scanning " + f.getAbsolutePath());
while ((entry = zin.getNextEntry()) != null) {
String entryName = entry.getName().replace('/', '.');
if (".".equals(packageName)
|| entryName.startsWith(packageName))
handleActionClass(entryName.replace(".class", ""));
zin.closeEntry();
}
zin.close();
} catch (Error e) {
continue;
} catch (Exception e) {
continue;
}
}
}
/**
* handle action class
*
* @param clsName
* @throws Exception
*/
private static void handleActionClass(String clsName) {
Class<?> cls = null;
try {
cls = Class.forName(clsName);
if (cls == null)
return;
if (cls.getAnnotation(Controller.class) == null
&& !clsName.endsWith("Controller")
&& !clsName.endsWith("Action")
&& !clsName.endsWith("Control"))
return;
Object obj = null;
try {
if (cls.getAnnotation(Singleton.class) != null) {
obj = SingleBeanCache.get(cls);
if (obj == null) {
obj = cls.newInstance();
SingleBeanCache.add(cls, obj);
}
} else
obj = cls.newInstance();
} catch (Exception e) {
LogFactory.getMVCLogger("WARRING").write(
"the action class new instance failued -> " + clsName);
}
ReflectUtil ru = new ReflectUtil(obj);
Method[] ms = ru.getMethods();
if (ms == null)
return;
// 扫描方法的注解信息
for (Method m : ms) {
if (m.getModifiers() != 1)
continue;
Path path = m.getAnnotation(Path.class);
if (path == null) {
String methodName = m.getName();
Method getter = ru.getGetter(methodName.replace("get", ""));
Method setter = ru.getSetter(methodName.replace("set", ""));
// 默认下setter和getter不作为action方法
if (getter != null || setter != null)
continue;
}
readMethodAnnotation(ru, cls, m);
}
} catch (Error e) {
return;
} catch (Exception e) {
return;
}
}
/**
* 读取方法里的注解
*
* @param cls
* @param m
* @throws Exception
*/
private static void readMethodAnnotation(ReflectUtil ru, Class<?> cls,
Method m) {
String methodName = m.getName();
String fullName = cls.getName() + "." + methodName;
LogFactory.getMVCLogger("INFO").write(
"read action.method --> " + fullName);
String methodReqMapVal = null;
Path m_path = m.getAnnotation(Path.class);
if (m_path != null) {
methodReqMapVal = StringUtil.parsePropValue(m_path.value());
} else if (methodName.startsWith("do")) {
methodReqMapVal = methodName.substring("do".length());
methodReqMapVal = StringUtil.toLowCaseFirst(methodReqMapVal);
methodReqMapVal = StringUtil.hump2ohter(methodReqMapVal, "-");
} else {
String info = fullName
+ " does not starts with 'do' so that can not be a valid action uri mapping";
LogFactory.getMVCLogger("INFO").write(info);
return;
}
final String GET = HttpMethod.GET;
final String POST = HttpMethod.POST;
final String PUT = HttpMethod.PUT;
final String DELETE = HttpMethod.DELETE;
String[] methods = new String[4];
methods[0] = cls.getAnnotation(GET.class) != null ? GET : "";
methods[1] = cls.getAnnotation(POST.class) != null ? POST : "";
methods[2] = cls.getAnnotation(DELETE.class) != null ? DELETE : "";
methods[3] = cls.getAnnotation(PUT.class) != null ? PUT : "";
StringBuilder m_sb = new StringBuilder("");
for (String s : methods) {
if (m_sb.length() > 0 && s.length() > 0)
m_sb.append("|");
m_sb.append(s);
}
String controlMethod = m_sb.toString();
ShowValMess cls_vm = cls.getAnnotation(ShowValMess.class);
String controlShowValErr = cls_vm == null ? "alert" : cls_vm.value();
controlShowValErr = StringUtil.parsePropValue(controlShowValErr);
String clazzName = cls.getName();
String reqMethod = controlMethod.length() == 0 ? GET + "|" + POST + "|"
+ PUT + "|" + DELETE : controlMethod;// 默认四种HTTP方法都支持
String valErrType = controlShowValErr.trim().length() == 0 ? "alert"
: controlShowValErr;// 验证器验证信息输出方式默认”alert“
String[] _methods = new String[4];
_methods[0] = m.getAnnotation(GET.class) != null ? GET : "";
_methods[1] = m.getAnnotation(POST.class) != null ? POST : "";
_methods[2] = m.getAnnotation(DELETE.class) != null ? DELETE : "";
_methods[3] = m.getAnnotation(PUT.class) != null ? PUT : "";
StringBuilder _sb = new StringBuilder("");
for (String s : _methods) {
if (_sb.length() > 0 && s.length() > 0)
_sb.append("|");
_sb.append(s);
}
String m_reqMethod = _sb.toString();
reqMethod = m_reqMethod.trim().length() > 0 ? m_reqMethod : reqMethod;
ShowValMess m_vm = m.getAnnotation(ShowValMess.class);
valErrType = m_vm == null ? "alert" : m_vm.value();
controlShowValErr = StringUtil.parsePropValue(valErrType);
// Action properties
String propId = cls.getName();
Hashtable<String, String> props = Props.getMap(propId);
String parentPath = null;
String path = null;
String method = null;
String showValMess = null;
String retn = null;
ResultConfigBean rcb = null;
Path cls_path = cls.getAnnotation(Path.class);
String controlReqMapVal = cls_path == null ? "" : cls_path.value();
controlReqMapVal = StringUtil.parsePropValue(controlReqMapVal);
if (controlReqMapVal.length() > 0 && !methodReqMapVal.startsWith("/"))
controlReqMapVal = controlReqMapVal + "/";
String actionName = controlReqMapVal + methodReqMapVal;
if (actionName.startsWith("/"))
actionName = actionName.substring(1);
if (actionName.endsWith("/"))
actionName = actionName.substring(0, actionName.length() - 1);
if (props != null) {
path = props.get(methodName + ".path");
String _path = path == null ? methodReqMapVal : path;
parentPath = props.get("path");
String _parentPath = parentPath == null ? controlReqMapVal
: parentPath;
actionName = _parentPath + _path;
method = props.get(methodName + ".method");
reqMethod = method == null ? reqMethod : method;
showValMess = props.get(methodName + ".showValMess");
retn = props.get(methodName + ".return");
rcb = null;
if (retn != null) {
rcb = new ResultConfigBean();
rcb.setName("_props_");
if (retn.startsWith("redirect:")) {
rcb.setType(MVCConfigConstant.REDIRECT_TYPE);
rcb.setLocation(retn.replace("redirect:", ""));
} else if (retn.startsWith("forward:")) {
rcb.setType(MVCConfigConstant.FORWARD_TYPE);
rcb.setLocation(retn.replace("forward:", ""));
} else if (retn.equalsIgnoreCase("null")
|| retn.equalsIgnoreCase(MVCConfigConstant.AJAX_TYPE)) {
rcb.setType(MVCConfigConstant.AJAX_TYPE);
} else if (retn.equalsIgnoreCase("action:")) {
rcb.setType(MVCConfigConstant.ACTION_TYPE);
rcb.setLocation(retn.replace("action:", ""));
} else if (retn.startsWith("out:")) {
rcb.setType(MVCConfigConstant.OUT_TYPE);
rcb.setLocation(retn.replace("out:", ""));
} else if (retn.equalsIgnoreCase(MVCConfigConstant.JSON_TYPE)) {
rcb.setType(MVCConfigConstant.JSON_TYPE);
} else {
rcb.setType(MVCConfigConstant.FORWARD_TYPE);
rcb.setLocation(retn);
}
}
}
ActionConfigBean action = new ActionConfigBean();
action.setShowValErrorType(showValMess == null ? valErrType
: showValMess);
action.setClazz(clazzName);
action.setMethod(methodName);
action.setName(actionName);
action.setReqMethod(reqMethod);
// 读取@ActionLevel注解
ActionLevel actionLevel = cls.getAnnotation(ActionLevel.class);
if (actionLevel == null)
actionLevel = m.getAnnotation(ActionLevel.class);
int level = 1;
if (actionLevel != null)
level = actionLevel.value();
action.setLevel(String.valueOf(level));
// 读取@Produces注解
Produces producesAnn = m.getAnnotation(Produces.class);
if (producesAnn != null) {
String producesStr = StringUtil
.parsePropValue(producesAnn.value()[0]);
action.getProduces().add(producesStr);
}
// 读取@Result注解
Result resultAnn = m.getAnnotation(Result.class);
if (resultAnn != null)
action.setResult(ResultAnnUtil.readResultAnn(resultAnn));
if (rcb != null)
action.getResult().add(rcb);
// 读取@Validator注解
Validator validatorAnn = m.getAnnotation(Validator.class);
if (validatorAnn != null)
action.setValidator(ValidatorUtil.readValidator(validatorAnn,
m.getAnnotation(ValField.class),
m.getAnnotation(ValMess.class),
m.getAnnotation(ValParamName.class),
m.getAnnotation(ValParam.class)));
// 读取Action object 的属性 验证信息
List<ValidatorConfigBean> vals = ValidatorUtil.readValidator(null, ru,
null, null);
if (vals != null)
action.setValidator(vals);
// Action全名,框架用,包括对“{xxx}”url参数的正则化,HttpRequestMethod
String actionFullName = ActionUrlUtil.mathersUrlMapping(m, actionName,
cls);
if (actionFullName == null)
return;
if (actionFullName.endsWith("/")) {
actionFullName.substring(0, actionFullName.length() - 1);
}
actionFullName = actionFullName + "@" + reqMethod;
// 将读取成功的配置信息放入缓存供框架运行期使用
ActionConfigBeanCache.add(actionFullName, action);
ActionClassCache.add(clazzName, cls);
}
}