diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml index 59bb67bfd5..450ecafe5e 100644 --- a/dubbo-common/pom.xml +++ b/dubbo-common/pom.xml @@ -57,6 +57,18 @@ limitations under the License. com.alibaba fastjson + + com.esotericsoftware.kryo + kryo + + + de.javakaffee + kryo-serializers + + + de.ruedigermoeller + fst + org.jvnet.sorcerer sorcerer-javac diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java index ae3552af0e..c28335c35a 100644 --- a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java @@ -259,6 +259,12 @@ public class Constants { public static final String SERIALIZATION_KEY = "serialization"; + public static final String EXTENSION_KEY = "extension"; + + public static final String KEEP_ALIVE_KEY = "keepalive"; + + public static final String OPTIMIZER_KEY = "optimizer"; + public static final String EXCHANGER_KEY = "exchanger"; public static final String TRANSPORTER_KEY = "transporter"; diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Cleanable.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Cleanable.java new file mode 100644 index 0000000000..1b69c56b4f --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Cleanable.java @@ -0,0 +1,24 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize; + +/** + * @author lishen + */ +public interface Cleanable { + + void cleanup(); +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/OptimizedSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/OptimizedSerialization.java new file mode 100644 index 0000000000..9216327502 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/OptimizedSerialization.java @@ -0,0 +1,24 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize; + +/** + * Just a marker interface for now + * + * @author lishen + */ +public interface OptimizedSerialization extends Serialization { +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/SerializableClassRegistry.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/SerializableClassRegistry.java new file mode 100644 index 0000000000..3a09d41fe1 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/SerializableClassRegistry.java @@ -0,0 +1,23 @@ +package com.alibaba.dubbo.common.serialize.support; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author lishen + */ +public abstract class SerializableClassRegistry { + + private static final Set registrations = new LinkedHashSet(); + + /** + * only supposed to be called at startup time + */ + public static void registerClass(Class clazz) { + registrations.add(clazz); + } + + public static Set getRegisteredClasses() { + return registrations; + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/SerializationOptimizer.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/SerializationOptimizer.java new file mode 100644 index 0000000000..0f6f7a10cd --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/SerializationOptimizer.java @@ -0,0 +1,13 @@ +package com.alibaba.dubbo.common.serialize.support; + +import java.util.Collection; + +/** + * This class can be replaced with the contents in config file, but for now I think the class is easier to write + * + * @author lishen + */ +public interface SerializationOptimizer { + + Collection getSerializableClasses(); +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstFactory.java new file mode 100644 index 0000000000..38b531eadb --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstFactory.java @@ -0,0 +1,53 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.fst; + +import com.alibaba.dubbo.common.serialize.support.SerializableClassRegistry; +import de.ruedigermoeller.serialization.FSTConfiguration; +import de.ruedigermoeller.serialization.FSTObjectInput; +import de.ruedigermoeller.serialization.FSTObjectOutput; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author lishen + */ +public class FstFactory { + + private static final FstFactory factory = new FstFactory(); + + private final FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration(); + + + public static FstFactory getDefaultFactory() { + return factory; + } + + public FstFactory() { + for (Class clazz : SerializableClassRegistry.getRegisteredClasses()) { + conf.registerClass(clazz); + } + } + + public FSTObjectOutput getObjectOutput(OutputStream outputStream) { + return conf.getObjectOutput(outputStream); + } + + public FSTObjectInput getObjectInput(InputStream inputStream) { + return conf.getObjectInput(inputStream); + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstObjectInput.java new file mode 100644 index 0000000000..01fd6ea8de --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstObjectInput.java @@ -0,0 +1,95 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.fst; + +import com.alibaba.dubbo.common.serialize.ObjectInput; +import de.ruedigermoeller.serialization.FSTObjectInput; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; + +/** + * @author lishen + */ +public class FstObjectInput implements ObjectInput { + + private FSTObjectInput input; + + public FstObjectInput(InputStream inputStream) { + input = FstFactory.getDefaultFactory().getObjectInput(inputStream); + } + + public boolean readBool() throws IOException { + return input.readBoolean(); + } + + public byte readByte() throws IOException { + return input.readByte(); + } + + public short readShort() throws IOException { + return input.readShort(); + } + + public int readInt() throws IOException { + return input.readInt(); + } + + public long readLong() throws IOException { + return input.readLong(); + } + + public float readFloat() throws IOException { + return input.readFloat(); + } + + public double readDouble() throws IOException { + return input.readDouble(); + } + + public byte[] readBytes() throws IOException { + int len = input.readInt(); + if (len < 0) { + return null; + } else if (len == 0) { + return new byte[]{}; + } else { + byte[] b = new byte[len]; + input.readFully(b); + return b; + } + } + + public String readUTF() throws IOException { + return input.readUTF(); + } + + public Object readObject() throws IOException, ClassNotFoundException { + return input.readObject(); + } + + + @SuppressWarnings("unchecked") + public T readObject(Class clazz) throws IOException, ClassNotFoundException { + return (T) readObject(); + } + + @SuppressWarnings("unchecked") + public T readObject(Class clazz, Type type) throws IOException, ClassNotFoundException { + return (T) readObject(); + } +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstObjectOutput.java new file mode 100644 index 0000000000..ba14f041bb --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstObjectOutput.java @@ -0,0 +1,92 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.fst; + +import com.alibaba.dubbo.common.serialize.ObjectOutput; +import de.ruedigermoeller.serialization.FSTObjectOutput; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author lishen + */ +public class FstObjectOutput implements ObjectOutput { + + private FSTObjectOutput output; + + public FstObjectOutput(OutputStream outputStream) { + output = FstFactory.getDefaultFactory().getObjectOutput(outputStream); + } + + public void writeBool(boolean v) throws IOException { + output.writeBoolean(v); + } + + public void writeByte(byte v) throws IOException { + output.writeByte(v); + } + + public void writeShort(short v) throws IOException { + output.writeShort(v); + } + + public void writeInt(int v) throws IOException { + output.writeInt(v); + } + + public void writeLong(long v) throws IOException { + output.writeLong(v); + } + + public void writeFloat(float v) throws IOException { + output.writeFloat(v); + } + + public void writeDouble(double v) throws IOException { + output.writeDouble(v); + } + + public void writeBytes(byte[] v) throws IOException { + if (v == null) { + output.writeInt(-1); + } else { + writeBytes(v, 0, v.length); + } + } + + public void writeBytes(byte[] v, int off, int len) throws IOException { + if (v == null) { + output.writeInt(-1); + } else { + output.writeInt(len); + output.write(v, off, len); + } + } + + + public void writeUTF(String v) throws IOException { + output.writeUTF(v); + } + + public void writeObject(Object v) throws IOException { + output.writeObject(v); + } + + public void flushBuffer() throws IOException { + output.flush(); + } +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstSerialization.java new file mode 100644 index 0000000000..dff215b60b --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/fst/FstSerialization.java @@ -0,0 +1,47 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.fst; + +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.common.serialize.ObjectInput; +import com.alibaba.dubbo.common.serialize.ObjectOutput; +import com.alibaba.dubbo.common.serialize.OptimizedSerialization; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author lishen + */ +public class FstSerialization implements OptimizedSerialization { + + public byte getContentTypeId() { + return 9; + } + + public String getContentType() { + return "x-application/fst"; + } + + public ObjectOutput serialize(URL url, OutputStream out) throws IOException { + return new FstObjectOutput(out); + } + + public ObjectInput deserialize(URL url, InputStream is) throws IOException { + return new FstObjectInput(is); + } +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/CompatibleKryo.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/CompatibleKryo.java new file mode 100644 index 0000000000..8ab2a9ecfd --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/CompatibleKryo.java @@ -0,0 +1,45 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.alibaba.dubbo.common.logger.Logger; +import com.alibaba.dubbo.common.logger.LoggerFactory; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.serializers.JavaSerializer; + +/** + * @author lishen + */ +public class CompatibleKryo extends Kryo { + + private static final Logger logger = LoggerFactory.getLogger(CompatibleKryo.class); + + @Override + public Serializer getDefaultSerializer(Class type) { + if (type == null) { + throw new IllegalArgumentException("type cannot be null."); + } + + if (!type.isArray() && !ReflectionUtils.checkZeroArgConstructor(type)) { + if (logger.isWarnEnabled()) { + logger.warn(type + " has no zero-arg constructor and this will affect the serialization performance"); + } + return new JavaSerializer(); + } + return super.getDefaultSerializer(type); + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoFactory.java new file mode 100644 index 0000000000..72eadd16fd --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoFactory.java @@ -0,0 +1,156 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.alibaba.dubbo.common.serialize.support.SerializableClassRegistry; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.serializers.DefaultSerializers; +import de.javakaffee.kryoserializers.*; + +import java.lang.reflect.InvocationHandler; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; +import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +/** + * @author lishen + */ +public abstract class KryoFactory { + +// private static final KryoFactory factory = new PrototypeKryoFactory(); +// private static final KryoFactory factory = new SingletonKryoFactory(); + private static final KryoFactory factory = new PooledKryoFactory(); + + private final Set registrations = new LinkedHashSet(); + + private boolean registrationRequired; + + private volatile boolean kryoCreated; + + protected KryoFactory() { + // TODO configurable +// Log.DEBUG(); + } + + public static KryoFactory getDefaultFactory() { + return factory; + } + + /** + * only supposed to be called at startup time + * + * later may consider adding support for custom serializer, custom id, etc + */ + public void registerClass(Class clazz) { + + if (kryoCreated) { + throw new IllegalStateException("Can't register class after creating kryo instance"); + } + registrations.add(clazz); + } + + protected Kryo createKryo() { + if (!kryoCreated) { + kryoCreated = true; + } + + Kryo kryo = new CompatibleKryo(); + + // TODO +// kryo.setReferences(false); + kryo.setRegistrationRequired(registrationRequired); + + kryo.register(Arrays.asList("").getClass(), new ArraysAsListSerializer()); + kryo.register(GregorianCalendar.class, new GregorianCalendarSerializer()); + kryo.register(InvocationHandler.class, new JdkProxySerializer()); + kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer()); + kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer()); + kryo.register(Pattern.class, new RegexSerializer()); + kryo.register(BitSet.class, new BitSetSerializer()); + kryo.register(URI.class, new URISerializer()); + kryo.register(UUID.class, new UUIDSerializer()); + UnmodifiableCollectionsSerializer.registerSerializers(kryo); + SynchronizedCollectionsSerializer.registerSerializers(kryo); + + // now just added some very common classes + // TODO optimization + kryo.register(HashMap.class); + kryo.register(ArrayList.class); + kryo.register(LinkedList.class); + kryo.register(HashSet.class); + kryo.register(TreeSet.class); + kryo.register(Hashtable.class); + kryo.register(Date.class); + kryo.register(Calendar.class); + kryo.register(ConcurrentHashMap.class); + kryo.register(SimpleDateFormat.class); + kryo.register(GregorianCalendar.class); + kryo.register(Vector.class); + kryo.register(BitSet.class); + kryo.register(StringBuffer.class); + kryo.register(StringBuilder.class); + kryo.register(Object.class); + kryo.register(Object[].class); + kryo.register(String[].class); + kryo.register(byte[].class); + kryo.register(char[].class); + kryo.register(int[].class); + kryo.register(float[].class); + kryo.register(double[].class); + + for (Class clazz : registrations) { + kryo.register(clazz); + } + + for (Class clazz : SerializableClassRegistry.getRegisteredClasses()) { + kryo.register(clazz); + } + + return kryo; + } + + public void returnKryo(Kryo kryo) { + // do nothing by default + } + + public void setRegistrationRequired(boolean registrationRequired) { + this.registrationRequired = registrationRequired; + } + + public void close() { + // do nothing by default + } + + public abstract Kryo getKryo(); +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoObjectInput.java new file mode 100644 index 0000000000..e441a64c21 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoObjectInput.java @@ -0,0 +1,158 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.alibaba.dubbo.common.serialize.Cleanable; +import com.alibaba.dubbo.common.serialize.ObjectInput; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.KryoException; +import com.esotericsoftware.kryo.io.Input; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; + +/** + * @author lishen + */ +public class KryoObjectInput implements ObjectInput, Cleanable { + + private Kryo kryo = KryoFactory.getDefaultFactory().getKryo(); + private Input input; + + public KryoObjectInput(InputStream inputStream) { + input = new Input(inputStream); + } + + public boolean readBool() throws IOException { + try { + return input.readBoolean(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public byte readByte() throws IOException { + try { + return input.readByte(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public short readShort() throws IOException { + try { + return input.readShort(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public int readInt() throws IOException { + try { + return input.readInt(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public long readLong() throws IOException { + try { + return input.readLong(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public float readFloat() throws IOException { + try { + return input.readFloat(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public double readDouble() throws IOException { + try { + return input.readDouble(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public byte[] readBytes() throws IOException { + try { + int len = input.readInt(); + if (len < 0) { + return null; + } else if (len == 0) { + return new byte[]{}; + } else { + return input.readBytes(len); + } + } catch (KryoException e) { + throw new IOException(e); + } + } + + public String readUTF() throws IOException { + // TODO + try { +// return kryo.readObject(input, String.class); + return input.readString(); + } catch (KryoException e) { + throw new IOException(e); + } + } + + public Object readObject() throws IOException, ClassNotFoundException { + // TODO +// throw new UnsupportedOperationException(); + try { + return kryo.readClassAndObject(input); + } catch (KryoException e) { + throw new IOException(e); + } + } + + + @SuppressWarnings("unchecked") + public T readObject(Class clazz) throws IOException, ClassNotFoundException { + // TODO optimization +// try { +// return (T) kryo.readClassAndObject(input); +// } catch (KryoException e) { +// throw new IOException(e); +// } + return (T) readObject(); + } + + @SuppressWarnings("unchecked") + public T readObject(Class clazz, Type type) throws IOException, ClassNotFoundException { +// try { +// return readObject(clazz); +// } catch (KryoException e) { +// throw new IOException(e); +// } + // TODO optimization + return (T) readObject(clazz); + } + + public void cleanup() { + KryoFactory.getDefaultFactory().returnKryo(kryo); + kryo = null; + } +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoObjectOutput.java new file mode 100644 index 0000000000..fb127be602 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoObjectOutput.java @@ -0,0 +1,102 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.alibaba.dubbo.common.serialize.Cleanable; +import com.alibaba.dubbo.common.serialize.ObjectOutput; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Output; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author lishen + */ +public class KryoObjectOutput implements ObjectOutput, Cleanable { + + private Kryo kryo = KryoFactory.getDefaultFactory().getKryo(); + private Output output; + + public KryoObjectOutput(OutputStream outputStream) { + output = new Output(outputStream); + } + + public void writeBool(boolean v) throws IOException { + output.writeBoolean(v); + } + + public void writeByte(byte v) throws IOException { + output.writeByte(v); + } + + public void writeShort(short v) throws IOException { + output.writeShort(v); + } + + public void writeInt(int v) throws IOException { + output.writeInt(v); + } + + public void writeLong(long v) throws IOException { + output.writeLong(v); + } + + public void writeFloat(float v) throws IOException { + output.writeFloat(v); + } + + public void writeDouble(double v) throws IOException { + output.writeDouble(v); + } + + public void writeBytes(byte[] v) throws IOException { + if (v == null) { + output.writeInt(-1); + } else { + writeBytes(v, 0, v.length); + } + } + + public void writeBytes(byte[] v, int off, int len) throws IOException { + if (v == null) { + output.writeInt(-1); + } else { + output.writeInt(len); + output.write(v, off, len); + } + } + + + public void writeUTF(String v) throws IOException { + // TODO + output.writeString(v); +// kryo.writeObject(output, v); + } + + public void writeObject(Object v) throws IOException { + kryo.writeClassAndObject(output, v); + } + + public void flushBuffer() throws IOException { + output.flush(); + } + + public void cleanup() { + KryoFactory.getDefaultFactory().returnKryo(kryo); + kryo = null; + } +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoSerialization.java new file mode 100644 index 0000000000..07766cb4c5 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/KryoSerialization.java @@ -0,0 +1,49 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.common.serialize.ObjectInput; +import com.alibaba.dubbo.common.serialize.ObjectOutput; +import com.alibaba.dubbo.common.serialize.OptimizedSerialization; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * TODO for now kryo serialization doesn't deny classes that don't implement the serializable interface + * + * @author lishen + */ +public class KryoSerialization implements OptimizedSerialization { + + public byte getContentTypeId() { + return 8; + } + + public String getContentType() { + return "x-application/kryo"; + } + + public ObjectOutput serialize(URL url, OutputStream out) throws IOException { + return new KryoObjectOutput(out); + } + + public ObjectInput deserialize(URL url, InputStream is) throws IOException { + return new KryoObjectInput(is); + } +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/PooledKryoFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/PooledKryoFactory.java new file mode 100644 index 0000000000..7c2cfa5a6a --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/PooledKryoFactory.java @@ -0,0 +1,47 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.esotericsoftware.kryo.Kryo; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @author lishen + */ +public class PooledKryoFactory extends KryoFactory { + + private final Queue pool = new ConcurrentLinkedQueue(); + + @Override + public void returnKryo(Kryo kryo) { + pool.offer(kryo); + } + + @Override + public void close() { + pool.clear(); + } + + public Kryo getKryo() { + Kryo kryo = pool.poll(); + if (kryo == null) { + kryo = createKryo(); + } + return kryo; + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/PrototypeKryoFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/PrototypeKryoFactory.java new file mode 100644 index 0000000000..06c18787cb --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/PrototypeKryoFactory.java @@ -0,0 +1,28 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.esotericsoftware.kryo.Kryo; + +/** + * @author lishen + */ +public class PrototypeKryoFactory extends KryoFactory { + + public Kryo getKryo() { + return createKryo(); + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/ReflectionUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/ReflectionUtils.java new file mode 100644 index 0000000000..40997fe399 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/ReflectionUtils.java @@ -0,0 +1,31 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +/** + * @author lishen + */ +public abstract class ReflectionUtils { + + public static boolean checkZeroArgConstructor(Class clazz) { + try { + clazz.getDeclaredConstructor(); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/SingletonKryoFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/SingletonKryoFactory.java new file mode 100644 index 0000000000..2e3aefd487 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/SingletonKryoFactory.java @@ -0,0 +1,37 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.esotericsoftware.kryo.Kryo; + +/** + * CAUSION this is only for test purpose since both kryo and this class are not thread-safe + * + * @author lishen + */ +public class SingletonKryoFactory extends KryoFactory { + +// private final Kryo instance = createKryo(); + private Kryo instance; + + @Override + public Kryo getKryo() { + if (instance == null) { + instance = createKryo(); + } + return instance; + } +} diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/ThreadLocalKryoFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/ThreadLocalKryoFactory.java new file mode 100644 index 0000000000..aee8659ab5 --- /dev/null +++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/kryo/ThreadLocalKryoFactory.java @@ -0,0 +1,35 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.common.serialize.support.kryo; + +import com.esotericsoftware.kryo.Kryo; + +/** + * @author lishen + */ +public class ThreadLocalKryoFactory extends KryoFactory { + + private final ThreadLocal holder = new ThreadLocal() { + @Override + protected Kryo initialValue() { + return createKryo(); + } + }; + + public Kryo getKryo() { + return holder.get(); + } +} diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.common.serialize.Serialization b/dubbo-common/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.common.serialize.Serialization index 01fb303512..92cc56d477 100644 --- a/dubbo-common/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.common.serialize.Serialization +++ b/dubbo-common/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.common.serialize.Serialization @@ -3,4 +3,6 @@ hessian2=com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serializatio java=com.alibaba.dubbo.common.serialize.support.java.JavaSerialization compactedjava=com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization fastjson=com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization -nativejava=com.alibaba.dubbo.common.serialize.support.nativejava.NativeJavaSerialization \ No newline at end of file +nativejava=com.alibaba.dubbo.common.serialize.support.nativejava.NativeJavaSerialization +kryo=com.alibaba.dubbo.common.serialize.support.kryo.KryoSerialization +fst=com.alibaba.dubbo.common.serialize.support.fst.FstSerialization \ No newline at end of file diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FstSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FstSerializationTest.java new file mode 100644 index 0000000000..02932765df --- /dev/null +++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FstSerializationTest.java @@ -0,0 +1,13 @@ +package com.alibaba.dubbo.common.serialize.serialization; + +import com.alibaba.dubbo.common.serialize.support.kryo.KryoSerialization; + +/** + * @author lishen + */ +public class FstSerializationTest extends AbstractSerializationTest { + + { + serialization = new KryoSerialization(); + } +} diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/KyroSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/KyroSerializationTest.java new file mode 100644 index 0000000000..244a49fa1d --- /dev/null +++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/KyroSerializationTest.java @@ -0,0 +1,13 @@ +package com.alibaba.dubbo.common.serialize.serialization; + +import com.alibaba.dubbo.common.serialize.support.kryo.KryoSerialization; + +/** + * @author lishen + */ +public class KyroSerializationTest extends AbstractSerializationTest { + + { + serialization = new KryoSerialization(); + } +} \ No newline at end of file diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/ReflectionUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/ReflectionUtilsTest.java new file mode 100644 index 0000000000..b78c5be181 --- /dev/null +++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/ReflectionUtilsTest.java @@ -0,0 +1,33 @@ +package com.alibaba.dubbo.common.serialize.serialization; + +import com.alibaba.dubbo.common.serialize.support.kryo.ReflectionUtils; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +/** + * @author lishen + */ +public class ReflectionUtilsTest { + + @Test + public void test() { + assertTrue(ReflectionUtils.checkZeroArgConstructor(String.class)); + assertTrue(ReflectionUtils.checkZeroArgConstructor(Bar.class)); + assertFalse(ReflectionUtils.checkZeroArgConstructor(Foo.class)); + } + + static class Foo { + public Foo(int i) { + + } + } + + static class Bar { + private Bar() { + + } + } +} diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java index 9669112489..f88d1efcf4 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java @@ -119,6 +119,16 @@ public class ProtocolConfig extends AbstractConfig { // whether to register private Boolean register; + // parameters + // 是否长连接 + // TODO add this to provider config + private Boolean keepAlive; + + // TODO add this to provider config + private String optimizer; + + private String extension; + // parameters private Map parameters; @@ -443,6 +453,30 @@ public class ProtocolConfig extends AbstractConfig { this.isDefault = isDefault; } + public Boolean getKeepAlive() { + return keepAlive; + } + + public void setKeepAlive(Boolean keepAlive) { + this.keepAlive = keepAlive; + } + + public String getOptimizer() { + return optimizer; + } + + public void setOptimizer(String optimizer) { + this.optimizer = optimizer; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + public void destory() { if (name != null) { ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).destroy(); diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java index 635245f155..d2e7e9b557 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java @@ -34,6 +34,7 @@ import com.alibaba.dubbo.rpc.Exporter; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.Protocol; import com.alibaba.dubbo.rpc.ProxyFactory; +import com.alibaba.dubbo.rpc.ServiceClassHolder; import com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory; import com.alibaba.dubbo.rpc.service.GenericService; import com.alibaba.dubbo.rpc.support.ProtocolUtils; @@ -523,6 +524,7 @@ public class ServiceConfig extends AbstractServiceConfig { .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(LOCALHOST) .setPort(0); + ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref)); Exporter exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); @@ -530,6 +532,9 @@ public class ServiceConfig extends AbstractServiceConfig { } } + protected Class getServiceClass(T ref) { + return ref.getClass(); + } /** * Register & bind IP address for service provider, can be configured separately. diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd index 2a41cb9cae..c064c3e21f 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd @@ -804,6 +804,21 @@ + + + + + + + + + + + + + + + diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java index a1a8f19037..c35da1eede 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java @@ -20,6 +20,7 @@ import com.alibaba.dubbo.common.io.Bytes; import com.alibaba.dubbo.common.io.StreamUtils; import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; +import com.alibaba.dubbo.common.serialize.Cleanable; import com.alibaba.dubbo.common.serialize.ObjectInput; import com.alibaba.dubbo.common.serialize.ObjectOutput; import com.alibaba.dubbo.common.serialize.Serialization; @@ -230,6 +231,9 @@ public class ExchangeCodec extends TelnetCodec { encodeRequestData(channel, out, req.getData()); } out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } bos.flush(); bos.close(); int len = bos.writtenBytes(); @@ -271,6 +275,9 @@ public class ExchangeCodec extends TelnetCodec { } } else out.writeUTF(res.getErrorMessage()); out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } bos.flush(); bos.close(); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java index 10f09dce6a..71368754a3 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java @@ -16,6 +16,7 @@ */ package com.alibaba.dubbo.remoting.transport.codec; +import com.alibaba.dubbo.common.serialize.Cleanable; import com.alibaba.dubbo.common.serialize.ObjectInput; import com.alibaba.dubbo.common.serialize.ObjectOutput; import com.alibaba.dubbo.common.utils.StringUtils; @@ -39,11 +40,19 @@ public class TransportCodec extends AbstractCodec { ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output); encodeData(channel, objectOutput, message); objectOutput.flushBuffer(); + if (objectOutput instanceof Cleanable) { + ((Cleanable) objectOutput).cleanup(); + } } public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { InputStream input = new ChannelBufferInputStream(buffer); - return decodeData(channel, getSerialization(channel).deserialize(channel.getUrl(), input)); + ObjectInput objectInput = getSerialization(channel).deserialize(channel.getUrl(), input); + Object object = decodeData(channel, objectInput); + if (objectInput instanceof Cleanable) { + ((Cleanable) objectInput).cleanup(); + } + return object; } protected void encodeData(Channel channel, ObjectOutput output, Object message) throws IOException { diff --git a/dubbo-remoting/dubbo-remoting-http/pom.xml b/dubbo-remoting/dubbo-remoting-http/pom.xml index 7b72659148..f06660d5f0 100644 --- a/dubbo-remoting/dubbo-remoting-http/pom.xml +++ b/dubbo-remoting/dubbo-remoting-http/pom.xml @@ -39,5 +39,13 @@ org.mortbay.jetty jetty + + org.apache.tomcat.embed + tomcat-embed-core + + + org.apache.tomcat.embed + tomcat-embed-logging-juli + \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java index 9070a65eeb..83f4d826b1 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java @@ -23,12 +23,15 @@ import com.alibaba.dubbo.common.logger.LoggerFactory; import com.alibaba.dubbo.common.utils.NetUtils; import com.alibaba.dubbo.remoting.http.HttpHandler; import com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet; +import com.alibaba.dubbo.remoting.http.servlet.ServletManager; import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer; - import org.mortbay.jetty.Server; import org.mortbay.jetty.nio.SelectChannelConnector; +import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.ServletHandler; import org.mortbay.jetty.servlet.ServletHolder; +import org.mortbay.log.Log; +import org.mortbay.log.StdErrLog; import org.mortbay.thread.QueuedThreadPool; public class JettyHttpServer extends AbstractHttpServer { @@ -36,9 +39,16 @@ public class JettyHttpServer extends AbstractHttpServer { private static final Logger logger = LoggerFactory.getLogger(JettyHttpServer.class); private Server server; + + private URL url; public JettyHttpServer(URL url, final HttpHandler handler) { super(url, handler); + this.url = url; + // TODO we should leave this setting to slf4j + Log.setLog(new StdErrLog()); + Log.getLog().setDebugEnabled(false); + DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()), handler); int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS); @@ -62,8 +72,13 @@ public class JettyHttpServer extends AbstractHttpServer { ServletHandler servletHandler = new ServletHandler(); ServletHolder servletHolder = servletHandler.addServletWithMapping(DispatcherServlet.class, "/*"); servletHolder.setInitOrder(2); - - server.addHandler(servletHandler); + + // dubbo's original impl can't support the use of ServletContext + // server.addHandler(servletHandler); + // TODO Context.SESSIONS is the best option here? + Context context = new Context(server, "/", Context.SESSIONS); + context.setServletHandler(servletHandler); + ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext()); try { server.start(); @@ -75,6 +90,7 @@ public class JettyHttpServer extends AbstractHttpServer { public void close() { super.close(); + ServletManager.getInstance().removeServletContext(url.getPort()); if (server != null) { try { server.stop(); diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/BootstrapListener.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/BootstrapListener.java new file mode 100644 index 0000000000..7a562574aa --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/BootstrapListener.java @@ -0,0 +1,35 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.remoting.http.servlet; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * This class must be defined before something like spring's ContextLoaderListener in web.xml + * + * @author lishen + */ +public class BootstrapListener implements ServletContextListener { + + public void contextInitialized(ServletContextEvent servletContextEvent) { + ServletManager.getInstance().addServletContext(ServletManager.EXTERNAL_SERVER_PORT, servletContextEvent.getServletContext()); + } + + public void contextDestroyed(ServletContextEvent servletContextEvent) { + ServletManager.getInstance().removeServletContext(ServletManager.EXTERNAL_SERVER_PORT); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletManager.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletManager.java new file mode 100644 index 0000000000..8e9744d3aa --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletManager.java @@ -0,0 +1,51 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.remoting.http.servlet; + +import javax.servlet.ServletContext; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * TODO this may not be a pretty elegant solution, + * and we may need to make change to the whole remoting-http architecture in the future + * + * @author lishen + */ +public class ServletManager { + + public static final int EXTERNAL_SERVER_PORT = -1234; + + private static final ServletManager instance = new ServletManager(); + + private final Map contextMap = new ConcurrentHashMap(); + + public static ServletManager getInstance() { + return instance; + } + + public void addServletContext(int port, ServletContext servletContext) { + contextMap.put(port, servletContext); + } + + public void removeServletContext(int port) { + contextMap.remove(port); + } + + public ServletContext getServletContext(int port) { + return contextMap.get(port); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/tomcat/TomcatHttpBinder.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/tomcat/TomcatHttpBinder.java new file mode 100755 index 0000000000..fd6a05eb36 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/tomcat/TomcatHttpBinder.java @@ -0,0 +1,32 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.remoting.http.tomcat; + +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.remoting.http.HttpBinder; +import com.alibaba.dubbo.remoting.http.HttpHandler; +import com.alibaba.dubbo.remoting.http.HttpServer; + +/** + * @author lishen + */ +public class TomcatHttpBinder implements HttpBinder { + + public HttpServer bind(URL url, HttpHandler handler) { + return new TomcatHttpServer(url, handler); + } + +} \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/tomcat/TomcatHttpServer.java b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/tomcat/TomcatHttpServer.java new file mode 100755 index 0000000000..686d562ae6 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/tomcat/TomcatHttpServer.java @@ -0,0 +1,91 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.remoting.http.tomcat; + + +import com.alibaba.dubbo.common.Constants; +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.common.logger.Logger; +import com.alibaba.dubbo.common.logger.LoggerFactory; +import com.alibaba.dubbo.remoting.http.HttpHandler; +import com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet; +import com.alibaba.dubbo.remoting.http.servlet.ServletManager; +import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.startup.Tomcat; + +import java.io.File; + + +/** + * @author lishen + */ +public class TomcatHttpServer extends AbstractHttpServer { + + private static final Logger logger = LoggerFactory.getLogger(TomcatHttpServer.class); + + private final Tomcat tomcat; + + private final URL url; + + public TomcatHttpServer(URL url, final HttpHandler handler) { + super(url, handler); + + this.url = url; + DispatcherServlet.addHttpHandler(url.getPort(), handler); + String baseDir = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath(); + tomcat = new Tomcat(); + tomcat.setBaseDir(baseDir); + tomcat.setPort(url.getPort()); + tomcat.getConnector().setProperty( + "maxThreads", String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS))); +// tomcat.getConnector().setProperty( +// "minSpareThreads", String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS))); + + tomcat.getConnector().setProperty( + "maxConnections", String.valueOf(url.getParameter(Constants.ACCEPTS_KEY, -1))); + + tomcat.getConnector().setProperty("URIEncoding", "UTF-8"); + tomcat.getConnector().setProperty("connectionTimeout", "60000"); + + tomcat.getConnector().setProperty("maxKeepAliveRequests", "-1"); + tomcat.getConnector().setProtocol("org.apache.coyote.http11.Http11NioProtocol"); + + Context context = tomcat.addContext("/", baseDir); + Tomcat.addServlet(context, "dispatcher", new DispatcherServlet()); + context.addServletMapping("/*", "dispatcher"); + ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext()); + + try { + tomcat.start(); + } catch (LifecycleException e) { + throw new IllegalStateException("Failed to start tomcat server at " + url.getAddress(), e); + } + } + + public void close() { + super.close(); + + ServletManager.getInstance().removeServletContext(url.getPort()); + + try { + tomcat.stop(); + } catch (Exception e) { + logger.warn(e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.remoting.http.HttpBinder b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.remoting.http.HttpBinder index b1be1077e5..a241d7d563 100644 --- a/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.remoting.http.HttpBinder +++ b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.remoting.http.HttpBinder @@ -1,2 +1,3 @@ servlet=com.alibaba.dubbo.remoting.http.servlet.ServletHttpBinder -jetty=com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder \ No newline at end of file +jetty=com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder +tomcat=com.alibaba.dubbo.remoting.http.tomcat.TomcatHttpBinder \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java index 4db313d4ad..5fc527067e 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java @@ -68,6 +68,13 @@ public class RpcContext { private InetSocketAddress localAddress; private InetSocketAddress remoteAddress; + + // now we don't use the 'values' map to hold these objects + // we want these objects to be as generic as possible + private Object request; + + private Object response; + @Deprecated private List> invokers; @Deprecated @@ -78,6 +85,32 @@ public class RpcContext { protected RpcContext() { } + /** + * Get the request object of the underlying RPC protocol, e.g. HttServletRequest + * + * @return null if the underlying protocol doesn't provide support for getting request + */ + public Object getRequest() { + return request; + } + + public void setRequest(Object request) { + this.request = request; + } + + /** + * Get the response object of the underlying RPC protocol, e.g. HttServletResponse + * + * @return null if the underlying protocol doesn't provide support for getting response + */ + public Object getResponse() { + return response; + } + + public void setResponse(Object response) { + this.response = response; + } + /** * get context. * diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ServiceClassHolder.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ServiceClassHolder.java new file mode 100644 index 0000000000..e6f9ec1516 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/ServiceClassHolder.java @@ -0,0 +1,30 @@ +package com.alibaba.dubbo.rpc; + +/** + * TODO this is just a workround for rest protocol, and now we just ensure it works in the most common dubbo usages + * + * @author lishen + */ +public class ServiceClassHolder { + + private static final ServiceClassHolder INSTANCE = new ServiceClassHolder(); + + private final ThreadLocal holder = new ThreadLocal(); + + public static ServiceClassHolder getInstance() { + return INSTANCE; + } + + private ServiceClassHolder() { + } + + public Class popServiceClass() { + Class clazz = holder.get(); + holder.remove(); + return clazz; + } + + public void pushServiceClass(Class clazz) { + holder.set(clazz); + } +} diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java index c46f39267d..66a2d44222 100644 --- a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -19,6 +19,7 @@ package com.alibaba.dubbo.rpc.protocol.dubbo; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; +import com.alibaba.dubbo.common.serialize.Cleanable; import com.alibaba.dubbo.common.serialize.ObjectInput; import com.alibaba.dubbo.common.utils.Assert; import com.alibaba.dubbo.common.utils.ReflectUtils; @@ -94,19 +95,40 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec try { Object[] args; Class[] pts; - String desc = in.readUTF(); - if (desc.length() == 0) { - pts = DubboCodec.EMPTY_CLASS_ARRAY; - args = DubboCodec.EMPTY_OBJECT_ARRAY; + int argNum = in.readInt(); + if (argNum >= 0) { + if (argNum == 0) { + pts = DubboCodec.EMPTY_CLASS_ARRAY; + args = DubboCodec.EMPTY_OBJECT_ARRAY; + } else { + args = new Object[argNum]; + pts = new Class[argNum]; + for (int i = 0; i < args.length; i++) { + try { + args[i] = in.readObject(); + pts[i] = args[i].getClass(); + } catch (Exception e) { + if (log.isWarnEnabled()) { + log.warn("Decode argument failed: " + e.getMessage(), e); + } + } + } + } } else { - pts = ReflectUtils.desc2classArray(desc); - args = new Object[pts.length]; - for (int i = 0; i < args.length; i++) { - try { - args[i] = in.readObject(pts[i]); - } catch (Exception e) { - if (log.isWarnEnabled()) { - log.warn("Decode argument failed: " + e.getMessage(), e); + String desc = in.readUTF(); + if (desc.length() == 0) { + pts = DubboCodec.EMPTY_CLASS_ARRAY; + args = DubboCodec.EMPTY_OBJECT_ARRAY; + } else { + pts = ReflectUtils.desc2classArray(desc); + args = new Object[pts.length]; + for (int i = 0; i < args.length; i++) { + try { + args[i] = in.readObject(pts[i]); + } catch (Exception e) { + if (log.isWarnEnabled()) { + log.warn("Decode argument failed: " + e.getMessage(), e); + } } } } @@ -131,6 +153,10 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec } catch (ClassNotFoundException e) { throw new IOException(StringUtils.toString("Read invocation data failed.", e)); + } finally { + if (in instanceof Cleanable) { + ((Cleanable) in).cleanup(); + } } return this; } diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java index f4ae684311..75ef0ac55f 100644 --- a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java @@ -18,6 +18,7 @@ package com.alibaba.dubbo.rpc.protocol.dubbo; import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; +import com.alibaba.dubbo.common.serialize.Cleanable; import com.alibaba.dubbo.common.serialize.ObjectInput; import com.alibaba.dubbo.common.utils.Assert; import com.alibaba.dubbo.common.utils.StringUtils; @@ -69,7 +70,7 @@ public class DecodeableRpcResult extends RpcResult implements Codec, Decodeable public Object decode(Channel channel, InputStream input) throws IOException { ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) .deserialize(channel.getUrl(), input); - + byte flag = in.readByte(); switch (flag) { case DubboCodec.RESPONSE_NULL_VALUE: @@ -97,6 +98,9 @@ public class DecodeableRpcResult extends RpcResult implements Codec, Decodeable default: throw new IOException("Unknown result flag, expect '0' '1' '2', get " + flag); } + if (in instanceof Cleanable) { + ((Cleanable) in).cleanup(); + } return this; } diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java index 012051cbc4..949266af83 100644 --- a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java +++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java @@ -25,6 +25,7 @@ import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; import com.alibaba.dubbo.common.serialize.ObjectInput; import com.alibaba.dubbo.common.serialize.ObjectOutput; +import com.alibaba.dubbo.common.serialize.OptimizedSerialization; import com.alibaba.dubbo.common.serialize.Serialization; import com.alibaba.dubbo.common.utils.ReflectUtils; import com.alibaba.dubbo.common.utils.StringUtils; @@ -168,7 +169,14 @@ public class DubboCodec extends ExchangeCodec implements Codec2 { out.writeUTF(inv.getAttachment(Constants.VERSION_KEY)); out.writeUTF(inv.getMethodName()); - out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes())); + + if (getSerialization(channel) instanceof OptimizedSerialization && !containComplexArguments(inv)) { + out.writeInt(inv.getParameterTypes().length); + } else { + out.writeInt(-1); + out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes())); + } + Object[] args = inv.getArguments(); if (args != null) for (int i = 0; i < args.length; i++) { @@ -195,4 +203,14 @@ public class DubboCodec extends ExchangeCodec implements Codec2 { out.writeObject(th); } } + + // workaround for the target method matching of kryo & fst + private boolean containComplexArguments(RpcInvocation invocation) { + for (int i = 0; i < invocation.getParameterTypes().length; i++) { + if (invocation.getArguments()[i] == null || invocation.getParameterTypes()[i] != invocation.getArguments()[i].getClass()) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java index eb83f1cc5e..1b151ceb96 100644 --- a/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java +++ b/dubbo-rpc/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java @@ -18,8 +18,10 @@ package com.alibaba.dubbo.rpc.protocol.dubbo; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; -import com.alibaba.dubbo.common.Version; import com.alibaba.dubbo.common.extension.ExtensionLoader; +import com.alibaba.dubbo.common.serialize.support.SerializableClassRegistry; +import com.alibaba.dubbo.common.serialize.support.SerializationOptimizer; +import com.alibaba.dubbo.common.utils.ConcurrentHashSet; import com.alibaba.dubbo.common.utils.NetUtils; import com.alibaba.dubbo.common.utils.StringUtils; import com.alibaba.dubbo.remoting.Channel; @@ -62,6 +64,7 @@ public class DubboProtocol extends AbstractProtocol { private final Map serverMap = new ConcurrentHashMap(); // private final Map referenceClientMap = new ConcurrentHashMap(); // private final ConcurrentMap ghostClientMap = new ConcurrentHashMap(); + private final Set optimizers = new ConcurrentHashSet(); //consumer side export a stub service for dispatching event //servicekey-stubmethods private final ConcurrentMap stubServiceMethodsMap = new ConcurrentHashMap(); @@ -236,7 +239,7 @@ public class DubboProtocol extends AbstractProtocol { } openServer(url); - + optimizeSerialization(url); return exporter; } @@ -283,7 +286,42 @@ public class DubboProtocol extends AbstractProtocol { return server; } + private void optimizeSerialization(URL url) throws RpcException { + String className = url.getParameter(Constants.OPTIMIZER_KEY, ""); + if (StringUtils.isEmpty(className) || optimizers.contains(className)) { + return; + } + + logger.info("Optimizing the serialization process for Kryo, FST, etc..."); + + try { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className); + if (!SerializationOptimizer.class.isAssignableFrom(clazz)) { + throw new RpcException("The serialization optimizer " + className + " isn't an instance of " + SerializationOptimizer.class.getName()); + } + + SerializationOptimizer optimizer = (SerializationOptimizer) clazz.newInstance(); + + if (optimizer.getSerializableClasses() == null) { + return; + } + + for (Class c : optimizer.getSerializableClasses()) { + SerializableClassRegistry.registerClass(c); + } + + optimizers.add(className); + } catch (ClassNotFoundException e) { + throw new RpcException("Cannot find the serialization optimizer class: " + className, e); + } catch (InstantiationException e) { + throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e); + } catch (IllegalAccessException e) { + throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e); + } + } + public Invoker refer(Class serviceType, URL url) throws RpcException { + optimizeSerialization(url); // create rpc invoker. DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers); invokers.add(invoker); diff --git a/dubbo-rpc/dubbo-rpc-rest/pom.xml b/dubbo-rpc/dubbo-rpc-rest/pom.xml new file mode 100644 index 0000000000..1faf0e5a3b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + com.alibaba + dubbo-rpc + 2.5.8 + + dubbo-rpc-rest + jar + ${project.artifactId} + The JAX-RS rpc module of dubbo project + + true + + + + com.alibaba + dubbo-rpc-api + ${project.parent.version} + + + + com.alibaba + dubbo-remoting-http + ${project.parent.version} + + + + org.jboss.resteasy + resteasy-jaxrs + + + + org.jboss.resteasy + resteasy-client + + + + javax.validation + validation-api + + + + + + org.jboss.resteasy + resteasy-netty + + + + org.jboss.resteasy + resteasy-jdk-http + + + + + + + + + + + + + + + + + + + + + + + + + + org.jboss.resteasy + resteasy-jackson-provider + + + + org.jboss.resteasy + resteasy-jaxb-provider + + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/BaseRestServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/BaseRestServer.java new file mode 100644 index 0000000000..a788043e66 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/BaseRestServer.java @@ -0,0 +1,64 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.Constants; +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.common.utils.StringUtils; +import org.jboss.resteasy.spi.ResteasyDeployment; + +/** + * @author lishen + */ +public abstract class BaseRestServer implements RestServer { + + public void start(URL url) { + getDeployment().getMediaTypeMappings().put("json", "application/json"); + getDeployment().getMediaTypeMappings().put("xml", "text/xml"); +// server.getDeployment().getMediaTypeMappings().put("xml", "application/xml"); + getDeployment().getProviderClasses().add(RpcContextFilter.class.getName()); + // TODO users can override this mapper, but we just rely on the current priority strategy of resteasy + getDeployment().getProviderClasses().add(RpcExceptionMapper.class.getName()); + + loadProviders(url.getParameter(Constants.EXTENSION_KEY, "")); + + doStart(url); + } + + public void deploy(Class resourceDef, Object resourceInstance, String contextPath) { + if (StringUtils.isEmpty(contextPath)) { + getDeployment().getRegistry().addResourceFactory(new DubboResourceFactory(resourceInstance, resourceDef)); + } else { + getDeployment().getRegistry().addResourceFactory(new DubboResourceFactory(resourceInstance, resourceDef), contextPath); + } + } + + public void undeploy(Class resourceDef) { + getDeployment().getRegistry().removeRegistrations(resourceDef); + } + + protected void loadProviders(String value) { + for (String clazz : Constants.COMMA_SPLIT_PATTERN.split(value)) { + if (!StringUtils.isEmpty(clazz)) { + getDeployment().getProviderClasses().add(clazz.trim()); + } + } + } + + protected abstract ResteasyDeployment getDeployment(); + + protected abstract void doStart(URL url); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/DubboHttpServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/DubboHttpServer.java new file mode 100644 index 0000000000..92d259907d --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/DubboHttpServer.java @@ -0,0 +1,122 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.remoting.http.HttpBinder; +import com.alibaba.dubbo.remoting.http.HttpHandler; +import com.alibaba.dubbo.remoting.http.HttpServer; +import com.alibaba.dubbo.remoting.http.servlet.BootstrapListener; +import com.alibaba.dubbo.remoting.http.servlet.ServletManager; +import com.alibaba.dubbo.rpc.RpcContext; +import com.alibaba.dubbo.rpc.RpcException; +import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; +import org.jboss.resteasy.spi.ResteasyDeployment; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Enumeration; + +/** + * @author lishen + */ +public class DubboHttpServer extends BaseRestServer { + + private final HttpServletDispatcher dispatcher = new HttpServletDispatcher(); + private final ResteasyDeployment deployment = new ResteasyDeployment(); + private HttpBinder httpBinder; + private HttpServer httpServer; +// private boolean isExternalServer; + + public DubboHttpServer(HttpBinder httpBinder) { + this.httpBinder = httpBinder; + } + + protected void doStart(URL url) { + // TODO jetty will by default enable keepAlive so the xml config has no effect now + httpServer = httpBinder.bind(url, new RestHandler()); + + ServletContext servletContext = ServletManager.getInstance().getServletContext(url.getPort()); + if (servletContext == null) { + servletContext = ServletManager.getInstance().getServletContext(ServletManager.EXTERNAL_SERVER_PORT); + } + if (servletContext == null) { + throw new RpcException("No servlet context found. If you are using server='servlet', " + + "make sure that you've configured " + BootstrapListener.class.getName() + " in web.xml"); + } + + servletContext.setAttribute(ResteasyDeployment.class.getName(), deployment); + + try { + dispatcher.init(new SimpleServletConfig(servletContext)); + } catch (ServletException e) { + throw new RpcException(e); + } + } + + public void stop() { + httpServer.close(); + } + + protected ResteasyDeployment getDeployment() { + return deployment; + } + + private class RestHandler implements HttpHandler { + + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); + dispatcher.service(request, response); + } + } + + private static class SimpleServletConfig implements ServletConfig { + + private final ServletContext servletContext; + + public SimpleServletConfig(ServletContext servletContext) { + this.servletContext = servletContext; + } + + public String getServletName() { + return "DispatcherServlet"; + } + + public ServletContext getServletContext() { + return servletContext; + } + + public String getInitParameter(String s) { + return null; + } + + public Enumeration getInitParameterNames() { + return new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public Object nextElement() { + return null; + } + }; + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/DubboResourceFactory.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/DubboResourceFactory.java new file mode 100644 index 0000000000..c28cc22518 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/DubboResourceFactory.java @@ -0,0 +1,71 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.ResourceFactory; +import org.jboss.resteasy.spi.ResteasyProviderFactory; + +/** + * We don't support propertyInjector here since the resource impl should be singleton in dubbo + * + * @author lishen + */ +public class DubboResourceFactory implements ResourceFactory { + + private Object resourceInstance; + private Class scannableClass; +// private PropertyInjector propertyInjector; +// private String context = null; + + public DubboResourceFactory(Object resourceInstance, Class scannableClass) { + this.resourceInstance = resourceInstance; + this.scannableClass = scannableClass; + } + +// public PropertyInjector getPropertyInjector() { +// return propertyInjector; +// } + + public Object createResource(HttpRequest request, HttpResponse response, + ResteasyProviderFactory factory) { + return resourceInstance; + } + + public Class getScannableClass() { + return scannableClass; + } + + public void registered(ResteasyProviderFactory factory) { +// this.propertyInjector = factory.getInjectorFactory().createPropertyInjector(getScannableClass(), factory); + } + + public void requestFinished(HttpRequest request, HttpResponse response, + Object resource) { + } + + public void unregistered() { + } + +// public void setContext(String context) { +// this.context = context; +// } +// +// public String getContext() { +// return context; +// } +} \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/NettyServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/NettyServer.java new file mode 100644 index 0000000000..9b3ed76389 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/NettyServer.java @@ -0,0 +1,47 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.Constants; +import com.alibaba.dubbo.common.URL; +import org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer; +import org.jboss.resteasy.spi.ResteasyDeployment; + +/** + * Netty server can't support @Context injection of servlet objects since it's not a servlet container + * + * @author lishen + */ +public class NettyServer extends BaseRestServer { + + private final NettyJaxrsServer server = new NettyJaxrsServer(); + + protected void doStart(URL url) { + server.setPort(url.getPort()); + server.setKeepAlive(url.getParameter(Constants.KEEP_ALIVE_KEY, true)); + server.setExecutorThreadCount(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS)); + server.setIoWorkerCount(url.getParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS)); + server.start(); + } + + public void stop() { + server.stop(); + } + + protected ResteasyDeployment getDeployment() { + return server.getDeployment(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestConstraintViolation.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestConstraintViolation.java new file mode 100644 index 0000000000..8c73243efc --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestConstraintViolation.java @@ -0,0 +1,68 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; + +/** + * @author lishen + */ +@XmlRootElement(name = "constraintViolation") +@XmlAccessorType(XmlAccessType.FIELD) +public class RestConstraintViolation implements Serializable { + + private static final long serialVersionUID = -23497234978L; + + private String path; + private String message; + private String value; + + public RestConstraintViolation(String path, String message, String value) { + this.path = path; + this.message = message; + this.value = value; + } + + public RestConstraintViolation() { + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestProtocol.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestProtocol.java new file mode 100644 index 0000000000..918f28f91a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestProtocol.java @@ -0,0 +1,265 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.Constants; +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.common.utils.StringUtils; +import com.alibaba.dubbo.remoting.http.HttpBinder; +import com.alibaba.dubbo.remoting.http.servlet.BootstrapListener; +import com.alibaba.dubbo.remoting.http.servlet.ServletManager; +import com.alibaba.dubbo.rpc.RpcException; +import com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol; +import com.alibaba.dubbo.rpc.ServiceClassHolder; +import org.apache.http.HeaderElement; +import org.apache.http.HeaderElementIterator; +import org.apache.http.HttpResponse; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.message.BasicHeaderElementIterator; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HTTP; +import org.apache.http.protocol.HttpContext; +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; +import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine; +import org.jboss.resteasy.util.GetRestful; + +import javax.servlet.ServletContext; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.WebApplicationException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * @author lishen + */ +public class RestProtocol extends AbstractProxyProtocol { + + private static final int DEFAULT_PORT = 80; + + private final Map servers = new ConcurrentHashMap(); + + private final RestServerFactory serverFactory = new RestServerFactory(); + + // TODO in the future maybe we can just use a single rest client and connection manager + private final List clients = Collections.synchronizedList(new LinkedList()); + + private volatile ConnectionMonitor connectionMonitor; + + public RestProtocol() { + super(WebApplicationException.class, ProcessingException.class); + } + + public void setHttpBinder(HttpBinder httpBinder) { + serverFactory.setHttpBinder(httpBinder); + } + + public int getDefaultPort() { + return DEFAULT_PORT; + } + + protected Runnable doExport(T impl, Class type, URL url) throws RpcException { + String addr = url.getIp() + ":" + url.getPort(); + Class implClass = ServiceClassHolder.getInstance().popServiceClass(); + RestServer server = servers.get(addr); + if (server == null) { + server = serverFactory.createServer(url.getParameter(Constants.SERVER_KEY, "jetty")); + server.start(url); + servers.put(addr, server); + } + + String contextPath = getContextPath(url); + if ("servlet".equalsIgnoreCase(url.getParameter(Constants.SERVER_KEY, "jetty"))) { + ServletContext servletContext = ServletManager.getInstance().getServletContext(ServletManager.EXTERNAL_SERVER_PORT); + if (servletContext == null) { + throw new RpcException("No servlet context found. Since you are using server='servlet', " + + "make sure that you've configured " + BootstrapListener.class.getName() + " in web.xml"); + } + String webappPath = servletContext.getContextPath(); + if (StringUtils.isNotEmpty(webappPath)) { + webappPath = webappPath.substring(1); + if (!contextPath.startsWith(webappPath)) { + throw new RpcException("Since you are using server='servlet', " + + "make sure that the 'contextpath' property starts with the path of external webapp"); + } + contextPath = contextPath.substring(webappPath.length()); + if (contextPath.startsWith("/")) { + contextPath = contextPath.substring(1); + } + } + } + + final Class resourceDef = GetRestful.getRootResourceClass(implClass) != null ? implClass : type; + + server.deploy(resourceDef, impl, contextPath); + + final RestServer s = server; + return new Runnable() { + public void run() { + // TODO due to dubbo's current architecture, + // it will be called from registry protocol in the shutdown process and won't appear in logs + s.undeploy(resourceDef); + } + }; + } + + protected T doRefer(Class serviceType, URL url) throws RpcException { + if (connectionMonitor == null) { + connectionMonitor = new ConnectionMonitor(); + } + + // TODO more configs to add + + PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(); + // 20 is the default maxTotal of current PoolingClientConnectionManager + connectionManager.setMaxTotal(url.getParameter(Constants.CONNECTIONS_KEY, 20)); + connectionManager.setDefaultMaxPerRoute(url.getParameter(Constants.CONNECTIONS_KEY, 20)); + + connectionMonitor.addConnectionManager(connectionManager); + +// BasicHttpContext localContext = new BasicHttpContext(); + + DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager); + + httpClient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() { + public long getKeepAliveDuration(HttpResponse response, HttpContext context) { + HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE)); + while (it.hasNext()) { + HeaderElement he = it.nextElement(); + String param = he.getName(); + String value = he.getValue(); + if (value != null && param.equalsIgnoreCase("timeout")) { + return Long.parseLong(value) * 1000; + } + } + // TODO constant + return 30 * 1000; + } + }); + + HttpParams params = httpClient.getParams(); + // TODO currently no xml config for Constants.CONNECT_TIMEOUT_KEY so we directly reuse Constants.TIMEOUT_KEY for now + HttpConnectionParams.setConnectionTimeout(params, url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT)); + HttpConnectionParams.setSoTimeout(params, url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT)); + HttpConnectionParams.setTcpNoDelay(params, true); + HttpConnectionParams.setSoKeepalive(params, true); + + ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient/*, localContext*/); + + ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build(); + clients.add(client); + + client.register(RpcContextFilter.class); + for (String clazz : Constants.COMMA_SPLIT_PATTERN.split(url.getParameter(Constants.EXTENSION_KEY, ""))) { + if (!StringUtils.isEmpty(clazz)) { + try { + client.register(Thread.currentThread().getContextClassLoader().loadClass(clazz.trim())); + } catch (ClassNotFoundException e) { + throw new RpcException("Error loading JAX-RS extension class: " + clazz.trim(), e); + } + } + } + + // TODO protocol + ResteasyWebTarget target = client.target("http://" + url.getHost() + ":" + url.getPort() + "/" + getContextPath(url)); + return target.proxy(serviceType); + } + + protected int getErrorCode(Throwable e) { + // TODO + return super.getErrorCode(e); + } + + public void destroy() { + super.destroy(); + + if (connectionMonitor != null) { + connectionMonitor.shutdown(); + } + + for (Map.Entry entry : servers.entrySet()) { + try { + if (logger.isInfoEnabled()) { + logger.info("Closing the rest server at " + entry.getKey()); + } + entry.getValue().stop(); + } catch (Throwable t) { + logger.warn("Error closing rest server", t); + } + } + servers.clear(); + + if (logger.isInfoEnabled()) { + logger.info("Closing rest clients"); + } + for (ResteasyClient client : clients) { + try { + client.close(); + } catch (Throwable t) { + logger.warn("Error closing rest client", t); + } + } + clients.clear(); + } + + protected String getContextPath(URL url) { + int pos = url.getPath().lastIndexOf("/"); + return pos > 0 ? url.getPath().substring(0, pos) : ""; + } + + protected class ConnectionMonitor extends Thread { + private volatile boolean shutdown; + private final List connectionManagers = Collections.synchronizedList(new LinkedList()); + + public void addConnectionManager(ClientConnectionManager connectionManager) { + connectionManagers.add(connectionManager); + } + + public void run() { + try { + while (!shutdown) { + synchronized (this) { + wait(1000); + for (ClientConnectionManager connectionManager : connectionManagers) { + connectionManager.closeExpiredConnections(); + // TODO constant + connectionManager.closeIdleConnections(30, TimeUnit.SECONDS); + } + } + } + } catch (InterruptedException ex) { + shutdown(); + } + } + + public void shutdown() { + shutdown = true; + connectionManagers.clear(); + synchronized (this) { + notifyAll(); + } + } + } +} \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestServer.java new file mode 100644 index 0000000000..cc989550a0 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestServer.java @@ -0,0 +1,35 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.URL; + +/** + * @author lishen + */ +public interface RestServer { + + void start(URL url); + + /** + * @param resourceDef it could be either resource interface or resource impl + */ + void deploy(Class resourceDef, Object resourceInstance, String contextPath); + + void undeploy(Class resourceDef); + + void stop(); +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestServerFactory.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestServerFactory.java new file mode 100644 index 0000000000..0b7c9b3873 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RestServerFactory.java @@ -0,0 +1,48 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.remoting.http.HttpBinder; + +/** + * Only the server that implements servlet container + * could support something like @Context injection of servlet objects. + * + * @author lishen + */ +public class RestServerFactory { + + private HttpBinder httpBinder; + + public void setHttpBinder(HttpBinder httpBinder) { + this.httpBinder = httpBinder; + } + + public RestServer createServer(String name) { + // TODO move names to Constants + if ("servlet".equalsIgnoreCase(name) || "jetty".equalsIgnoreCase(name) || "tomcat".equalsIgnoreCase(name)) { + return new DubboHttpServer(httpBinder); +// } else if ("tjws".equalsIgnoreCase(name)) { +// return new TjwsServer(); + } else if ("netty".equalsIgnoreCase(name)) { + return new NettyServer(); + } else if ("sunhttp".equalsIgnoreCase(name)) { + return new SunHttpServer(); + } else { + throw new IllegalArgumentException("Unrecognized server name: " + name); + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RpcContextFilter.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RpcContextFilter.java new file mode 100644 index 0000000000..5c678d964b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RpcContextFilter.java @@ -0,0 +1,90 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.utils.StringUtils; +import com.alibaba.dubbo.rpc.RpcContext; +import org.jboss.resteasy.spi.ResteasyProviderFactory; + +import javax.annotation.Priority; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import java.io.IOException; +import java.util.Map; + +/** + * @author lishen + */ +@Priority(Integer.MIN_VALUE + 1) +public class RpcContextFilter implements ContainerRequestFilter, ClientRequestFilter { + + private static final String DUBBO_ATTACHMENT_HEADER = "Dubbo-Attachments"; + + // currently we use a single header to hold the attachments so that the total attachment size limit is about 8k + private static final int MAX_HEADER_SIZE = 8 * 1024; + + public void filter(ContainerRequestContext requestContext) throws IOException { + HttpServletRequest request = ResteasyProviderFactory.getContextData(HttpServletRequest.class); + RpcContext.getContext().setRequest(request); + + // this only works for servlet containers + if (request != null && RpcContext.getContext().getRemoteAddress() == null) { + RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); + } + + RpcContext.getContext().setResponse(ResteasyProviderFactory.getContextData(HttpServletResponse.class)); + + String headers = requestContext.getHeaderString(DUBBO_ATTACHMENT_HEADER); + if (headers != null) { + for (String header : headers.split(",")) { + int index = header.indexOf("="); + if (index > 0) { + String key = header.substring(0, index); + String value = header.substring(index + 1); + if (!StringUtils.isEmpty(key)) { + RpcContext.getContext().setAttachment(key.trim(), value.trim()); + } + } + } + } + } + + public void filter(ClientRequestContext requestContext) throws IOException { + int size = 0; + for (Map.Entry entry : RpcContext.getContext().getAttachments().entrySet()) { + if (entry.getValue().contains(",") || entry.getValue().contains("=") + || entry.getKey().contains(",") || entry.getKey().contains("=")) { + throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " must not contain ',' or '=' when using rest protocol"); + } + + // TODO for now we don't consider the differences of encoding and server limit + size += entry.getValue().getBytes("UTF-8").length; + if (size > MAX_HEADER_SIZE) { + throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " is too big"); + } + + StringBuilder attachments = new StringBuilder(); + attachments.append(entry.getKey()); + attachments.append("="); + attachments.append(entry.getValue()); + requestContext.getHeaders().add(DUBBO_ATTACHMENT_HEADER, attachments.toString()); + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RpcExceptionMapper.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RpcExceptionMapper.java new file mode 100644 index 0000000000..3b0cc31dca --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/RpcExceptionMapper.java @@ -0,0 +1,52 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.rpc.RpcException; +import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author lishen + */ +public class RpcExceptionMapper implements ExceptionMapper { + + public Response toResponse(RpcException e) { + // TODO do more sophisticated exception handling and output + if (e.getCause() instanceof ConstraintViolationException) { + return handleConstraintViolationException((ConstraintViolationException) e.getCause()); + } + // we may want to avoid exposing the dubbo exception details to certain clients + // TODO for now just do plain text output + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal server error: " + e.getMessage()).type(ContentType.TEXT_PLAIN_UTF_8).build(); + } + + protected Response handleConstraintViolationException(ConstraintViolationException cve) { + ViolationReport report = new ViolationReport(); + for (ConstraintViolation cv : cve.getConstraintViolations()) { + report.addConstraintViolation(new RestConstraintViolation( + cv.getPropertyPath().toString(), + cv.getMessage(), + cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString())); + } + // TODO for now just do xml output + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(report).type(ContentType.TEXT_XML_UTF_8).build(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/SunHttpServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/SunHttpServer.java new file mode 100644 index 0000000000..7e4e51a1e1 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/SunHttpServer.java @@ -0,0 +1,41 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.URL; +import org.jboss.resteasy.plugins.server.sun.http.SunHttpJaxrsServer; +import org.jboss.resteasy.spi.ResteasyDeployment; + +/** + * @author lishen + */ +public class SunHttpServer extends BaseRestServer { + + private final SunHttpJaxrsServer server = new SunHttpJaxrsServer(); + + protected void doStart(URL url) { + server.setPort(url.getPort()); + server.start(); + } + + public void stop() { + server.stop(); + } + + protected ResteasyDeployment getDeployment() { + return server.getDeployment(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/TjwsServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/TjwsServer.java new file mode 100644 index 0000000000..3a5e7c5a8a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/TjwsServer.java @@ -0,0 +1,45 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import com.alibaba.dubbo.common.URL; +import org.jboss.resteasy.spi.ResteasyDeployment; + +/** + * @author lishen + */ +public class TjwsServer extends BaseRestServer { + +// private final TJWSEmbeddedJaxrsServer server = new TJWSEmbeddedJaxrsServer(); + + protected void doStart(URL url) { + throw new UnsupportedOperationException("TJWS server is now unsupported"); +// server.setPort(url.getPort()); +// // below config is useless due to a resteasy bug +//// server.setKeepAlive(false); +// server.start(); + } + + protected ResteasyDeployment getDeployment() { + throw new UnsupportedOperationException("TJWS server is now unsupported"); +// return server.getDeployment(); + } + + public void stop() { + throw new UnsupportedOperationException("TJWS server is now unsupported"); +// server.stop(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/UndertowServer.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/UndertowServer.java new file mode 100644 index 0000000000..d8d7ff0602 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/UndertowServer.java @@ -0,0 +1,68 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +//import com.alibaba.dubbo.common.URL; +//import com.alibaba.dubbo.common.utils.StringUtils; +//import io.undertow.Undertow; +//import io.undertow.servlet.api.DeploymentInfo; +//import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; +//import org.jboss.resteasy.spi.ResteasyDeployment; + +/** + * TODO this impl hasn't been well tested, and we can consider move undertow to a general remoting-http impl in the future + * + * @author lishen + */ +public class UndertowServer /*implements RestServer*/ { + +// // Note that UndertowJaxrsServer doesn't implement EmbeddedJaxrsServer +// +// private final ResteasyDeployment deployment = new ResteasyDeployment(); +// +// private final UndertowJaxrsServer server = new UndertowJaxrsServer(); +// +// public void start(URL url) { +// deployment.start(); +// DeploymentInfo deploymentInfo = server.undertowDeployment(deployment); +// deploymentInfo.setContextPath("/"); +// deploymentInfo.setDeploymentName("dubbo-rest"); +// deploymentInfo.setClassLoader(Thread.currentThread().getContextClassLoader()); +// server.deploy(deploymentInfo); +// server.start(Undertow.builder().addHttpListener(url.getPort(), url.getHost())); +// } +// +// public void deploy(Class resourceDef, Object resourceInstance, String contextPath) { +// if (StringUtils.isEmpty(contextPath)) { +// deployment.getRegistry().addResourceFactory(new DubboResourceFactory(resourceInstance, resourceDef)); +// } else { +// deployment.getRegistry().addResourceFactory(new DubboResourceFactory(resourceInstance, resourceDef), contextPath); +// } +// } +// +// public void undeploy(Class resourceDef) { +// deployment.getRegistry().removeRegistrations(resourceDef); +// } +// +// public void deploy(Class resourceDef, Object resourceInstance) { +// deploy(resourceDef, resourceInstance, "/"); +// } +// +// public void stop() { +// deployment.stop(); +// server.stop(); +// } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/ViolationReport.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/ViolationReport.java new file mode 100644 index 0000000000..4fa6e5dd08 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/ViolationReport.java @@ -0,0 +1,50 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; + +/** + * @author lishen + */ +@XmlRootElement(name="violationReport") +@XmlAccessorType(XmlAccessType.FIELD) +public class ViolationReport implements Serializable { + + private static final long serialVersionUID = -130498234L; + + private List constraintViolations; + + public List getConstraintViolations() { + return constraintViolations; + } + + public void setConstraintViolations(List constraintViolations) { + this.constraintViolations = constraintViolations; + } + + public void addConstraintViolation(RestConstraintViolation constraintViolation) { + if (constraintViolations == null) { + constraintViolations = new LinkedList(); + } + constraintViolations.add(constraintViolation); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/support/ContentType.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/support/ContentType.java new file mode 100644 index 0000000000..d388748134 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/support/ContentType.java @@ -0,0 +1,28 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest.support; + +import javax.ws.rs.core.MediaType; + +/** + * @author lishen + */ +public class ContentType { + + public static final String APPLICATION_JSON_UTF_8 = MediaType.APPLICATION_JSON + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8"; + public static final String TEXT_XML_UTF_8 = MediaType.TEXT_XML + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8"; + public static final String TEXT_PLAIN_UTF_8 = MediaType.TEXT_PLAIN + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8"; +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/support/LoggingFilter.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/support/LoggingFilter.java new file mode 100644 index 0000000000..92b1b44053 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/com/alibaba/dubbo/rpc/protocol/rest/support/LoggingFilter.java @@ -0,0 +1,141 @@ +/** + * Copyright 1999-2014 dangdang.com. + * + * Licensed 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.alibaba.dubbo.rpc.protocol.rest.support; + +import com.alibaba.dubbo.common.logger.Logger; +import com.alibaba.dubbo.common.logger.LoggerFactory; +import org.apache.commons.io.IOUtils; + +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + +/** + * This logging filter is not highly optimized for now + * + * @author lishen + */ +@Priority(Integer.MIN_VALUE) +public class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, ClientResponseFilter, WriterInterceptor, ReaderInterceptor { + + private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class); + + public void filter(ClientRequestContext context) throws IOException { + logHttpHeaders(context.getStringHeaders()); + } + + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + logHttpHeaders(responseContext.getHeaders()); + } + + public void filter(ContainerRequestContext context) throws IOException { + logHttpHeaders(context.getHeaders()); + } + + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { + logHttpHeaders(responseContext.getStringHeaders()); + } + + public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { + byte[] buffer = IOUtils.toByteArray(context.getInputStream()); + logger.info("The contents of request body is: \n" + new String(buffer, "UTF-8") + "\n"); + context.setInputStream(new ByteArrayInputStream(buffer)); + return context.proceed(); + } + + public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { + OutputStreamWrapper wrapper = new OutputStreamWrapper(context.getOutputStream()); + context.setOutputStream(wrapper); + context.proceed(); + logger.info("The contents of response body is: \n" + new String(wrapper.getBytes(), "UTF-8") + "\n"); + } + + protected void logHttpHeaders(MultivaluedMap headers) { + StringBuilder msg = new StringBuilder("The HTTP headers are: \n"); + for (Map.Entry> entry : headers.entrySet()) { + msg.append(entry.getKey()).append(": "); + for (int i = 0; i < entry.getValue().size(); i++) { + msg.append(entry.getValue().get(i)); + if (i < entry.getValue().size() - 1) { + msg.append(", "); + } + } + msg.append("\n"); + } + logger.info(msg.toString()); + } + + protected static class OutputStreamWrapper extends OutputStream { + + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private final OutputStream output; + + private OutputStreamWrapper(OutputStream output) { + this.output = output; + } + + @Override + public void write(int i) throws IOException { + buffer.write(i); + output.write(i); + } + + @Override + public void write(byte[] b) throws IOException { + buffer.write(b); + output.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + buffer.write(b, off, len); + output.write(b, off, len); + } + + @Override + public void flush() throws IOException { + output.flush(); + } + + @Override + public void close() throws IOException { + output.close(); + } + + public byte[] getBytes() { + return buffer.toByteArray(); + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol new file mode 100644 index 0000000000..a0f05272d0 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol @@ -0,0 +1 @@ +rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol \ No newline at end of file diff --git a/dubbo-rpc/pom.xml b/dubbo-rpc/pom.xml index a1d0ef8068..d24f424a92 100644 --- a/dubbo-rpc/pom.xml +++ b/dubbo-rpc/pom.xml @@ -40,5 +40,6 @@ limitations under the License. dubbo-rpc-thrift dubbo-rpc-memcached dubbo-rpc-redis + dubbo-rpc-rest diff --git a/dubbo/pom.xml b/dubbo/pom.xml index 154919a171..ef0c769ecd 100644 --- a/dubbo/pom.xml +++ b/dubbo/pom.xml @@ -187,6 +187,53 @@ limitations under the License. + + com.alibaba + dubbo-rpc-rest + ${project.parent.version} + + + org.jboss.resteasy + resteasy-jaxrs + + + org.jboss.resteasy + resteasy-client + + + org.jboss.resteasy + resteasy-netty + + + org.jboss.resteasy + resteasy-jdk-http + + + org.jboss.resteasy + resteasy-undertow + + + io.undertow + undertow-servlet + + + io.undertow + undertow-core + + + org.jboss.resteasy + resteasy-jackson-provider + + + org.jboss.resteasy + resteasy-jaxb-provider + + + javax.validation + validation-api + + + com.alibaba dubbo-registry-default diff --git a/pom.xml b/pom.xml index 93b45ababf..3a632adec5 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,12 @@ limitations under the License. 2.2 3.1.6 1.7 + 2.24.0 + 0.26 + 1.55 + 2.0 + 3.0.7.Final + 8.0.11 1.7.25 1.2 @@ -303,6 +309,66 @@ limitations under the License. velocity ${velocity_version} + + com.esotericsoftware.kryo + kryo + ${kryo_version} + + + de.javakaffee + kryo-serializers + ${kryo_serializers_version} + + + de.ruedigermoeller + fst + ${fst_version} + + + javax.ws.rs + javax.ws.rs-api + ${rs_api_version} + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy_version} + + + org.jboss.resteasy + resteasy-client + ${resteasy_version} + + + org.jboss.resteasy + resteasy-netty + ${resteasy_version} + + + org.jboss.resteasy + resteasy-jdk-http + ${resteasy_version} + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy_version} + + + org.jboss.resteasy + resteasy-jaxb-provider + ${resteasy_version} + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat_embed_version} + + + org.apache.tomcat.embed + tomcat-embed-logging-juli + ${tomcat_embed_version} + org.slf4j