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