Java tutorial
/* * Copyright (c) Bosch Software Innovations GmbH 2015. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License Version 2.0 as published by the * Free Software Foundation with classpath exception. * * 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 version 2.0 for * more details. * * You should have received a copy of the GNU General Public License along with * this program (please see the COPYING file); if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ package com.bosch.osmi.bdp.access.mock; import com.bosch.osmi.bdp.access.api.BdpApiAccess; import com.bosch.osmi.bdp.access.api.model.User; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.*; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import java.util.Map; /** * Allows an access to the Bdp APIs. An access has to be initialized with a username and a password for which * a user object can be retrieved. The user object is the actual entry point into the whole object net retrieved * from Bdp. * * @author johannes.kristan@bosch-si.com * @since 11/16/15. */ public class BdpApiAccessMockImpl implements BdpApiAccess { public static final String MOCKDATA_CLASSPATH_LOCATION = "/mockdata.json"; private static final Logger LOGGER = LogManager.getLogger(BdpApiAccessMockImpl.class); private JsonObject sourceFile; public BdpApiAccessMockImpl() { LOGGER.info("Initialize mock implementation with data from classpath."); InputStream inputStream = BdpApiAccessMockImpl.class.getResourceAsStream(MOCKDATA_CLASSPATH_LOCATION); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); sourceFile = readJsonFile(inputStreamReader); } public BdpApiAccessMockImpl(String jsonFilePath) throws FileNotFoundException { LOGGER.info("Initialize mock implementation with data from " + jsonFilePath + "."); sourceFile = readJsonFile(new FileReader(jsonFilePath)); } @Override public boolean validateCredentials() { return true; } public User retrieveUser() { JsonInvocationHandler handler = new JsonInvocationHandler(sourceFile); return (User) Proxy.newProxyInstance(User.class.getClassLoader(), new Class[] { User.class }, handler); } private JsonObject readJsonFile(Reader jsonReader) { // Read from File to String JsonParser parser = new JsonParser(); JsonElement jsonElement = parser.parse(jsonReader); return jsonElement.getAsJsonObject(); } /** * InvocationHandler that handles incoming method requests to api methods and forwards them to the respective entry * in a json object with the data. The json-object is retrieved when an object of the host class is created. The * invocation handler is registered at a proxy object that acts as an implementation of the bdp api. * */ private static class JsonInvocationHandler implements InvocationHandler { // http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html private JsonObject context = null; JsonInvocationHandler(JsonObject context) { this.context = context; } /** * called every time a method on the api is called. */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // With the type- and method name we search the corresponding entries in the json document String typeName = method.getDeclaringClass().getCanonicalName(); String methodName = method.getName(); if (Object.class == method.getDeclaringClass()) { if ("equals".equals(methodName)) { return proxy == args[0]; } else if ("hashCode".equals(methodName)) { return System.identityHashCode(proxy); } else if ("toString".equals(methodName)) { return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy)) + ", with InvocationHandler " + this; } else { throw new IllegalStateException(String.valueOf(method)); } } // We search on the Json document that is stored in context. Every JsonInvocationHandler JsonElement jsonElement = context.get(typeName); JsonObject jsonObject = jsonElement.getAsJsonObject(); JsonElement element = jsonObject.get(methodName); if (element.isJsonPrimitive()) { // Maybe add some return type check at method object return element.getAsString(); } else if (element.isJsonArray()) { JsonArray content = element.getAsJsonArray(); return constructCollection(content); } else if (element.isJsonObject()) { JsonObject content = element.getAsJsonObject(); return constructObject(content); } else { throw new IllegalStateException( "No proper way to handle " + element.getClass().getCanonicalName() + " implemented"); } } private Collection<Object> constructCollection(JsonArray array) throws ClassNotFoundException { Collection<Object> result = new ArrayList<>(); for (JsonElement element : array) { if (element.isJsonPrimitive()) { result.add(element.getAsString()); } else if (element.isJsonObject()) { JsonObject jsonObject = element.getAsJsonObject(); Object proxy = constructObject(jsonObject); result.add(proxy); } else { throw new IllegalStateException("Illegal entry in json document: " + element); } } return result; } private Object constructObject(JsonObject jsonObject) throws ClassNotFoundException { Map.Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next(); String typeName = entry.getKey(); Class<?> type = loadClass(typeName); JsonInvocationHandler handler = new JsonInvocationHandler(jsonObject); return Proxy.newProxyInstance(JsonInvocationHandler.class.getClassLoader(), new Class[] { type }, handler); } private Class<?> loadClass(String typeName) throws ClassNotFoundException { // http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html ClassLoader classLoader = JsonInvocationHandler.class.getClassLoader(); return classLoader.loadClass(typeName); } } }