Merge pull request from #1001, merge dubbox branch

1. rest protocol
2. kryo & fst serialization
3. embed tomcat
This commit is contained in:
张亮 2018-01-02 16:33:40 +08:00 committed by ken.lj
parent 72cecba20b
commit ab8af672e6
63 changed files with 3066 additions and 22 deletions

View File

@ -57,6 +57,18 @@ limitations under the License.
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
</dependency>
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
</dependency>
<dependency>
<groupId>org.jvnet.sorcerer</groupId>
<artifactId>sorcerer-javac</artifactId>

View File

@ -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";

View File

@ -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();
}

View File

@ -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 {
}

View File

@ -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<Class> registrations = new LinkedHashSet<Class>();
/**
* only supposed to be called at startup time
*/
public static void registerClass(Class clazz) {
registrations.add(clazz);
}
public static Set<Class> getRegisteredClasses() {
return registrations;
}
}

View File

@ -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<Class> getSerializableClasses();
}

View File

@ -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);
}
}

View File

@ -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> T readObject(Class<T> clazz) throws IOException, ClassNotFoundException {
return (T) readObject();
}
@SuppressWarnings("unchecked")
public <T> T readObject(Class<T> clazz, Type type) throws IOException, ClassNotFoundException {
return (T) readObject();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<Class> registrations = new LinkedHashSet<Class>();
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();
}

View File

@ -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> T readObject(Class<T> 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> T readObject(Class<T> 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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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<Kryo> pool = new ConcurrentLinkedQueue<Kryo>();
@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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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<Kryo> holder = new ThreadLocal<Kryo>() {
@Override
protected Kryo initialValue() {
return createKryo();
}
};
public Kryo getKryo() {
return holder.get();
}
}

View File

@ -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
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

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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() {
}
}
}

View File

@ -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<String, String> 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();

View File

@ -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<T> 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<T> extends AbstractServiceConfig {
}
}
protected Class getServiceClass(T ref) {
return ref.getClass();
}
/**
* Register & bind IP address for service provider, can be configured separately.

View File

@ -804,6 +804,21 @@
<xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="keepalive" type="xsd:boolean" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The protocol keepAlive. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="optimizer" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The serialization optimizer. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="extension" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The extension for protocol. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="charset" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>

View File

@ -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();

View File

@ -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 {

View File

@ -39,5 +39,13 @@
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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<Integer, ServletContext> contextMap = new ConcurrentHashMap<Integer, ServletContext>();
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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -1,2 +1,3 @@
servlet=com.alibaba.dubbo.remoting.http.servlet.ServletHttpBinder
jetty=com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder
jetty=com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder
tomcat=com.alibaba.dubbo.remoting.http.tomcat.TomcatHttpBinder

View File

@ -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<Invoker<?>> 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.
*

View File

@ -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<Class> holder = new ThreadLocal<Class>();
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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>
private final Map<String, ReferenceCountExchangeClient> referenceClientMap = new ConcurrentHashMap<String, ReferenceCountExchangeClient>(); // <host:port,Exchanger>
private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap = new ConcurrentHashMap<String, LazyConnectExchangeClient>();
private final Set<String> optimizers = new ConcurrentHashSet<String>();
//consumer side export a stub service for dispatching event
//servicekey-stubmethods
private final ConcurrentMap<String, String> stubServiceMethodsMap = new ConcurrentHashMap<String, String>();
@ -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 <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);

View File

@ -0,0 +1,103 @@
<!--
- Copyright 1999-2011 Alibaba Group.
-
- 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc</artifactId>
<version>2.5.8</version>
</parent>
<artifactId>dubbo-rpc-rest</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The JAX-RS rpc module of dubbo project</description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-remoting-http</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!-- optional dependencies ==================== -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jdk-http</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.jboss.resteasy</groupId>-->
<!--<artifactId>tjws</artifactId>-->
<!--<version>3.0.7.Final</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.jboss.resteasy</groupId>-->
<!--<artifactId>resteasy-undertow</artifactId>-->
<!--<version>3.0.7.Final</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>io.undertow</groupId>-->
<!--<artifactId>undertow-servlet</artifactId>-->
<!--<version>1.0.1.Final</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>io.undertow</groupId>-->
<!--<artifactId>undertow-core</artifactId>-->
<!--<version>1.0.1.Final</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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);
}

View File

@ -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;
}
};
}
}
}

View File

@ -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;
// }
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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<String, RestServer> servers = new ConcurrentHashMap<String, RestServer>();
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<ResteasyClient> clients = Collections.synchronizedList(new LinkedList<ResteasyClient>());
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 <T> Runnable doExport(T impl, Class<T> 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> T doRefer(Class<T> 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<String, RestServer> 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<ClientConnectionManager> connectionManagers = Collections.synchronizedList(new LinkedList<ClientConnectionManager>());
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();
}
}
}
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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<String, String> 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());
}
}
}

View File

@ -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<RpcException> {
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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
// }
}

View File

@ -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<RestConstraintViolation> constraintViolations;
public List<RestConstraintViolation> getConstraintViolations() {
return constraintViolations;
}
public void setConstraintViolations(List<RestConstraintViolation> constraintViolations) {
this.constraintViolations = constraintViolations;
}
public void addConstraintViolation(RestConstraintViolation constraintViolation) {
if (constraintViolations == null) {
constraintViolations = new LinkedList<RestConstraintViolation>();
}
constraintViolations.add(constraintViolation);
}
}

View File

@ -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";
}

View File

@ -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<String, String> headers) {
StringBuilder msg = new StringBuilder("The HTTP headers are: \n");
for (Map.Entry<String, List<String>> 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();
}
}
}

View File

@ -0,0 +1 @@
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol

View File

@ -40,5 +40,6 @@ limitations under the License.
<module>dubbo-rpc-thrift</module>
<module>dubbo-rpc-memcached</module>
<module>dubbo-rpc-redis</module>
<module>dubbo-rpc-rest</module>
</modules>
</project>

View File

@ -187,6 +187,53 @@ limitations under the License.
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
<version>${project.parent.version}</version>
<exclusions>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jdk-http</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-undertow</artifactId>
</exclusion>
<exclusion>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
</exclusion>
<exclusion>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
</exclusion>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-default</artifactId>

66
pom.xml
View File

@ -113,6 +113,12 @@ limitations under the License.
<cglib_version>2.2</cglib_version>
<webx_version>3.1.6</webx_version>
<velocity_version>1.7</velocity_version>
<kryo_version>2.24.0</kryo_version>
<kryo_serializers_version>0.26</kryo_serializers_version>
<fst_version>1.55</fst_version>
<rs_api_version>2.0</rs_api_version>
<resteasy_version>3.0.7.Final</resteasy_version>
<tomcat_embed_version>8.0.11</tomcat_embed_version>
<!-- Log libs -->
<slf4j_version>1.7.25</slf4j_version>
<jcl_version>1.2</jcl_version>
@ -303,6 +309,66 @@ limitations under the License.
<artifactId>velocity</artifactId>
<version>${velocity_version}</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
<version>${kryo_version}</version>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>${kryo_serializers_version}</version>
</dependency>
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
<version>${fst_version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>${rs_api_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty</artifactId>
<version>${resteasy_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jdk-http</artifactId>
<version>${resteasy_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>${resteasy_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>${resteasy_version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat_embed_version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>${tomcat_embed_version}</version>
</dependency>
<!-- Log libs -->
<dependency>
<groupId>org.slf4j</groupId>