/** * This file is part of ObjectFabric (http://objectfabric.org). * * ObjectFabric is licensed under the Apache License, Version 2.0, the terms * of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html. * * Copyright ObjectFabric Inc. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ package org.objectfabric; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.security.SecureRandom; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import org.objectfabric.JVMBuff.TestBuff; import org.objectfabric.Workspace.Granularity; class JVMPlatform extends Platform { static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final ThreadLocal<Random> _random; private static final ThreadLocal<SimpleDateFormat> _utcNowFormat = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy.MM.dd'-'HH:mm:ss.SSS z"); } }; static { set(new JVMPlatform()); _random = new ThreadLocal<Random>() { @Override protected Random initialValue() { return new Random(); } }; } static void loadClass() { if (Debug.ENABLED) Debug.assertion(get() instanceof JVMPlatform); } @Override int value() { return JVM; } // @Override URI resolve(String uri, URIResolver resolver) { java.net.URI parsed; try { parsed = new java.net.URI(uri); } catch (URISyntaxException e) { throw new RuntimeException(e); } String path = ""; if (parsed.getPath() != null) path += parsed.getPath(); if (parsed.getQuery() != null) path += parsed.getQuery(); if (parsed.getFragment() != null) path += "#" + parsed.getFragment(); int port = parsed.getPort() < 0 ? Address.NULL_PORT : parsed.getPort(); Address address = new Address(parsed.getScheme(), parsed.getHost(), port); return resolver.resolve(address, path); } // @Override String lineSeparator() { return LINE_SEPARATOR; } @Override char fileSeparator() { return File.separatorChar; } @Override String[] split(String value, char... chars) { return value.split("[" + new String(chars) + "]"); } @Override Object[] copyWithTypedResize(Object[] source, int length, Object[] target) { return Arrays.copyOf(source, length, target.getClass()); } @Override Object[] clone(Object[] array) { return array.clone(); } @Override long[] clone(long[] array) { return array.clone(); } // @Override Workspace newCustomWorkspace(CustomLocation store) { return new CustomWorkspace(store); } @Override Buff newBuff(int capacity, boolean recycle) { if (Debug.ENABLED) return new TestBuff(capacity, recycle); return new JVMBuff(capacity, recycle); } @Override Object getReferenceQueue() { return GCQueue.getInstance(); } // @Override boolean randomBoolean() { return _random.get().nextBoolean(); } @Override int randomInt(int limit) { return _random.get().nextInt(limit); } @Override int randomInt() { return _random.get().nextInt(); } @Override double randomDouble() { return _random.get().nextDouble(); } // private final SecureRandom _secureRandom = new SecureRandom(); /** * See java.util.UUID. */ @Override byte[] newUID_() { byte[] bytes = new byte[UID.LENGTH]; _secureRandom.nextBytes(bytes); return bytes; } // @Override int floatToInt(float value) { return Float.floatToRawIntBits(value); } @Override float intToFloat(int value) { return Float.intBitsToFloat(value); } @Override long doubleToLong(double value) { return Double.doubleToRawLongBits(value); } @Override double longToDouble(long value) { return Double.longBitsToDouble(value); } // @Override String formatLog(String message) { String header = _utcNowFormat.get().format(new Date()) + ", "; header += Utils.padRight(Thread.currentThread().getName() + ", ", 25); return header + message; } @Override void logDefault(String message) { System.out.println(message); } @Override String getStackAsString(Throwable t) { Writer result = new StringWriter(); PrintWriter printWriter = new PrintWriter(result); t.printStackTrace(printWriter); return result.toString(); } // Class @Override Class enclosingClass(Class c) { return c.getEnclosingClass(); } @Override boolean isInstance(Class c, Object o) { return c.isInstance(o); } // @Override void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { } } @Override void assertLock(Object lock, boolean hold) { Debug.assertion(Thread.holdsLock(lock) == hold); } @Override void execute(Runnable runnable) { ThreadPool.getInstance().execute(runnable); } @Override void schedule(Runnable runnable, int ms) { ThreadPool.scheduleOnce(runnable, ms); } @Override long approxTimeMs() { return System.nanoTime() >> 20; } @Override String simpleName(Class c) { return c.getSimpleName(); } // @Override String toXML(Object model, String schema) { return XMLSerializer.toXMLString((ObjectModelDef) model, schema); } @Override ObjectModelDef fromXMLFile(String file) { return XMLSerializer.fromXMLFile(file); } // Debug @Override Workspace newTestWorkspace(Granularity granularity) { return new JVMWorkspace(granularity); } @Override Server newTestServer() { return new JVMServer(); } @Override URIHandler newTestStore(String path) { return new FileSystem(path); } // @Override boolean shallowEquals(Object a, Object b, Class c, String... exceptions) { if (!Debug.ENABLED) throw new IllegalStateException(); if (a.getClass() != b.getClass()) return false; if (c.isArray()) { Object[] x = (Object[]) a; Object[] y = (Object[]) b; if (x.length != y.length) return false; for (int i = 0; i < x.length; i++) if (!referenceLevelEquals(c.getComponentType(), x[i], y[i])) return false; } ArrayList<Field> exceptionFields = new ArrayList<Field>(); for (Field field : c.getDeclaredFields()) if (Arrays.asList(exceptions).contains(field.getName())) exceptionFields.add(field); Debug.assertion(exceptionFields.size() == exceptions.length); try { for (Field field : c.getDeclaredFields()) { if (!exceptionFields.contains(field)) { if (!Modifier.isStatic(field.getModifiers())) { if (!Modifier.isFinal(field.getModifiers())) { field.setAccessible(true); Object x = field.get(a); Object y = field.get(b); if (!referenceLevelEquals(field.getType(), x, y)) return false; } } } } } catch (Exception e) { throw new RuntimeException(e); } return true; } private static boolean referenceLevelEquals(Class c, Object a, Object b) { if (!Debug.ENABLED) throw new IllegalStateException(); // Primitives will be boxed so equals if (c.isPrimitive()) return a.equals(b); // Otherwise use == return a == b; } @Override Object getCurrentStack() { if (!Debug.ENABLED) throw new IllegalStateException(); return new Exception().getStackTrace(); } @Override void assertCurrentStack(Object previous) { if (!Debug.ENABLED) throw new IllegalStateException(); ArrayList<StackTraceElement> a = new ArrayList<StackTraceElement>(Arrays.asList((StackTraceElement[]) previous)); ArrayList<StackTraceElement> b = new ArrayList<StackTraceElement>(Arrays.asList(new Exception().getStackTrace())); removeUntestable(a); removeUntestable(b); Debug.assertion(a.size() == b.size()); for (int i = 0; i < a.size(); i++) { if (i < 3) { Debug.assertion(!a.get(i).equals(b.get(i))); Debug.assertion(a.get(i).getClassName().equals(b.get(i).getClassName())); } else if (i == 3) { Debug.assertion(!a.get(i).equals(b.get(i))); Debug.assertion(a.get(i).getClassName().equals(b.get(i).getClassName())); Debug.assertion(a.get(i).getMethodName().equals(b.get(i).getMethodName())); } else if (i > 3) { boolean skip = false; if (a.get(i).toString().contains("org.objectfabric.vm.VM")) skip = true; if (a.get(i).toString().contains("org.mortbay.jetty")) skip = true; Debug.assertion(skip || a.get(i).equals(b.get(i))); } } } private static void removeUntestable(ArrayList<StackTraceElement> list) { for (int i = list.size() - 1; i >= 0; i--) { /* * Method invocation by reflection can sometime take different path. Native * invocation or dynamically generated method, so ignore this part. */ if (list.get(i).getClassName().contains(".reflect.")) list.remove(i); /* * Netty can replace pipeline elements at runtime. */ if (list.get(i).getClassName().toLowerCase().contains("netty")) list.remove(i); } } @Override Object getPrivateField(Object object, String name, Class c) { return getPrivateFieldStatic(object, name, c); } static Object getPrivateFieldStatic(Object object, String name, Class c) { if (!Debug.ENABLED) throw new IllegalStateException(); try { Field field = c.getDeclaredField(name); field.setAccessible(true); return field.get(object); } catch (Exception e) { throw new RuntimeException(e); } } @Override TType getTypeField(Class c) { if (!Debug.ENABLED) throw new IllegalStateException(); TType value = null; try { Field field = c.getField("TYPE"); if (field != null) { Debug.assertion((field.getModifiers() & Modifier.PUBLIC) != 0); Debug.assertion((field.getModifiers() & Modifier.STATIC) != 0); field.setAccessible(true); // Might be package visible class value = (TType) field.get(null); } } catch (Exception e) { throw new RuntimeException(e); } return value; } @Override void writeAndResetAtomicLongs(Object object, boolean write) { StringBuilder sb = write ? new StringBuilder() : null; NumberFormat format = NumberFormat.getInstance(); format.setGroupingUsed(true); for (Field field : object.getClass().getDeclaredFields()) { if (field.getType() == AtomicLong.class) { if (sb != null && sb.length() > 0) sb.append(", "); try { AtomicLong value = (AtomicLong) field.get(object); if (sb != null) sb.append(field.getName() + ": " + format.format(value.get())); value.set(0); } catch (Exception ex) { throw new RuntimeException(ex); } } } if (sb != null) System.out.println(sb.toString()); } }