001package co.codewizards.cloudstore.ls.core.invoke; 002 003import static co.codewizards.cloudstore.core.util.Util.*; 004import static java.util.Objects.*; 005 006import java.util.Collections; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.LinkedHashSet; 010import java.util.Map; 011import java.util.Set; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016import co.codewizards.cloudstore.core.Uid; 017 018public class ClassManager { 019 private static final Logger logger = LoggerFactory.getLogger(ClassManager.class); 020 021 private final Uid clientId; 022 023 // classes on the side of the real objects (not the proxies!) 024 private Map<Integer, Class<?>> classId2Class = new HashMap<>(); 025 private Map<Class<?>, Integer> class2ClassId = new HashMap<>(); 026 private Set<Integer> classIdsKnownByRemoteSide = new HashSet<>(); 027 028 // class-info on the side of the real objects (not the proxies!) 029 // the ClassInfoMap is used on the proxies' side. 030 private Map<Integer, ClassInfo> classId2ClassInfo = new HashMap<Integer, ClassInfo>(); 031 032 private static final Map<String, Class<?>> primitiveClassName2Class; 033 static { 034 final Class<?>[] primitives = { 035 byte.class, 036 short.class, 037 int.class, 038 long.class, 039 float.class, 040 double.class, 041 char.class, 042 boolean.class 043 }; 044 045 final Map<String, Class<?>> m = new HashMap<>(primitives.length); 046 047 for (Class<?> clazz : primitives) 048 m.put(clazz.getName(), clazz); 049 050 primitiveClassName2Class = Collections.unmodifiableMap(m); 051 } 052// private static Set<Class<?>> primitiveClasses = Collections.unmodifiableSet(new HashSet<Class<?>>(primitiveClassName2Class.values())); 053// 054// public static Set<Class<?>> getPrimitiveClasses() { 055// return primitiveClasses; 056// } 057 058 public ClassManager(final Uid clientId) { 059 this.clientId = requireNonNull(clientId, "clientId"); 060 logger.debug("[{}].<init>: Created ClassManager.", clientId); 061 } 062 063 private int nextClassId; 064 065 public synchronized int getClassIdOrFail(final Class<?> clazz) { 066 final int classId = getClassId(clazz); 067 if (classId < 0) 068 throw new IllegalArgumentException(String.format("ClassManager[%s] does not have classId for this class: %s", 069 clientId, clazz.getName())); 070 071 return classId; 072 } 073 074 public synchronized int getClassId(final Class<?> clazz) { 075 requireNonNull(clazz, "clazz"); 076 Integer classId = class2ClassId.get(clazz); 077 if (classId == null) 078 return -1; 079 else 080 return classId; 081 } 082 083 public synchronized int getClassIdOrCreate(final Class<?> clazz) { 084 requireNonNull(clazz, "clazz"); 085 Integer classId = class2ClassId.get(clazz); 086 if (classId == null) { 087 classId = nextClassId(); 088 logger.debug("[{}].getClassIdOrCreate: Assigned classId={} to {}.", clientId, classId, clazz.getName()); 089 class2ClassId.put(clazz, classId); 090 classId2Class.put(classId, clazz); 091 } 092 return classId; 093 } 094 095 public synchronized Class<?> getClassOrFail(final int classId) { 096 final Class<?> clazz = getClass(classId); 097 if (clazz == null) 098 throw new IllegalArgumentException(String.format("ClassManager[%s] does not have class for this classId: %s", 099 clientId, classId)); 100 101 return clazz; 102 } 103 104 public synchronized Class<?> getClass(final int classId) { 105 final Class<?> clazz = classId2Class.get(classId); 106 return clazz; 107 } 108 109 public synchronized boolean isClassIdKnownByRemoteSide(int classId) { 110 final boolean result = classIdsKnownByRemoteSide.contains(classId); 111 return result; 112 } 113 114 public synchronized void setClassIdKnownByRemoteSide(int classId) { 115 if (classIdsKnownByRemoteSide.add(classId)) 116 logger.debug("[{}].setClassIdKnownByRemoteSide: classId={}", clientId, classId); 117 } 118 119 public synchronized ClassInfo getClassInfo(int classId) { 120 ClassInfo classInfo = classId2ClassInfo.get(classId); 121 if (classInfo == null) { 122 final Class<?> clazz = getClass(classId); 123 124 if (clazz == null) 125 return null; 126 127 final Set<String> interfaceNames = getInterfaceNames(clazz); 128 classInfo = new ClassInfo(classId, clazz.getName(), interfaceNames, isEqualsOverridden(clazz)); 129 classId2ClassInfo.put(classId, classInfo); 130 } 131 return classInfo; 132 } 133 134 private boolean isEqualsOverridden(final Class<?> clazz) { 135 Class<?> c = clazz; 136 while (c != Object.class) { 137 try { 138 c.getDeclaredMethod("equals", Object.class); 139 return true; 140 } catch (NoSuchMethodException | SecurityException e) { 141 doNothing(); 142 } 143 c = c.getSuperclass(); 144 } 145 return false; 146 } 147 148 protected synchronized int nextClassId() { 149 return nextClassId++; 150 } 151 152 protected Set<String> getInterfaceNames(Class<?> clazz) { 153 requireNonNull(clazz, "clazz"); 154 final Set<String> interfaceNames = new LinkedHashSet<>(); 155 populateInterfaceNames(interfaceNames, clazz); 156 return interfaceNames; 157 } 158 159 private void populateInterfaceNames(Set<String> interfaceNames, Class<?> clazz) { 160 if (clazz.isInterface()) 161 interfaceNames.add(clazz.getName()); 162 163 for (Class<?> iface : clazz.getInterfaces()) 164 populateInterfaceNames(interfaceNames, iface); 165 166 final Class<?> superclass = clazz.getSuperclass(); 167 if (superclass != Object.class && superclass != null) 168 populateInterfaceNames(interfaceNames, superclass); 169 } 170 171 public Class<?>[] getClassesOrFail(final String[] classNames) { 172 requireNonNull(classNames, "classNames"); 173 final Class<?>[] classes = new Class<?>[classNames.length]; 174 175 for (int i = 0; i < classNames.length; i++) 176 classes[i] = getClassOrFail(classNames[i]); 177 178 return classes; 179 } 180 181 public Class<?> getClassOrFail(final String className) { 182 requireNonNull(className, "className"); 183 184 Class<?> clazz = primitiveClassName2Class.get(className); 185 if (clazz != null) 186 return clazz; 187 188 // TODO maybe use context-class-loader, too and other loaders (which?)? 189 try { 190 clazz = Class.forName(className); 191 } catch (ClassNotFoundException e) { 192 throw new IllegalArgumentException(e); 193 } 194 return clazz; 195 } 196}