Java tutorial
/** * Copyright (c) 2012 - Reinaldo de Carvalho <reinaldoc@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * */ package br.gov.frameworkdemoiselle.ldap.internal; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.ArrayUtils; import org.slf4j.Logger; import br.gov.frameworkdemoiselle.annotation.Ignore; import br.gov.frameworkdemoiselle.annotation.Name; import br.gov.frameworkdemoiselle.internal.producer.LoggerProducer; import br.gov.frameworkdemoiselle.ldap.annotation.DistinguishedName; import br.gov.frameworkdemoiselle.ldap.annotation.Id; import br.gov.frameworkdemoiselle.ldap.annotation.LDAPEntry; import br.gov.frameworkdemoiselle.ldap.annotation.ParentDN; import br.gov.frameworkdemoiselle.ldap.annotation.RemoveOnMerge; import br.gov.frameworkdemoiselle.ldap.core.EntryManager; import br.gov.frameworkdemoiselle.ldap.exception.EntryException; import br.gov.frameworkdemoiselle.util.Beans; import br.gov.frameworkdemoiselle.util.Reflections; import br.gov.frameworkdemoiselle.util.contrib.Strings; public class ClazzUtils { private static Logger logger = LoggerProducer.create(ClazzUtils.class); public static final boolean EntryObjectListCascade = false; public static final boolean EntryObjectCascade = true; /** * Get Distinguished Name value of a @LDAPEntry annotated object. The * Distinguished Name will be get by annotation @DistinguishedName if not * present or is null, will be get from value of @ParentDN annotation * concatenate with @Id * * @param entry * @return Distinguished Name */ public static String getDistinguishedName(Object entry) { isAnnotationPresent(entry.getClass(), LDAPEntry.class, true); Field idField = null; Field parentDnField = null; for (Field field : getSuperClassesFields(entry.getClass())) { if (field.isAnnotationPresent(DistinguishedName.class)) { Object dn = Reflections.getFieldValue(field, entry); if (dn != null) return (String) dn; } else if (field.isAnnotationPresent(Id.class)) idField = field; else if (field.isAnnotationPresent(ParentDN.class)) parentDnField = field; } if (parentDnField != null && idField != null) return getFieldName(idField) + "=" + (String) Reflections.getFieldValue(idField, entry) + "," + (String) Reflections.getFieldValue(parentDnField, entry); throw new EntryException( "Field with @DistinguishedName or fields with @ParentDN and @Id not found on class " + entry.getClass().getSimpleName()); } /** * Convert @LDAPEntry annotated object to Map(String, Object). The valid * types for Object Map value is String, String[] or byte[] * * @param entry * @return Entry Map */ public static Map<String, Object> getObjectMap(Object entry) { isAnnotationPresent(entry.getClass(), LDAPEntry.class, true); Map<String, Object> map = new HashMap<String, Object>(); Field[] fields = getSuperClassesFields(entry.getClass()); for (Field field : fields) { if (field.isAnnotationPresent(DistinguishedName.class) || field.isAnnotationPresent(Ignore.class) || field.isAnnotationPresent(ParentDN.class)) continue; // Ignore override attributes if (map.containsKey(getFieldName(field))) continue; Object value = Reflections.getFieldValue(field, entry); if (value == null) continue; if (field.isAnnotationPresent(RemoveOnMerge.class)) { map.put("@RemoveOnMerge", value); Reflections.setFieldValue(field, entry, null); continue; } map.put(getFieldName(field), getObjectAsSupportedType(value)); } return map; } /** * Process field value of @LDAPEntry annotated class and return a LDAP * supported object of String, String[] or byte[] types * * @return A value to be commited to LDAP as String, String[] or byte[] */ public static Object getObjectAsSupportedType(Object obj) { if (obj instanceof String || obj instanceof String[] || obj instanceof byte[]) return obj; else if (isAnnotationPresent(obj.getClass(), LDAPEntry.class, false)) return getAnnotatedValue(obj, Id.class, true).toString(); else if (obj.getClass().isArray()) return convertToStringArrayFromArray(obj); else if (isCollection(obj.getClass())) return convertToStringArrayFromCollection(obj); else return obj.toString(); } public static boolean isCollection(Class<?> clazz) { for (Class<?> claz : clazz.getInterfaces()) if (claz == Collection.class) return true; return false; } public static String[] convertToStringArrayFromArray(Object array) { return new String[] { "Future implementation" }; } public static String[] convertToStringArrayFromCollection(Object array) { return new String[] { "Future implementation" }; } /** * Convert a Map(dn, Map(attr, Object)) to @LDAPEntry annotated object * * @param entryMap * @param clazz * <T> * @return List<T> */ public static <T> List<T> getEntryObjectList(Map<String, Map<String, Object>> entryMap, Class<T> clazz) { return getEntryObjectList(entryMap, clazz, EntryObjectListCascade); } private static <T> List<T> getEntryObjectList(Map<String, Map<String, Object>> entryMap, Class<T> clazz, boolean cascade) { if (entryMap == null) return null; isAnnotationPresent(clazz, LDAPEntry.class, true); List<T> resultList = new ArrayList<T>(); for (Map.Entry<String, Map<String, Object>> mapEntry : entryMap.entrySet()) resultList.add(getEntryObject(mapEntry.getKey(), mapEntry.getValue(), clazz, cascade)); return resultList; } public static <T> T getEntryObject(String dn, Map<String, Object> map, Class<T> clazz) { return getEntryObject(dn, map, clazz, EntryObjectCascade); } private static <T> T getEntryObject(String dn, Map<String, Object> map, Class<T> clazz, boolean cascade) { T entry = Beans.getReference(clazz); Field[] fields = getSuperClassesFields(entry.getClass()); for (Field field : fields) { if (field.isAnnotationPresent(Ignore.class) || field.isAnnotationPresent(RemoveOnMerge.class)) continue; if (field.isAnnotationPresent(ParentDN.class)) { setFieldValue(field, entry, new String[] { getParentDN(dn) }); continue; } if (field.isAnnotationPresent(DistinguishedName.class)) { setFieldValue(field, entry, new String[] { dn }); continue; } String fieldName = getFieldName(field); if (map.containsKey(fieldName)) setFieldValue(field, entry, map.get(fieldName), cascade); } return entry; } public static String getParentDN(String dn) { return Strings.substringBeforeLast(dn, ","); } /** * Feeling a Class for a String Representation of Search Filters (RFC 4515) * and throw EntryException when can't find a @LDAPEntry object * * @param searchFilter * @return Class */ public static Class<?> getRequiredClassForSearchFilter(String searchFilter, List<String> packageNames) { Class<?> clazz = getClassForSearchFilter(searchFilter, packageNames); if (clazz == null) throw new EntryException("Can't find a @LDAPEntry object for search filter " + searchFilter + ". Please, add all packages with @LDAPEntry classes to property list EntryManager.ldapentry.packages at demoiselle.properties file"); return clazz; } /** * Feeling a Class or return null for a String Representation of Search * Filters (RFC 4515) * * @param searchFilter * @return Class */ public static Class<?> getClassForSearchFilter(String searchFilter, List<String> packageNames) { if (searchFilter == null || packageNames == null || packageNames.size() == 0) return null; String patternStr = "objectClass=(.*?)(\\)|$)"; Matcher matcher = Pattern.compile(patternStr).matcher(searchFilter); boolean matchFound = matcher.find(); if (matchFound && matcher.groupCount() > 1) for (String packageName : packageNames) try { return Class.forName(packageName + "." + Character.toUpperCase(matcher.group(1).charAt(0)) + matcher.group(1).substring(1)); } catch (Exception e) { } return null; } /** * Build a super classes List(Class(? extends Object)) * * @return List of Super Classes */ public static List<Class<? extends Object>> getSuperClasses(Class<?> entryClass) { List<Class<? extends Object>> list = new ArrayList<Class<? extends Object>>(); Class<? extends Object> superClazz = entryClass.getSuperclass(); while (superClazz != null) { list.add(superClazz); superClazz = superClazz.getSuperclass(); } return list; } /** * Build a array of super classes fields * * @return Array of Super Classes Fields */ public static Field[] getSuperClassesFields(Class<?> entryClass) { Field[] fieldArray = entryClass.getDeclaredFields(); Class<?> superClazz = entryClass.getSuperclass(); while (superClazz != null && !"java.lang.Object".equals(superClazz.getName())) { fieldArray = (Field[]) ArrayUtils.addAll(fieldArray, superClazz.getDeclaredFields()); superClazz = superClazz.getSuperclass(); } return fieldArray; } /** * Get annotated value * * @param entry */ public static Object getAnnotatedValue(Object entry, Class<? extends Annotation> aclazz, boolean required) { Field field = getFieldAnnotatedAs(entry.getClass(), aclazz, required); if (field != null) return Reflections.getFieldValue(field, entry); return null; } public static Field getFieldAnnotatedAs(Class<?> clazz, Class<? extends Annotation> aclazz, boolean required) { for (Field field : getSuperClassesFields(clazz)) if (field.isAnnotationPresent(aclazz)) return field; if (required) throw new EntryException( "Field with @" + aclazz.getSimpleName() + " not found on class " + clazz.getSimpleName()); else return null; } /** * Verify if a POJO have a annotated field, throw EntryException if * annotation is required and wasn't found. * * @param clazz * a POJO class to be verified * @param aclazz * a Annotation class to be searched on POJO fields * @param required * especify if should throw a EntryExpcetion when annotation * was't found. * */ public static boolean hasFieldAnnotatedAs(Class<?> clazz, Class<? extends Annotation> aclazz, boolean required) { Field field = getFieldAnnotatedAs(clazz, aclazz, required); if (field == null) return false; return true; } /** * Value may be 'String', 'String[]' or Object Annotated with @LDAPEntry * * @param field * @param entry * @param value */ public static void setFieldValue(Field field, Object entry, Object value) { setFieldValue(field, entry, value, false); } public static void setFieldValue(Field field, Object entry, Object value, boolean cascade) { Reflections.setFieldValue(field, entry, getValueAsFieldType(field, value, cascade)); } public static Object getValueAsFieldType(Field field, Object values, boolean cascade) { if (values instanceof String[]) { String[] valueArray = (String[]) values; if (valueArray == null || valueArray.length == 0) return null; if (field.getType().isAssignableFrom(String.class)) return valueArray[0]; if (field.getType().isArray()) if (field.getType().getComponentType().isAssignableFrom(String.class)) return valueArray; if (field.getType().isPrimitive()) { if (field.getType().isAssignableFrom(int.class)) return Integer.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(long.class)) return Long.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(double.class)) return Double.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(float.class)) return Float.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(short.class)) return Short.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(byte.class)) return Byte.valueOf(valueArray[0]); } if (isAnnotationPresent(field.getType(), LDAPEntry.class, false)) return getMappedEntryObject(field.getType(), valueArray, cascade); if (field.getType().isAssignableFrom(ArrayList.class)) if (String.class.isAssignableFrom(Reflections.getGenericTypeArgument(field.getType(), 0))) return new ArrayList<String>(Arrays.asList(valueArray)); if (field.getType().isAssignableFrom(Integer.class)) return Integer.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(Long.class)) return Long.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(Double.class)) return Double.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(Float.class)) return Float.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(Short.class)) return Short.valueOf(valueArray[0]); if (field.getType().isAssignableFrom(Byte.class)) return Byte.valueOf(valueArray[0]); logger.error("Handling not implemented for field " + field.getName() + " with type " + field.getType().getSimpleName()); } else if (values instanceof byte[][]) { if (field.getType().isAssignableFrom(byte[][].class)) return values; if (field.getType().isAssignableFrom(byte[].class)) return ((byte[][]) values)[0]; logger.error("Binary data from LDAP can't be set in " + field.getName() + " with type " + field.getType().getSimpleName()); } logger.error("Object value should be String[] or byte[][]. The value type " + values.getClass() + " can't be set in " + field.getName() + " with type " + field.getType().getSimpleName()); return null; } public static Object getMappedEntryObject(Class<?> clazz, String[] value, boolean cascade) { if (cascade) { EntryManager em = Beans.getReference(EntryManager.class); return em.find(clazz, value[0]); } else { Object entry = Beans.getReference(clazz); setAnnotatedFieldValueAs(entry, Id.class, value); return entry; } } /** * Set annotated value * * @param entry */ public static void setAnnotatedFieldValueAs(Object entry, Class<? extends Annotation> clazz, Object value) { Field field = getFieldAnnotatedAs(entry.getClass(), clazz, false); setFieldValue(field, entry, value); } /** * Get a field name (object attribute name) or the value of @Name if * annotation is present. In other words, if @Name is present returns * field.getAnnotation(Name.class).value(), otherwise field.getName(); * * @param field * @return @Name annotation value or object attribute name; */ public static String getFieldName(Field field) { if (field.isAnnotationPresent(Name.class)) { String name = field.getAnnotation(Name.class).value(); if (name == null || name.trim().isEmpty()) throw new EntryException("Annotation @Name must have a value"); return name; } else return field.getName(); } /** * Verify if a class or yours super classes have annotation, throw * EntryException if annotation is required and wasn't found. * * @param clazz * a class to be verified * @param aclazz * a Annotation class to be searched * @param required * define if should throw a EntryExpcetion when annotation was't * found. * */ public static boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> aclazz, boolean required) { for (Class<?> claz : getSuperClasses(clazz)) if (claz.isAnnotationPresent(aclazz)) return true; if (required) throw new EntryException("Entry " + clazz.getSimpleName() + " doesn't have @" + aclazz.getName()); return false; } }