Skip to content

Commit fb80651

Browse files
jansupolsenivam
authored andcommitted
Make Kryo use setRegistrationRequired(true) by default
Signed-off-by: jansupol <[email protected]>
1 parent ae1631d commit fb80651

File tree

9 files changed

+226
-63
lines changed

9 files changed

+226
-63
lines changed

incubator/kryo/src/main/java/org/glassfish/jersey/kryo/KryoFeature.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,21 +22,58 @@
2222

2323
import org.glassfish.jersey.Beta;
2424
import org.glassfish.jersey.kryo.internal.KryoMessageBodyProvider;
25+
import org.glassfish.jersey.kryo.internal.RegistrationNotRequiredKryoContextResolver;
2526

2627
/**
28+
* <p>
2729
* Feature used to register Kryo providers.
30+
* </p>
31+
* <p>
32+
* For the security reasons, Kryo#setRegistrationRequired(true) should be specified.
33+
* Unless {@code KryoFeature#registrationRequired(false)} is registered, a {@code ContextResolver<Kryo>} should be registered.
34+
* There the user is expected to create new {@code Kryo} instance with the registrations:
35+
* <pre>
36+
* public Kryo getContext(Class<?> type) {
37+
* ...
38+
* Kryo kryo = new Kryo();
39+
* kryo.setRegistrationRequired(true);
40+
* kryo.register(The_class_for_which_the_KryoMessageBodyProvider_should_be_allowed);
41+
* ...
42+
* return kryo;
43+
* }
44+
* </pre>
45+
* Note that {@code ContextResolver#getContext} is invoked just once when creating {@code KryoPool} and the {@code type} argument
46+
* is {@code null}.
47+
* </p>
2848
*
2949
* @author Libor Kramolis
3050
*/
3151
@Beta
3252
public class KryoFeature implements Feature {
3353

54+
private final boolean registrationRequired;
55+
56+
public KryoFeature() {
57+
registrationRequired = true;
58+
}
59+
60+
public static KryoFeature registrationRequired(boolean registrationRequired) {
61+
return new KryoFeature(registrationRequired);
62+
}
63+
64+
private KryoFeature(boolean registrationRequired) {
65+
this.registrationRequired = registrationRequired;
66+
}
67+
3468
@Override
3569
public boolean configure(final FeatureContext context) {
3670
final Configuration config = context.getConfiguration();
3771

3872
if (!config.isRegistered(KryoMessageBodyProvider.class)) {
3973
context.register(KryoMessageBodyProvider.class);
74+
if (!registrationRequired) {
75+
context.register(RegistrationNotRequiredKryoContextResolver.class);
76+
}
4077
}
4178

4279
return true;

incubator/kryo/src/main/java/org/glassfish/jersey/kryo/internal/KryoMessageBodyProvider.java

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,15 +21,20 @@
2121
import java.io.OutputStream;
2222
import java.lang.annotation.Annotation;
2323
import java.lang.reflect.Type;
24+
import java.util.Optional;
2425

2526
import javax.ws.rs.Consumes;
2627
import javax.ws.rs.Produces;
2728
import javax.ws.rs.WebApplicationException;
29+
import javax.ws.rs.core.Configuration;
30+
import javax.ws.rs.core.Context;
2831
import javax.ws.rs.core.MediaType;
2932
import javax.ws.rs.core.MultivaluedMap;
33+
import javax.ws.rs.ext.ContextResolver;
3034
import javax.ws.rs.ext.MessageBodyReader;
3135
import javax.ws.rs.ext.MessageBodyWriter;
3236
import javax.ws.rs.ext.Provider;
37+
import javax.ws.rs.ext.Providers;
3338

3439
import com.esotericsoftware.kryo.Kryo;
3540
import com.esotericsoftware.kryo.io.Input;
@@ -39,25 +44,35 @@
3944
import com.esotericsoftware.kryo.pool.KryoPool;
4045

4146
/**
47+
* The KryoMessageBodyProvider expects a {@code ContextResolver<Kryo>} registered.
4248
* @author Libor Kramolis
4349
*/
4450
@Provider
4551
@Consumes("application/x-kryo")
4652
@Produces("application/x-kryo")
4753
public class KryoMessageBodyProvider implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
4854

49-
private final KryoPool kryoPool;
55+
private final ContextResolver<Kryo> contextResolver;
56+
private final Optional<KryoPool> kryoPool;
5057

51-
public KryoMessageBodyProvider() {
58+
public KryoMessageBodyProvider(@Context Providers providers) {
59+
final MediaType mediaType = new MediaType("application", "x-kryo");
60+
contextResolver = providers.getContextResolver(Kryo.class, mediaType);
61+
kryoPool = getKryoPool();
62+
}
63+
64+
private Kryo getKryo() {
65+
return contextResolver != null ? contextResolver.getContext(null) : null;
66+
}
67+
68+
private Optional<KryoPool> getKryoPool() {
69+
final Kryo kryo = getKryo();
5270
final KryoFactory kryoFactory = new KryoFactory() {
5371
public Kryo create() {
54-
final Kryo kryo = new Kryo();
55-
//TODO: configure kryo instance, customize settings
56-
//TODO: e.g. looking for Kryo via ContextResolver (like Jackson)
5772
return kryo;
5873
}
5974
};
60-
kryoPool = new KryoPool.Builder(kryoFactory).softReferences().build();
75+
return kryo == null ? Optional.empty() : Optional.of(new KryoPool.Builder(kryoFactory).softReferences().build());
6176
}
6277

6378
//
@@ -73,7 +88,7 @@ public long getSize(final Object object, final Class<?> type, final Type generic
7388
@Override
7489
public boolean isWriteable(final Class<?> type, final Type genericType,
7590
final Annotation[] annotations, final MediaType mediaType) {
76-
return true;
91+
return kryoPool != null;
7792
}
7893

7994
@Override
@@ -82,12 +97,12 @@ public void writeTo(final Object object, final Class<?> type, final Type generic
8297
final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
8398
throws IOException, WebApplicationException {
8499
final Output output = new Output(entityStream);
85-
kryoPool.run(new KryoCallback() {
100+
kryoPool.ifPresent(a -> a.run(new KryoCallback() {
86101
public Object execute(Kryo kryo) {
87102
kryo.writeObject(output, object);
88103
return null;
89104
}
90-
});
105+
}));
91106
output.flush();
92107
}
93108

@@ -98,7 +113,7 @@ public Object execute(Kryo kryo) {
98113
@Override
99114
public boolean isReadable(final Class<?> type, final Type genericType,
100115
final Annotation[] annotations, final MediaType mediaType) {
101-
return true;
116+
return kryoPool != null;
102117
}
103118

104119
@Override
@@ -108,11 +123,11 @@ public Object readFrom(final Class<Object> type, final Type genericType,
108123
final InputStream entityStream) throws IOException, WebApplicationException {
109124
final Input input = new Input(entityStream);
110125

111-
return kryoPool.run(new KryoCallback() {
126+
return kryoPool.map(pool -> pool.run(new KryoCallback() {
112127
public Object execute(Kryo kryo) {
113128
return kryo.readObject(input, type);
114129
}
115-
});
130+
})).orElse(null);
116131
}
117132

118133
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.kryo.internal;
18+
19+
import com.esotericsoftware.kryo.Kryo;
20+
21+
import javax.ws.rs.ext.ContextResolver;
22+
23+
/**
24+
* Backwards compatibility ContextResolver.
25+
* It should only be used when the user specifically agrees on a vulnerability provided when the
26+
* <code>KryoFeature#registrationRequired(false)</code> is used.
27+
* The default behaviour is demanded to require {@code ContextResolver} with
28+
* <pre>
29+
* public Kryo getContext(Class<?> type) {
30+
* ...
31+
* Kryo kryo = new Kryo();
32+
* kryo.setRegistrationRequired(true);
33+
* kryo.register(The_class_for_which_the_KryoMessageBodyProvider_should_be_allowed);
34+
* ...
35+
* return kryo;
36+
* }
37+
* </pre>
38+
*/
39+
public class RegistrationNotRequiredKryoContextResolver implements ContextResolver<Kryo> {
40+
@Override
41+
public Kryo getContext(Class<?> type) {
42+
return new Kryo();
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,25 +16,16 @@
1616

1717
package org.glassfish.jersey.kryo;
1818

19-
import java.util.HashSet;
20-
import java.util.Set;
19+
import com.esotericsoftware.kryo.Kryo;
2120

22-
import javax.ws.rs.core.Application;
23-
24-
/**
25-
* Test case JAX-RS application.
26-
*
27-
* @author Libor Kramolis
28-
*/
29-
public class JaxRsApplication extends Application {
30-
31-
static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
32-
add(PersonResource.class);
33-
}};
21+
import javax.ws.rs.ext.ContextResolver;
3422

23+
public class KryoContextResolver implements ContextResolver<Kryo> {
3524
@Override
36-
public Set<Class<?>> getClasses() {
37-
return APP_CLASSES;
25+
public Kryo getContext(Class<?> type) {
26+
Kryo kryo = new Kryo();
27+
kryo.setRegistrationRequired(true);
28+
kryo.register(Person.class);
29+
return kryo;
3830
}
39-
4031
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.kryo;
18+
19+
import org.glassfish.jersey.test.JerseyTest;
20+
import org.junit.Test;
21+
22+
import javax.ws.rs.client.Entity;
23+
import javax.ws.rs.core.Response;
24+
25+
import static org.junit.Assert.assertEquals;
26+
27+
public abstract class PersonResourceBaseTest extends JerseyTest {
28+
29+
@Test
30+
public void testGet() {
31+
final Person getResponse = target().request().get(Person.class);
32+
assertEquals("Wolfgang", getResponse.name);
33+
assertEquals(21, getResponse.age);
34+
assertEquals("Salzburg", getResponse.address);
35+
}
36+
37+
@Test
38+
public void testPost() {
39+
final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth")};
40+
for (Person original : testData) {
41+
final Person postResponse = target().request()
42+
.post(Entity.entity(original, "application/x-kryo"), Person.class);
43+
assertEquals(original, postResponse);
44+
}
45+
}
46+
47+
@Test
48+
public void testPut() {
49+
final Response putResponse = target().request().put(Entity.entity(new Person("Jules", 12, "Paris"),
50+
"application/x-kryo"));
51+
assertEquals(204, putResponse.getStatus());
52+
}
53+
54+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.kryo;
18+
19+
import org.glassfish.jersey.client.ClientConfig;
20+
import org.glassfish.jersey.server.ResourceConfig;
21+
import org.glassfish.jersey.test.TestProperties;
22+
23+
import javax.ws.rs.core.Application;
24+
25+
public class PersonResourceRegistrationNotRequiredTest extends PersonResourceBaseTest {
26+
@Override
27+
protected Application configure() {
28+
enable(TestProperties.LOG_TRAFFIC);
29+
enable(TestProperties.DUMP_ENTITY);
30+
return new ResourceConfig().register(PersonResource.class).register(KryoFeature.registrationRequired(false));
31+
}
32+
33+
@Override
34+
protected void configureClient(final ClientConfig config) {
35+
config.register(KryoFeature.registrationRequired(false));
36+
}
37+
}

0 commit comments

Comments
 (0)