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

com.github.netty.protocol.dubbo.serialization.Hessian2FactoryManager Maven / Gradle / Ivy

The newest version!
/*
 * 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.github.netty.protocol.dubbo.serialization;

import com.alibaba.com.caucho.hessian.io.*;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

public class Hessian2FactoryManager {
    private final ConcurrentHashMap CL_2_SERIALIZER_FACTORY = new ConcurrentHashMap<>();
    private final SerializeSecurityManager serializeSecurityManager;
    private final DefaultSerializeClassChecker defaultSerializeClassChecker;
    String WHITELIST = "dubbo.application.hessian2.whitelist";
    String ALLOW = "dubbo.application.hessian2.allow";
    String DENY = "dubbo.application.hessian2.deny";
    private volatile SerializerFactory SYSTEM_SERIALIZER_FACTORY;
    private volatile SerializerFactory stickySerializerFactory = null;

    public Hessian2FactoryManager() {
        serializeSecurityManager = SerializeSecurityManager.INSTANCE;
        defaultSerializeClassChecker = DefaultSerializeClassChecker.INSTANCE;
    }

    public static  V computeIfAbsent(ConcurrentMap map, K key, Function func) {
        V v = map.get(key);
        if (null == v) {
            // issue#11986 lock bug
            // v = map.computeIfAbsent(key, func);

            // this bug fix methods maybe cause `func.apply` multiple calls.
            v = func.apply(key);
            if (null == v) {
                return null;
            }
            final V res = map.putIfAbsent(key, v);
            if (null != res) {
                // if pre value present, means other thread put value already, and putIfAbsent not effect
                // return exist value
                return res;
            }
            // if pre value is null, means putIfAbsent effected, return current value
        }
        return v;
    }

    public SerializerFactory getSerializerFactory(ClassLoader classLoader) {
        SerializerFactory sticky = stickySerializerFactory;
        if (sticky != null && Objects.equals(sticky.getClassLoader(), classLoader)) {
            return sticky;
        }

        if (classLoader == null) {
            // system classloader
            if (SYSTEM_SERIALIZER_FACTORY == null) {
                synchronized (this) {
                    if (SYSTEM_SERIALIZER_FACTORY == null) {
                        SYSTEM_SERIALIZER_FACTORY = createSerializerFactory(null);
                    }
                }
            }
            stickySerializerFactory = SYSTEM_SERIALIZER_FACTORY;
            return SYSTEM_SERIALIZER_FACTORY;
        }

        SerializerFactory factory = computeIfAbsent(CL_2_SERIALIZER_FACTORY, classLoader,
                this::createSerializerFactory);
        stickySerializerFactory = factory;
        return factory;
    }

    private SerializerFactory createSerializerFactory(ClassLoader classLoader) {
        String whitelist = System.getProperty(WHITELIST);
        if (whitelist != null && !whitelist.isEmpty()) {
            return createWhiteListSerializerFactory(classLoader);
        }
        return createDefaultSerializerFactory(classLoader);
    }

    private SerializerFactory createDefaultSerializerFactory(ClassLoader classLoader) {
        Hessian2SerializerFactory hessian2SerializerFactory = new Hessian2SerializerFactory(classLoader,
                defaultSerializeClassChecker);
        hessian2SerializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian"
                + ".allowNonSerializable", "false")));
        hessian2SerializerFactory.getClassFactory()
                .allow("org.apache.dubbo.*");
        return hessian2SerializerFactory;
    }

    public SerializerFactory createWhiteListSerializerFactory(ClassLoader classLoader) {
        SerializerFactory serializerFactory = new Hessian2SerializerFactory(classLoader, defaultSerializeClassChecker);
        String whiteList = System.getProperty(WHITELIST);
        if ("true".equals(whiteList)) {
            serializerFactory.getClassFactory()
                    .setWhitelist(true);
            String allowPattern = System.getProperty(ALLOW);
            if (allowPattern != null && !allowPattern.isEmpty()) {
                for (String pattern : allowPattern.split(";")) {
                    serializerFactory.getClassFactory()
                            .allow(pattern);
                    serializeSecurityManager.addToAlwaysAllowed(pattern);
                }
            }
            serializeSecurityManager.setCheckStatus(SerializeCheckStatus.STRICT);
        } else {
            serializerFactory.getClassFactory()
                    .setWhitelist(false);
            String denyPattern = System.getProperty(DENY);
            if (denyPattern != null && !denyPattern.isEmpty()) {
                for (String pattern : denyPattern.split(";")) {
                    serializerFactory.getClassFactory()
                            .deny(pattern);
                    serializeSecurityManager.addToDisAllowed(pattern);
                }
            }
        }
        serializerFactory.setAllowNonSerializable(Boolean.parseBoolean(System.getProperty("dubbo.hessian"
                + ".allowNonSerializable", "false")));
        serializerFactory.getClassFactory()
                .allow("org.apache.dubbo.*");
        return serializerFactory;
    }

    public void onRemoveClassLoader(ClassLoader classLoader) {
        CL_2_SERIALIZER_FACTORY.remove(classLoader);
    }

    public static class LazyMapDeserializer extends MapDeserializer {
        private final Class type;

        public LazyMapDeserializer(Class type) {
            super(type);
            this.type = type;
        }

        @Override
        public Object readMap(AbstractHessianInput in, Class expectKeyType, Class expectValueType) throws IOException {
            Deserializer keyDeserializer = null, valueDeserializer = null;
            SerializerFactory factory = findSerializerFactory(in);
            if (expectKeyType != null) {
                keyDeserializer = factory.getDeserializer(expectKeyType.getName());
            }
            if (expectValueType != null) {
                valueDeserializer = factory.getDeserializer(expectValueType.getName());
            }
            LazyMap map = new LazyMap(in, keyDeserializer, valueDeserializer);
            in.addRef(map);
            in.skipOptionalCall();
            return map;
        }

        public static class LazyMap extends HashMap {
            private final AbstractHessianInput in;
            private final Deserializer keyDeserializer, valueDeserializer;

            public LazyMap(AbstractHessianInput in, Deserializer keyDeserializer, Deserializer valueDeserializer) {
                this.in = in;
                this.keyDeserializer = keyDeserializer;
                this.valueDeserializer = valueDeserializer;
            }

            public void readAll() {
                while (true) {
                    try {
                        if (!next()) {
                            break;
                        }
                    } catch (IOException ignored) {
                        break;
                    }
                }
            }

            @Override
            public boolean isEmpty() {
                boolean empty = super.isEmpty();
                if (empty) {
                    try {
                        next();
                    } catch (IOException ignored) {

                    }
                }
                return super.isEmpty();
            }

            @Override
            public V get(Object key) {
                while (true) {
                    if (super.containsKey(key)) {
                        return super.get(key);
                    }
                    try {
                        if (!next()) {
                            return null;
                        }
                    } catch (IOException e) {
                        return null;
                    }
                }
            }

            @Override
            public V getOrDefault(Object key, V defaultValue) {
                while (true) {
                    if (super.containsKey(key)) {
                        return super.get(key);
                    }
                    try {
                        if (!next()) {
                            return defaultValue;
                        }
                    } catch (IOException e) {
                        return null;
                    }
                }
            }

            private boolean next() throws IOException {
                if (in == null || in.isEnd()) {
                    return false;
                } else {
                    Object key = keyDeserializer != null ? keyDeserializer.readObject(in) : in.readObject();
                    Object value = valueDeserializer != null ? valueDeserializer.readObject(in) : in.readObject();
                    if (in.isEnd()) {
                        in.readEnd();
                    }
                    put((K) key, (V) value);
                    return true;
                }
            }
        }
    }

    public static class Hessian2SerializerFactory extends SerializerFactory {
        private final DefaultSerializeClassChecker defaultSerializeClassChecker;
        private final Map mapDeserializerCache = new ConcurrentHashMap<>(3);

        public Hessian2SerializerFactory(
                ClassLoader classLoader, DefaultSerializeClassChecker defaultSerializeClassChecker) {
            super(classLoader);
            this.defaultSerializeClassChecker = defaultSerializeClassChecker;
        }

        @Override
        public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
            if (LazyMapDeserializer.LazyMap.class == cl) {
                return mapDeserializerCache.computeIfAbsent(cl, LazyMapDeserializer::new);
            } else {
                return super.getDeserializer(cl);
            }
        }

        @Override
        public Class loadSerializedClass(String className) throws ClassNotFoundException {
            return defaultSerializeClassChecker.loadClass(getClassLoader(), className);
        }

        @Override
        protected com.alibaba.com.caucho.hessian.io.Serializer getDefaultSerializer(Class cl) {
            if (_defaultSerializer != null) {
                return _defaultSerializer;
            }

            try {
                // pre-check if class is allow
                defaultSerializeClassChecker.loadClass(getClassLoader(), cl.getName());
            } catch (ClassNotFoundException e) {
                // ignore
            }

            checkSerializable(cl);

            return new JavaSerializer(cl, getClassLoader());
        }

        @Override
        protected Deserializer getDefaultDeserializer(Class cl) {
            try {
                // pre-check if class is allow
                defaultSerializeClassChecker.loadClass(getClassLoader(), cl.getName());
            } catch (ClassNotFoundException e) {
                // ignore
            }

            checkSerializable(cl);

            return new JavaDeserializer(cl);
        }

        private void checkSerializable(Class cl) {
            // If class is Serializable => ok
            // If class has not implement Serializable
            //      If hessian check serializable => fail
            //      If dubbo class checker check serializable => fail
            //      If both hessian and dubbo class checker allow non-serializable => ok
            if (!Serializable.class.isAssignableFrom(cl) && (!isAllowNonSerializable()
                    || defaultSerializeClassChecker.isCheckSerializable())) {
                throw new IllegalStateException(
                        "Serialized class " + cl.getName() + " must implement java.io.Serializable");
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy