Java tutorial
/* * JSmart Framework - Java Web Development Framework * Copyright (c) 2015, Jeferson Albino da Silva, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ package com.jsmartframework.web.manager; import static com.jsmartframework.web.config.Config.CONFIG; import com.google.gson.internal.Primitives; import com.jsmartframework.web.annotation.AuthAccess; import com.jsmartframework.web.annotation.AuthBean; import com.jsmartframework.web.annotation.AuthField; import com.jsmartframework.web.annotation.AuthMethod; import com.jsmartframework.web.annotation.ExecuteAccess; import com.jsmartframework.web.annotation.ExposeVar; import com.jsmartframework.web.annotation.PostAction; import com.jsmartframework.web.annotation.PostSubmit; import com.jsmartframework.web.annotation.PreAction; import com.jsmartframework.web.annotation.PreSet; import com.jsmartframework.web.annotation.PreSubmit; import com.jsmartframework.web.annotation.Unescape; import com.jsmartframework.web.annotation.VarMapping; import com.jsmartframework.web.annotation.WebBean; import com.jsmartframework.web.annotation.WebFilter; import com.jsmartframework.web.annotation.WebSecurity; import com.jsmartframework.web.annotation.WebServlet; import com.jsmartframework.web.config.UrlPattern; import com.jsmartframework.web.util.WebText; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; enum BeanHelper { HELPER(); private static final String ENCODING = "UTF-8"; private static final Logger LOGGER = Logger.getLogger(BeanHelper.class.getPackage().getName()); private static final Pattern SET_METHOD_PATTERN = Pattern.compile("^set(.*)"); private static final Pattern PROPERTIES_NAME_PATTERN = Pattern.compile("\\s*_(.*)\\.properties"); private static final Pattern PATH_BEAN_ALL_PATTERN = Pattern.compile("(.*)/\\*"); private Map<Class<?>, String> beanNames = new ConcurrentHashMap<>(); private Map<Class<?>, Field[]> beanFields = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> beanMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Field[]> preSetFields = new ConcurrentHashMap<>(); private Map<Class<?>, Field[]> exposeVarFields = new ConcurrentHashMap<>(); private Map<String, List<Class<?>>> exposeVarPaths = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> postConstructMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> preDestroyMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> preSubmitMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> preActionMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> postSubmitMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> postActionMethods = new ConcurrentHashMap<>(); private Map<Class<?>, String[]> unescapeMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> executeAccessMethods = new ConcurrentHashMap<>(); private Map<Class<?>, Field[]> authFields = new ConcurrentHashMap<>(); private Map<Class<?>, Field[]> authAccess = new ConcurrentHashMap<>(); private Map<Class<?>, Method[]> authMethods = new ConcurrentHashMap<>(); private Map<Field, Map<String, WebText.WebTextSet>> varMappingFields = new ConcurrentHashMap<>(); String getBeanName(Class<?> clazz) { return beanNames.get(clazz); } String getClassName(String name) { return name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toLowerCase()); } String getClassName(WebBean webBean, Class<?> beanClass) { String beanName = webBean.name(); if (StringUtils.isBlank(beanName)) { beanName = getClassName(beanClass.getSimpleName()); } beanNames.put(beanClass, beanName); return beanName; } String getClassName(AuthBean authBean, Class<?> authClass) { String beanName = authBean.name(); if (StringUtils.isBlank(beanName)) { beanName = getClassName(authClass.getSimpleName()); } beanNames.put(authClass, beanName); return beanName; } String getClassName(WebServlet servlet, Class<?> servletClass) { if (StringUtils.isBlank(servlet.name())) { String servletName = servletClass.getSimpleName(); return getClassName(servletName); } return servlet.name(); } String getClassName(WebFilter filter, Class<?> filterClass) { if (StringUtils.isBlank(filter.name())) { String filterName = filterClass.getSimpleName(); return getClassName(filterName); } return filter.name(); } String getClassName(WebSecurity security, Class<?> securityClass) { String securityName = securityClass.getSimpleName(); return getClassName(securityName); } Field[] getBeanFields(Class<?> clazz) { if (!beanFields.containsKey(clazz)) { beanFields.put(clazz, getAllDeclaredFields(clazz)); } return beanFields.get(clazz); } void setBeanFields(Class<?> clazz) { if (!beanFields.containsKey(clazz)) { List<Field> preSets = new ArrayList<>(); List<Field> exposeVars = new ArrayList<>(); for (Field field : getAllDeclaredFields(clazz)) { field.setAccessible(true); if (field.isAnnotationPresent(PreSet.class)) { preSets.add(field); } if (field.isAnnotationPresent(ExposeVar.class)) { exposeVars.add(field); ExposeVar exposeVar = field.getAnnotation(ExposeVar.class); if (StringUtils.isNotBlank(exposeVar.value().i18n())) { if (!field.getType().equals(Map.class)) { throw new RuntimeException("Field [" + field + "] annotated with ExposeVar containing " + "VarMapping attribute must be the type of Map<String, Object>"); } setExposeVarMapping(field, exposeVar.value()); } for (String varPath : cleanPaths(exposeVar.forPaths())) { List<Class<?>> classes = exposeVarPaths.get(varPath); if (classes == null) { exposeVarPaths.put(varPath, classes = new ArrayList<>()); } classes.add(clazz); } } } beanFields.put(clazz, getAllDeclaredFields(clazz)); preSetFields.put(clazz, preSets.toArray(new Field[preSets.size()])); exposeVarFields.put(clazz, exposeVars.toArray(new Field[exposeVars.size()])); } } private Field[] getAllDeclaredFields(Class<?> clazz) { List<Field> fields = new ArrayList<>(); Class<?> superClazz = clazz; while (superClazz != null && superClazz != Object.class) { fields.addAll(Arrays.asList(superClazz.getDeclaredFields())); superClazz = superClazz.getSuperclass(); } return fields.toArray(new Field[fields.size()]); } Field[] getPreSetFields(Class<?> clazz) { Field[] fields = preSetFields.get(clazz); return fields != null ? fields : new Field[] {}; } String[] getUnescapeMethods(Class<?> clazz) { String[] methods = unescapeMethods.get(clazz); return methods != null ? methods : new String[] {}; } Method[] getPostConstructMethods(Class<?> clazz) { Method[] methods = postConstructMethods.get(clazz); return methods != null ? methods : new Method[] {}; } Method[] getPreDestroyMethods(Class<?> clazz) { Method[] methods = preDestroyMethods.get(clazz); return methods != null ? methods : new Method[] {}; } @Deprecated Method[] getPostSubmitMethods(Class<?> clazz) { Method[] methods = postSubmitMethods.get(clazz); return methods != null ? methods : new Method[] {}; } Method[] getPostActionMethods(Class<?> clazz) { Method[] methods = postActionMethods.get(clazz); return methods != null ? methods : new Method[] {}; } @Deprecated Method[] getPreSubmitMethods(Class<?> clazz) { Method[] methods = preSubmitMethods.get(clazz); return methods != null ? methods : new Method[] {}; } Method[] getPreActionMethods(Class<?> clazz) { Method[] methods = preActionMethods.get(clazz); return methods != null ? methods : new Method[] {}; } Method[] getExecuteAccessMethods(Class<?> clazz) { Method[] methods = executeAccessMethods.get(clazz); return methods != null ? methods : new Method[] {}; } Method[] getBeanMethods(Class<?> clazz) { if (!beanMethods.containsKey(clazz)) { beanMethods.put(clazz, clazz.getMethods()); } return beanMethods.get(clazz); } void setBeanMethods(Class<?> clazz) { if (!beanMethods.containsKey(clazz)) { List<Method> postConstructs = new ArrayList<>(); List<Method> preDestroys = new ArrayList<>(); List<Method> postSubmits = new ArrayList<>(); List<Method> postActions = new ArrayList<>(); List<Method> preSubmits = new ArrayList<>(); List<Method> preActions = new ArrayList<>(); List<String> unescapes = new ArrayList<>(); List<Method> executeAccess = new ArrayList<>(); for (Method method : clazz.getMethods()) { if (method.isAnnotationPresent(PostConstruct.class)) { postConstructs.add(method); } if (method.isAnnotationPresent(PreDestroy.class)) { preDestroys.add(method); } if (method.isAnnotationPresent(PostSubmit.class)) { postSubmits.add(method); } if (method.isAnnotationPresent(PostAction.class)) { postActions.add(method); } if (method.isAnnotationPresent(PreSubmit.class)) { preSubmits.add(method); } if (method.isAnnotationPresent(PreAction.class)) { preActions.add(method); } if (method.isAnnotationPresent(Unescape.class)) { Matcher matcher = SET_METHOD_PATTERN.matcher(method.getName()); if (matcher.find()) { unescapes.add(matcher.group(1)); } } if (method.isAnnotationPresent(ExecuteAccess.class)) { executeAccess.add(method); } } beanMethods.put(clazz, clazz.getMethods()); executeAccessMethods.put(clazz, executeAccess.toArray(new Method[executeAccess.size()])); postConstructMethods.put(clazz, postConstructs.toArray(new Method[postConstructs.size()])); preDestroyMethods.put(clazz, preDestroys.toArray(new Method[preDestroys.size()])); postSubmitMethods.put(clazz, postSubmits.toArray(new Method[postSubmits.size()])); postActionMethods.put(clazz, postActions.toArray(new Method[postActions.size()])); preSubmitMethods.put(clazz, preSubmits.toArray(new Method[preSubmits.size()])); preActionMethods.put(clazz, preActions.toArray(new Method[preActions.size()])); unescapeMethods.put(clazz, unescapes.toArray(new String[unescapes.size()])); } } Field[] getAuthFields(Class<?> clazz) { Field[] fields = authFields.get(clazz); return fields != null ? fields : new Field[] {}; } boolean hasPrimitiveAuthFields(Class<?> clazz) { Field[] fields = getAuthFields(clazz); for (int i = 0; i < fields.length; i++) { if (Primitives.isPrimitive(fields[i].getGenericType())) { return true; } } return false; } void setAuthFields(Class<?> clazz) { if (!authFields.containsKey(clazz)) { List<Field> fields = new ArrayList<>(); for (Field field : getBeanFields(clazz)) { if (!field.isAnnotationPresent(AuthField.class)) { continue; } fields.add(field); } authFields.put(clazz, fields.toArray(new Field[fields.size()])); } } Field[] getAuthAccess(Class<?> clazz) { Field[] fields = authAccess.get(clazz); return fields != null ? fields : new Field[] {}; } void setAuthAccess(Class<?> clazz) { if (!authAccess.containsKey(clazz)) { List<Field> fields = new ArrayList<>(); for (Field field : getBeanFields(clazz)) { if (!field.isAnnotationPresent(AuthAccess.class)) { continue; } fields.add(field); } authAccess.put(clazz, fields.toArray(new Field[fields.size()])); } } Method[] getAuthMethods(Class<?> clazz) { Method[] methods = authMethods.get(clazz); return methods != null ? methods : new Method[] {}; } void setAuthMethods(Class<?> clazz) { if (!authMethods.containsKey(clazz)) { List<Method> methods = new ArrayList<>(); for (Method method : getBeanMethods(clazz)) { if (!method.isAnnotationPresent(AuthMethod.class)) { continue; } Class<?> returnType = method.getReturnType(); if (Boolean.class == returnType || boolean.class == returnType) { methods.add(method); } } authMethods.put(clazz, methods.toArray(new Method[methods.size()])); } } List<String> cleanPaths(String[] urlPaths) { List<String> cleanPaths = new ArrayList<>(); if (urlPaths.length == 1 && "/*".equals(urlPaths[0].trim())) { urlPaths = CONFIG.getContent().getUrlPatternsArray(); } for (String urlPattern : urlPaths) { String path = getCleanPath(urlPattern); cleanPaths.add(matchUrlPattern(path)); } return cleanPaths; } String getCleanPath(String path) { Matcher matcher = PATH_BEAN_ALL_PATTERN.matcher(path); if (matcher.find()) { path = matcher.group(1); } return path; } String matchUrlPattern(String path) { for (UrlPattern urlPattern : CONFIG.getContent().getUrlPatterns()) { if (urlPattern.getUrl().equals(path)) { return urlPattern.getUrl(); } } for (UrlPattern urlPattern : CONFIG.getContent().getUrlPatterns()) { if (urlPattern.getUrl().contains("/*")) { String url = urlPattern.getUrl().replace("/*", ""); if (path.equals(url)) { return url; } } } return path; } Field[] getExposeVarFields(Class<?> clazz) { Field[] fields = exposeVarFields.get(clazz); return fields != null ? fields : new Field[] {}; } List<Class<?>> getExposeVarByPath(String path) { List<Class<?>> classes = exposeVarPaths.get(path); return classes != null ? classes : Collections.EMPTY_LIST; } Map<String, Object> getExposeVarMapping(Field field) { Locale locale = WebContext.getLocale(); String language = locale != null ? locale.getLanguage() : Locale.getDefault().getLanguage(); Map<String, WebText.WebTextSet> localeTexts = varMappingFields.get(field); if (localeTexts != null) { if (localeTexts.containsKey(language)) { return localeTexts.get(language).getValues(); } return localeTexts.get(Locale.getDefault().getLanguage()).getValues(); } return null; } private void setExposeVarMapping(Field field, VarMapping varMapping) { try { List<String> properties = IOUtils.readLines(getClass().getClassLoader().getResourceAsStream("/"), ENCODING); for (String propertiesName : properties) { if (propertiesName.startsWith(varMapping.i18n()) && propertiesName.endsWith(".properties")) { String language = CONFIG.getContent().getDefaultLanguage(); Matcher matcher = PROPERTIES_NAME_PATTERN.matcher(propertiesName); if (matcher.find()) { language = matcher.group(1); } setExposeVarMapping(field, varMapping, language); } } setExposeVarMapping(field, varMapping, Locale.getDefault().getLanguage()); } catch (IOException ex) { LOGGER.log(Level.SEVERE, "Error reading list of resource properties", ex); } } private void setExposeVarMapping(Field field, VarMapping varMapping, String language) { WebText.WebTextSet webTextSet = WebText.getStrings(varMapping.i18n(), varMapping.prefix(), language); if (webTextSet != null) { Map<String, WebText.WebTextSet> localeTexts = varMappingFields.get(field); if (localeTexts == null) { varMappingFields.put(field, localeTexts = new ConcurrentHashMap<>()); } localeTexts.put(webTextSet.getLanguage(), webTextSet); } } }