All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geode.CopyHelper Maven / Gradle / Ivy

/*
 * 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.geode;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.UUID;

import org.apache.geode.internal.HeapDataOutputStream;
import org.apache.geode.internal.cache.CachedDeserializable;
import org.apache.geode.internal.cache.Token;
import org.apache.geode.internal.serialization.Version;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.WritablePdxInstance;
import org.apache.geode.pdx.internal.PdxUnreadData;

/**
 * A static helper for optimally creating copies. Creating copies of cache values provides improved
 * concurrency as well as isolation. For transactions, creating a copy is the guaranteed way to
 * enforce "Read Committed" isolation on changes to cache Entries.
 *
 * 

* Here is a simple example of how to use CopyHelper.copy * *

 * Object o = r.get("stringBuf");
 * StringBuffer s = (StringBuffer) CopyHelper.copy(o);
 * s.append("... and they lived happily ever after.  The End.");
 * r.put("stringBuf", s);
 * 
* * @see Cloneable * @see Serializable * @see DataSerializer * @see org.apache.geode.cache.Cache#setCopyOnRead * @see org.apache.geode.cache.CacheTransactionManager * * @since GemFire 4.0 */ public final class CopyHelper { // no instances allowed private CopyHelper() {} /** * Return true if the given object is an instance of a well known immutable class. The well known * classes are: *
    *
  • String *
  • Byte *
  • Character *
  • Short *
  • Integer *
  • Long *
  • Float *
  • Double *
  • BigInteger *
  • BigDecimal *
  • UUID *
  • PdxInstance but not WritablePdxInstance *
* * @param o the object to check * @return true if o is an instance of a well known immutable class. * @since GemFire 6.6.2 */ public static boolean isWellKnownImmutableInstance(Object o) { if (o instanceof String) { return true; } if (o instanceof Number) { if (o instanceof Integer) { return true; } if (o instanceof Long) { return true; } if (o instanceof Byte) { return true; } if (o instanceof Short) { return true; } if (o instanceof Float) { return true; } if (o instanceof Double) { return true; } // subclasses of non-final classes may be mutable if (o.getClass().equals(BigInteger.class)) { return true; } if (o.getClass().equals(BigDecimal.class)) { return true; } } if (o instanceof PdxInstance && !(o instanceof WritablePdxInstance)) { // no need to copy since it is immutable return true; } if (o instanceof Character) { return true; } if (o instanceof UUID) { return true; } return false; } /** *

* Makes a copy of the specified object. The object returned is not guaranteed to be a deep copy * of the original object, as explained below. * *

* Copies can only be made if the original is a Cloneable or serializable by GemFire. If * o is a {@link #isWellKnownImmutableInstance(Object) well known immutable instance} then it will * be returned without copying it. * *

* If the argument o is an instance of {@link java.lang.Cloneable}, a copy is made by invoking * clone on it. Note that not all implementations of clone make deep copies * (e.g. {@link java.util.HashMap#clone HashMap.clone}). Otherwise, if the argument is not an * instance of Cloneable, a copy is made using serialization: if GemFire serialization is * implemented, it is used; otherwise, java serialization is used. * *

* The difference between this method and {@link #deepCopy(Object) deepCopy}, is that this method * uses clone if available, whereas deepCopy does not. As a result, for * Cloneable objects copied using this method, how deep a copy the returned object is * depends on its implementation of clone. * * @param o the original object that a copy is needed of * @return the new instance that is a copy of of the original * @throws CopyException if copying fails because a class could not be found or could not be * serialized. * @see #deepCopy(Object) * @since GemFire 4.0 */ @SuppressWarnings("unchecked") public static T copy(T o) { T copy = null; try { if (o == null) { return null; } else if (o instanceof Token) { return o; } else { if (isWellKnownImmutableInstance(o)) { return o; } if (o instanceof Cloneable) { try { // Note that Object.clone is protected so we need to use reflection // to call clone even though this object implements Cloneable Class c = o.getClass(); // By convention, the user should make the clone method public. // But even if they don't, let's go ahead and use it. // The other problem is that if the class is private, we still // need to make the method accessible even if the method is public, // because Object.clone is protected. Method m = c.getDeclaredMethod("clone", new Class[0]); m.setAccessible(true); copy = (T) m.invoke(o, new Object[0]); return copy; } catch (NoSuchMethodException | IllegalAccessException | SecurityException ignore) { // try using Serialization } catch (InvocationTargetException ex) { Throwable cause = ex.getTargetException(); if (cause instanceof CloneNotSupportedException) { // try using Serialization } else { throw new CopyException("Clone failed.", cause != null ? cause : ex); } } } else if (o instanceof CachedDeserializable) { copy = (T) ((CachedDeserializable) o).getDeserializedWritableCopy(null, null); return copy; } // Copy using serialization copy = doDeepCopy(o); return copy; } } finally { if (copy != null) { PdxUnreadData.copy(o, copy); } } } /** * Makes a deep copy of the specified object o using serialization, so the object has to be * serializable by GemFire. * *

* If o is a {@link #isWellKnownImmutableInstance(Object) well known immutable instance} then it * will be returned without copying it. * *

* The passed in object is serialized in memory, and then deserialized into a new instance, which * is returned. If GemFire serialization is implemented for the object, it is used; otherwise, * java serialization is used. * * @param o the original object to be copied * @return the new instance that is a copy of the original * @throws CopyException if copying fails because a class could not be found or could not be * serialized * @see #copy(Object) */ public static T deepCopy(T o) { T copy = null; try { if (o == null) { return null; } else if (o instanceof Token || isWellKnownImmutableInstance(o)) { return o; } else { copy = doDeepCopy(o); return copy; } } finally { if (copy != null) { PdxUnreadData.copy(o, copy); } } } @SuppressWarnings("unchecked") private static T doDeepCopy(T o) { try { HeapDataOutputStream hdos = new HeapDataOutputStream(Version.CURRENT); DataSerializer.writeObject(o, hdos); return (T) DataSerializer.readObject(new DataInputStream(hdos.getInputStream())); } catch (ClassNotFoundException ex) { throw new CopyException( String.format("Copy failed on instance of %s", o.getClass()), ex); } catch (IOException ex) { throw new CopyException( String.format("Copy failed on instance of %s", o.getClass()), ex); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy