/* * 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 com.gemstone.gemfire; import java.io.DataInput; import java.io.DataOutput; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.geode.cache.Cache; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.InternalDataSerializer; import org.apache.geode.internal.Version; import org.apache.geode.internal.VersionedDataOutputStream; import org.apache.geode.internal.cache.CacheService; import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.tier.sockets.OldClientSupportService; import org.apache.geode.management.internal.beans.CacheServiceMBeanBase; import com.gemstone.gemfire.cache.execute.EmtpyRegionFunctionException; /** * Support for old GemFire clients */ public class OldClientSupportProvider implements OldClientSupportService { static final String GEODE = "org.apache.geode"; static final String GEMFIRE = "com.gemstone.gemfire"; static final String ALWAYS_CONVERT_CLASSES_NAME = DistributionConfig.GEMFIRE_PREFIX + "old-client-support.convert-all"; /** whether to always convert new package names to old on outgoing serialization */ static final boolean ALWAYS_CONVERT_CLASSES = Boolean.getBoolean(ALWAYS_CONVERT_CLASSES_NAME); private final Map<String, String> oldClassNamesToNew = new ConcurrentHashMap<>(); private final Map<String, String> newClassNamesToOld = new ConcurrentHashMap<>(); /** returns the cache's OldClientSupportService */ public static OldClientSupportService getService(Cache cache) { return (OldClientSupportService) ((InternalCache) cache) .getService(OldClientSupportService.class); } @Override public void init(final Cache cache) { InternalDataSerializer.setOldClientSupportService(this); } @Override public Class<? extends CacheService> getInterface() { return OldClientSupportService.class; } @Override public CacheServiceMBeanBase getMBean() { // no mbean services provided return null; } @Override public String processIncomingClassName(String name) { // tcpserver was moved to a different package in Geode. String oldPackage = "com.gemstone.org.jgroups.stack.tcpserver"; String newPackage = "org.apache.geode.distributed.internal.tcpserver"; if (name.startsWith(oldPackage)) { String cached = oldClassNamesToNew.get(name); if (cached == null) { cached = newPackage + name.substring(oldPackage.length()); oldClassNamesToNew.put(name, cached); } return cached; } return processClassName(name, GEMFIRE, GEODE, oldClassNamesToNew); } @Override public String processIncomingClassName(String name, DataInput in) { return processIncomingClassName(name); } @Override public String processOutgoingClassName(String name, DataOutput out) { // tcpserver was moved to a different package String oldPackage = "com.gemstone.org.jgroups.stack.tcpserver"; String newPackage = "org.apache.geode.distributed.internal.tcpserver"; if (name.startsWith(newPackage)) { return oldPackage + name.substring(newPackage.length()); } if (ALWAYS_CONVERT_CLASSES) { return processClassName(name, GEODE, GEMFIRE, newClassNamesToOld); } // if the client is old then it needs com.gemstone.gemfire package names if (out instanceof VersionedDataOutputStream) { VersionedDataOutputStream vout = (VersionedDataOutputStream) out; Version version = vout.getVersion(); if (version != null && version.compareTo(Version.GFE_90) < 0) { return processClassName(name, GEODE, GEMFIRE, newClassNamesToOld); } } return name; } @Override public Throwable getThrowable(Throwable theThrowable, Version clientVersion) { if (theThrowable == null) { return theThrowable; } if (clientVersion.compareTo(Version.GFE_90) >= 0) { return theThrowable; } String className = theThrowable.getClass().getName(); // this class has been renamed, so it cannot be automatically translated // during java deserialization if (className.equals("org.apache.geode.cache.execute.EmptyRegionFunctionException")) { return new EmtpyRegionFunctionException(theThrowable.getMessage(), theThrowable.getCause()); } // other exceptions will be translated automatically by receivers return theThrowable; } private String processClassName(String p_className, String oldPackage, String newPackage, Map<String, String> cache) { String cached = cache.get(p_className); if (cached != null) { return cached; } String className = p_className; if (className.startsWith(oldPackage)) { className = newPackage + className.substring(oldPackage.length()); } else if (className.startsWith("[") && className.contains("[L" + oldPackage)) { int idx = className.indexOf("[L") + 2; className = className.substring(0, idx) + newPackage + className.substring(idx, oldPackage.length()); } if (className != p_className) { cache.put(p_className, className); } return className; } }