001package co.codewizards.cloudstore.ls.client; 002 003import static co.codewizards.cloudstore.core.util.Util.*; 004import static java.util.Objects.*; 005 006import java.io.Closeable; 007import java.io.Serializable; 008import java.lang.reflect.Array; 009import java.lang.reflect.Proxy; 010import java.util.ArrayList; 011import java.util.List; 012import java.util.Set; 013 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017import co.codewizards.cloudstore.core.Uid; 018import co.codewizards.cloudstore.core.util.ExceptionUtil; 019import co.codewizards.cloudstore.core.util.ReflectionUtil; 020import co.codewizards.cloudstore.ls.client.handler.InverseServiceRequestHandlerThread; 021import co.codewizards.cloudstore.ls.core.LsConfig; 022import co.codewizards.cloudstore.ls.core.invoke.ClassInfo; 023import co.codewizards.cloudstore.ls.core.invoke.ClassInfoMap; 024import co.codewizards.cloudstore.ls.core.invoke.ClassManager; 025import co.codewizards.cloudstore.ls.core.invoke.DelayedMethodInvocationResponse; 026import co.codewizards.cloudstore.ls.core.invoke.IncDecRefCountQueue; 027import co.codewizards.cloudstore.ls.core.invoke.Invoker; 028import co.codewizards.cloudstore.ls.core.invoke.MethodInvocationRequest; 029import co.codewizards.cloudstore.ls.core.invoke.MethodInvocationResponse; 030import co.codewizards.cloudstore.ls.core.invoke.ObjectManager; 031import co.codewizards.cloudstore.ls.core.invoke.ObjectRef; 032import co.codewizards.cloudstore.ls.core.invoke.RemoteObjectProxy; 033import co.codewizards.cloudstore.ls.core.invoke.RemoteObjectProxyFactory; 034import co.codewizards.cloudstore.ls.core.invoke.RemoteObjectProxyInvocationHandler; 035import co.codewizards.cloudstore.ls.core.provider.JavaNativeWithObjectRefMessageBodyReader; 036import co.codewizards.cloudstore.ls.core.provider.JavaNativeWithObjectRefMessageBodyWriter; 037import co.codewizards.cloudstore.ls.rest.client.LocalServerRestClient; 038import co.codewizards.cloudstore.ls.rest.client.request.GetDelayedMethodInvocationResponse; 039import co.codewizards.cloudstore.ls.rest.client.request.InvokeMethod; 040 041/** 042 * @author Marco หงุ่ยตระกูล-Schulze - marco at codewizards dot co 043 */ 044public class LocalServerClient implements Invoker, Closeable { 045 046 private static final Logger logger = LoggerFactory.getLogger(LocalServerClient.class); 047 048 private volatile InverseServiceRequestHandlerThread inverseServiceRequestHandlerThread; 049 050 private LocalServerRestClient localServerRestClient; 051 private final ObjectManager objectManager = ObjectManager.getInstance(new Uid()); // needed for inverse references as used by listeners! 052 { 053 objectManager.setNeverEvict(true); 054 } 055 private final ClassInfoMap classInfoMap = new ClassInfoMap(); 056 057 private final IncDecRefCountQueue incDecRefCountQueue = new IncDecRefCountQueue(this); 058 059 @Override 060 public ClassInfoMap getClassInfoMap() { 061 return classInfoMap; 062 } 063 064 private static final class Holder { 065 public static final LocalServerClient instance = new LocalServerClient(); 066 } 067 068 public static LocalServerClient getInstance() { 069 return Holder.instance; 070 } 071 072 public final synchronized LocalServerRestClient getLocalServerRestClient() { 073 if (localServerRestClient == null) { 074 localServerRestClient = _getLocalServerRestClient(); 075 076 final ObjectRefConverterFactoryImpl objectRefConverterFactory = new ObjectRefConverterFactoryImpl(this); 077 localServerRestClient.registerRestComponent(new JavaNativeWithObjectRefMessageBodyReader(objectRefConverterFactory)); 078 localServerRestClient.registerRestComponent(new JavaNativeWithObjectRefMessageBodyWriter(objectRefConverterFactory)); 079 } 080 return localServerRestClient; 081 } 082 083 protected LocalServerRestClient _getLocalServerRestClient() { 084 return LocalServerRestClient.getInstance(); 085 } 086 087 protected LocalServerClient() { 088 if (LsConfig.isLocalServerEnabled()) { 089 inverseServiceRequestHandlerThread = new InverseServiceRequestHandlerThread(this); 090 inverseServiceRequestHandlerThread.start(); 091 } 092 } 093 094 @Override 095 public ObjectManager getObjectManager() { 096 return objectManager; 097 } 098 099 @Override 100 public <T> T invokeStatic(final Class<?> clazz, final String methodName, final Object ... arguments) { 101 requireNonNull(clazz, "clazz"); 102 requireNonNull(methodName, "methodName"); 103 if (! LsConfig.isLocalServerEnabled()) 104 return ReflectionUtil.invokeStatic(clazz, methodName, arguments); 105 106 return invokeStatic(clazz.getName(), methodName, (String[]) null, arguments); 107 } 108 109 @Override 110 public <T> T invokeStatic(final String className, final String methodName, final Object ... arguments) { 111 requireNonNull(className, "className"); 112 requireNonNull(methodName, "methodName"); 113 if (! LsConfig.isLocalServerEnabled()) 114 return ReflectionUtil.invokeStatic(getClassOrFail(className), methodName, arguments); 115 116 return invokeStatic(className, methodName, (String[]) null, arguments); 117 } 118 119 @Override 120 public <T> T invokeStatic(final Class<?> clazz, final String methodName, final Class<?>[] argumentTypes, final Object ... arguments) { 121 requireNonNull(clazz, "clazz"); 122 requireNonNull(methodName, "methodName"); 123 if (! LsConfig.isLocalServerEnabled()) 124 return ReflectionUtil.invokeStatic(clazz, methodName, argumentTypes, arguments); 125 126 return invokeStatic(clazz.getName(), methodName, toClassNames(argumentTypes), arguments); 127 } 128 129 @Override 130 public <T> T invokeStatic(final String className, final String methodName, final String[] argumentTypeNames, final Object ... arguments) { 131 requireNonNull(className, "className"); 132 requireNonNull(methodName, "methodName"); 133 if (! LsConfig.isLocalServerEnabled()) 134 return ReflectionUtil.invokeStatic(getClassOrFail(className), methodName, getClassesOrFail(argumentTypeNames), arguments); 135 136 final MethodInvocationRequest methodInvocationRequest = MethodInvocationRequest.forStaticInvocation( 137 className, methodName, argumentTypeNames, arguments); 138 139 return invoke(methodInvocationRequest); 140 } 141 142 @Override 143 public <T> T invokeConstructor(final Class<T> clazz, final Object ... arguments) { 144 requireNonNull(clazz, "clazz"); 145 if (! LsConfig.isLocalServerEnabled()) 146 return ReflectionUtil.invokeConstructor(clazz, arguments); 147 148 return invokeConstructor(clazz.getName(), (String[]) null, arguments); 149 } 150 151 @Override 152 public <T> T invokeConstructor(final String className, final Object ... arguments) { 153 requireNonNull(className, "className"); 154 if (! LsConfig.isLocalServerEnabled()) 155 return cast(ReflectionUtil.invokeConstructor(getClassOrFail(className), arguments)); 156 157 return invokeConstructor(className, (String[]) null, arguments); 158 } 159 160 @Override 161 public <T> T invokeConstructor(final Class<T> clazz, final Class<?>[] argumentTypes, final Object ... arguments) { 162 requireNonNull(clazz, "clazz"); 163 if (! LsConfig.isLocalServerEnabled()) 164 return ReflectionUtil.invokeConstructor(clazz, argumentTypes, arguments); 165 166 return invokeConstructor(clazz.getName(), toClassNames(argumentTypes), arguments); 167 } 168 169 @Override 170 public <T> T invokeConstructor(final String className, final String[] argumentTypeNames, final Object ... arguments) { 171 requireNonNull(className, "className"); 172 if (! LsConfig.isLocalServerEnabled()) 173 return cast(ReflectionUtil.invokeConstructor(getClassOrFail(className), getClassesOrFail(argumentTypeNames), arguments)); 174 175 final MethodInvocationRequest methodInvocationRequest = MethodInvocationRequest.forConstructorInvocation( 176 className, argumentTypeNames, arguments); 177 178 return invoke(methodInvocationRequest); 179 } 180 181 @Override 182 public <T> T invoke(final Object object, final String methodName, final Object ... arguments) { 183 requireNonNull(object, "object"); 184 requireNonNull(methodName, "methodName"); 185 if (! LsConfig.isLocalServerEnabled()) 186 return cast(ReflectionUtil.invoke(object, methodName, arguments)); 187 188 if (!(object instanceof RemoteObjectProxy) && !(object instanceof Serializable)) 189 throw new IllegalArgumentException("object is neither an instance of RemoteObjectProxy nor Serializable!"); 190 191 return invoke(object, methodName, (Class<?>[]) null, arguments); 192 } 193 194 @Override 195 public <T> T invoke(final Object object, final String methodName, final Class<?>[] argumentTypes, final Object... arguments) { 196 requireNonNull(object, "object"); 197 requireNonNull(methodName, "methodName"); 198 if (! LsConfig.isLocalServerEnabled()) 199 return cast(ReflectionUtil.invoke(object, methodName, argumentTypes, arguments)); 200 201 return invoke(object, methodName, toClassNames(argumentTypes), arguments); 202 } 203 204 @Override 205 public <T> T invoke(final Object object, final String methodName, final String[] argumentTypeNames, final Object... arguments) { 206 requireNonNull(object, "object"); 207 requireNonNull(methodName, "methodName"); 208 if (! LsConfig.isLocalServerEnabled()) 209 return cast(ReflectionUtil.invoke(object, methodName, getClassesOrFail(argumentTypeNames), arguments)); 210 211 final MethodInvocationRequest methodInvocationRequest = MethodInvocationRequest.forObjectInvocation( 212 object, methodName, argumentTypeNames, arguments); 213 214 return invoke(methodInvocationRequest); 215 } 216 217 private Class<?>[] getClassesOrFail(final String[] classNames) { 218 requireNonNull(classNames, "classNames"); 219 final Class<?>[] result = new Class<?>[classNames.length]; 220 for (int i = 0; i < classNames.length; i++) 221 result[i] = getClassOrFail(classNames[i]); 222 223 return result; 224 } 225 226 private Class<?> getClassOrFail(final String className) { 227 requireNonNull(className, "className"); 228 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 229 if (loader == null) 230 loader = getClass().getClassLoader(); 231 232 try { 233 return Class.forName(className, true, loader); 234 } catch (ClassNotFoundException e) { 235 throw new RuntimeException(e); 236 } 237 } 238 239 private String[] toClassNames(Class<?> ... classes) { 240 final String[] classNames; 241 if (classes == null) 242 classNames = null; 243 else { 244 classNames = new String[classes.length]; 245 for (int i = 0; i < classes.length; i++) 246 classNames[i] = classes[i].getName(); 247 } 248 return classNames; 249 } 250 251 private <T> T invoke(final MethodInvocationRequest methodInvocationRequest) { 252 requireNonNull(methodInvocationRequest, "methodInvocationRequest"); 253 254 MethodInvocationResponse methodInvocationResponse = getLocalServerRestClient().execute( 255 new InvokeMethod(methodInvocationRequest)); 256 257 while (methodInvocationResponse instanceof DelayedMethodInvocationResponse) { 258 final DelayedMethodInvocationResponse dmir = (DelayedMethodInvocationResponse) methodInvocationResponse; 259 final Uid delayedResponseId = dmir.getDelayedResponseId(); 260 261 methodInvocationResponse = getLocalServerRestClient().execute( 262 new GetDelayedMethodInvocationResponse(delayedResponseId)); 263 } 264 265 final Object result = methodInvocationResponse.getResult(); 266 if (methodInvocationResponse.getWritableArguments() != null) 267 copyWritableArgumentsBack(methodInvocationRequest.getArguments(), methodInvocationResponse.getWritableArguments()); 268 269 return cast(result); 270 } 271 272 private void copyWritableArgumentsBack(final Object[] requestArguments, final Object[] responseArguments) { 273 requireNonNull(requestArguments, "requestArguments"); 274 requireNonNull(responseArguments, "responseArguments"); 275 276 for (int i = 0; i < responseArguments.length; ++i) { 277 final Object responseArgument = responseArguments[i]; 278 if (responseArgument != null) 279 copyWritableArgumentBack(requestArguments[i], responseArgument); 280 } 281 } 282 283 private void copyWritableArgumentBack(final Object requestArgument, final Object responseArgument) { 284 requireNonNull(requestArgument, "requestArgument"); 285 requireNonNull(responseArgument, "responseArgument"); 286 287 if (requestArgument.getClass().isArray()) { 288 final int length = Array.getLength(requestArgument); 289 for (int i = 0; i < length; ++i) { 290 final Object value = Array.get(responseArgument, i); 291 Array.set(requestArgument, i, value); 292 } 293 } 294 else 295 throw new UnsupportedOperationException("No idea how to copy this back! requestArgument=" + requestArgument); 296 } 297 298 private RemoteObjectProxy _createRemoteObjectProxy(final ObjectRef objectRef, final Class<?>[] interfaces) { 299 final ClassLoader classLoader = this.getClass().getClassLoader(); 300 return (RemoteObjectProxy) Proxy.newProxyInstance(classLoader, interfaces, 301 new RemoteObjectProxyInvocationHandler(this, objectRef)); 302 } 303 304 private Class<?>[] getInterfaces(final ObjectRef objectRef) { 305 ClassInfo classInfo = classInfoMap.getClassInfo(objectRef.getClassId()); 306 if (classInfo == null) { 307 classInfo = objectRef.getClassInfo(); 308 if (classInfo == null) 309 throw new IllegalStateException("There is no ClassInfo in the ClassInfoMap and neither in the ObjectRef! " + objectRef); 310 311 classInfoMap.putClassInfo(classInfo); 312 objectRef.setClassInfo(null); 313 } 314 315 final ClassManager classManager = objectManager.getClassManager(); 316 final Set<String> interfaceNames = classInfo.getInterfaceNames(); 317 final List<Class<?>> interfaces = new ArrayList<>(interfaceNames.size() + 1); 318 for (final String interfaceName : interfaceNames) { 319 Class<?> iface = null; 320 try { 321 iface = classManager.getClassOrFail(interfaceName); 322 } catch (RuntimeException x) { 323 if (ExceptionUtil.getCause(x, ClassNotFoundException.class) == null) 324 throw x; 325 } 326 if (iface != null) 327 interfaces.add(iface); 328 } 329 interfaces.add(RemoteObjectProxy.class); 330 return interfaces.toArray(new Class<?>[interfaces.size()]); 331 } 332 333 @Override 334 public void incRefCount(final ObjectRef objectRef, final Uid refId) { 335 incDecRefCountQueue.incRefCount(objectRef, refId); 336 } 337 338 @Override 339 public void decRefCount(final ObjectRef objectRef, final Uid refId) { 340 incDecRefCountQueue.decRefCount(objectRef, refId); 341 } 342 343 @Override 344 protected void finalize() throws Throwable { 345 close(); 346 super.finalize(); 347 } 348 349 @Override 350 public void close() { 351 final Thread thread = inverseServiceRequestHandlerThread; 352 if (thread != null) { 353 inverseServiceRequestHandlerThread = null; 354 thread.interrupt(); 355 try { 356 thread.join(); 357 } catch (InterruptedException e) { 358 doNothing(); 359 } 360 } 361 362 objectManager.setNeverEvict(false); 363 364 if (LsConfig.isLocalServerEnabled()) { 365 try { 366 invokeStatic(ObjectRef.class, ObjectRef.VIRTUAL_METHOD_CLOSE_OBJECT_MANAGER, (Class<?>[])null, (Object[]) null); 367 } catch (Exception x) { 368 logger.error("close: " + x, x); 369 } 370 } 371 } 372 373 public Object getRemoteObjectProxyOrCreate(final ObjectRef objectRef) { 374 return objectManager.getRemoteObjectProxyManager().getRemoteObjectProxyOrCreate(objectRef, new RemoteObjectProxyFactory() { 375 @Override 376 public RemoteObjectProxy createRemoteObjectProxy(final ObjectRef objectRef) { 377 final Class<?>[] interfaces = getInterfaces(objectRef); 378 return _createRemoteObjectProxy(objectRef, interfaces); 379 } 380 }); 381 } 382 383 protected Uid getLocalProcessId() { 384 return LsConfig.getProcessId(); 385 } 386 387 protected Uid getRemoteProcessId() { 388 final Uid result = invokeStatic(LsConfig.class, "getProcessId"); 389 return result; 390 } 391 392 public boolean isLocalServerInSeparateProcess() { 393 final Uid localProcessId = getLocalProcessId(); 394 final Uid remoteProcessId = getRemoteProcessId(); 395 return ! localProcessId.equals(remoteProcessId); 396 } 397}