// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package org.apache.cloudstack.framework.jobs.impl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import com.cloud.utils.exception.CloudRuntimeException; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; /** * Note: toPairList and appendPairList only support simple POJO objects currently */ public class JobSerializerHelper { private static final Logger s_logger = Logger.getLogger(JobSerializerHelper.class); public static final String token = "/"; private static Gson s_gson; static { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setVersion(1.5); s_logger.debug("Job GSON Builder initialized."); gsonBuilder.registerTypeAdapter(Class.class, new ClassTypeAdapter()); gsonBuilder.registerTypeAdapter(Throwable.class, new ThrowableTypeAdapter()); s_gson = gsonBuilder.create(); } public static String toSerializedString(Object result) { if (result != null) { Class<?> clz = result.getClass(); return clz.getName() + token + s_gson.toJson(result); } return null; } public static Object fromSerializedString(String result) { try { if (result != null && !result.isEmpty()) { String[] serializedParts = result.split(token); if (serializedParts.length < 2) { return null; } String clzName = serializedParts[0]; String nameField = null; String content = null; if (serializedParts.length == 2) { content = serializedParts[1]; } else { nameField = serializedParts[1]; int index = result.indexOf(token + nameField + token); content = result.substring(index + nameField.length() + 2); } Class<?> clz; try { clz = Class.forName(clzName); } catch (ClassNotFoundException e) { return null; } Object obj = s_gson.fromJson(content, clz); return obj; } return null; } catch (RuntimeException e) { throw new CloudRuntimeException("Unable to deserialize: " + result, e); } } public static String toObjectSerializedString(Serializable object) { assert (object != null); ByteArrayOutputStream bs = new ByteArrayOutputStream(); try { ObjectOutputStream os = new ObjectOutputStream(bs); os.writeObject(object); os.close(); bs.close(); return Base64.encodeBase64URLSafeString(bs.toByteArray()); } catch (IOException e) { throw new CloudRuntimeException("Unable to serialize: " + object, e); } } public static Object fromObjectSerializedString(String base64EncodedString) { if (base64EncodedString == null) return null; byte[] content = Base64.decodeBase64(base64EncodedString); ByteArrayInputStream bs = new ByteArrayInputStream(content); try { ObjectInputStream is = new ObjectInputStream(bs); Object obj = is.readObject(); is.close(); bs.close(); return obj; } catch (IOException e) { throw new CloudRuntimeException("Unable to serialize: " + base64EncodedString, e); } catch (ClassNotFoundException e) { throw new CloudRuntimeException("Unable to serialize: " + base64EncodedString, e); } } public static class ClassTypeAdapter implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> { @Override public JsonElement serialize(Class<?> clazz, Type typeOfResponseObj, JsonSerializationContext ctx) { return new JsonPrimitive(clazz.getName()); } @Override public Class<?> deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2) throws JsonParseException { String str = arg0.getAsString(); try { return Class.forName(str); } catch (ClassNotFoundException e) { throw new CloudRuntimeException("Unable to find class " + str); } } } public static class ThrowableTypeAdapter implements JsonSerializer<Throwable>, JsonDeserializer<Throwable> { @Override public Throwable deserialize(JsonElement json, Type type, JsonDeserializationContext ctx) throws JsonParseException { JsonObject obj = (JsonObject)json; String className = obj.get("class").getAsString(); try { Class<Throwable> clazz = (Class<Throwable>)Class.forName(className); Throwable cause = s_gson.fromJson(obj.get("cause"), Throwable.class); String msg = obj.get("msg").getAsString(); Constructor<Throwable> constructor = clazz.getConstructor(String.class, Throwable.class); Throwable th = constructor.newInstance(msg, cause); return th; } catch (ClassNotFoundException e) { throw new JsonParseException("Unable to find " + className); } catch (NoSuchMethodException e) { throw new JsonParseException("Unable to find constructor for " + className); } catch (SecurityException e) { throw new JsonParseException("Unable to get over security " + className); } catch (InstantiationException e) { throw new JsonParseException("Unable to instantiate " + className); } catch (IllegalAccessException e) { throw new JsonParseException("Illegal access to " + className, e); } catch (IllegalArgumentException e) { throw new JsonParseException("Illegal argument to " + className, e); } catch (InvocationTargetException e) { throw new JsonParseException("Cannot invoke " + className, e); } } @Override public JsonElement serialize(Throwable th, Type type, JsonSerializationContext ctx) { JsonObject json = new JsonObject(); json.add("class", new JsonPrimitive(th.getClass().getName())); json.add("cause", s_gson.toJsonTree(th.getCause())); json.add("msg", new JsonPrimitive(th.getMessage())); // json.add("stack", s_gson.toJsonTree(th.getStackTrace())); return json; } } }