Skip to content

Commit f6c3812

Browse files
committed
Add support for return type params
1 parent cd4f54f commit f6c3812

File tree

5 files changed

+227
-33
lines changed

5 files changed

+227
-33
lines changed

cdt-java-client/src/main/java/com/github/kklisura/cdt/services/ChromeDevToolsService.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,41 @@
3232
* @author Kenan Klisura
3333
*/
3434
public interface ChromeDevToolsService extends ChromeDevTools, AutoCloseable {
35+
3536
/**
36-
* Invokes a dev tools method.
37+
* Invokes a dev tools method. If your return types is a generics please use {@link
38+
* #invoke(String, Class, Class[], MethodInvocation)}.
39+
*
40+
* <p>This method is deprecated and might be removed in future.
3741
*
3842
* @param returnProperty Return property.
3943
* @param clazz Return class type.
4044
* @param methodInvocation Method invocation definition.
4145
* @param <T> Type of a return class.
4246
* @return Return object.
4347
* @throws ChromeDevToolsInvocationException If invocation fails.
48+
* @deprecated Please use {@link #invoke(String, Class, Class[], MethodInvocation)}
4449
*/
50+
@Deprecated
4551
<T> T invoke(String returnProperty, Class<T> clazz, MethodInvocation methodInvocation);
4652

53+
/**
54+
* Invokes a dev tools method.
55+
*
56+
* @param returnProperty Return property.
57+
* @param clazz Return class type.
58+
* @param returnTypeClasses List of class to which returnType is parametrized with.
59+
* @param methodInvocation Method invocation definition.
60+
* @param <T> Type of a return class.
61+
* @return Return object.
62+
* @throws ChromeDevToolsInvocationException If invocation fails.
63+
*/
64+
<T> T invoke(
65+
String returnProperty,
66+
Class<T> clazz,
67+
Class<?>[] returnTypeClasses,
68+
MethodInvocation methodInvocation);
69+
4770
/** Closes the dev tools service. */
4871
void close();
4972

cdt-java-client/src/main/java/com/github/kklisura/cdt/services/impl/ChromeDevToolsServiceImpl.java

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222

2323
import com.fasterxml.jackson.annotation.JsonInclude;
2424
import com.fasterxml.jackson.databind.DeserializationFeature;
25+
import com.fasterxml.jackson.databind.JavaType;
2526
import com.fasterxml.jackson.databind.JsonNode;
2627
import com.fasterxml.jackson.databind.ObjectMapper;
28+
import com.fasterxml.jackson.databind.type.TypeFactory;
2729
import com.github.kklisura.cdt.protocol.support.types.EventHandler;
2830
import com.github.kklisura.cdt.protocol.support.types.EventListener;
2931
import com.github.kklisura.cdt.services.ChromeDevToolsService;
@@ -38,11 +40,7 @@
3840
import com.github.kklisura.cdt.services.utils.ProxyUtils;
3941
import java.io.IOException;
4042
import java.lang.reflect.InvocationHandler;
41-
import java.util.Collections;
42-
import java.util.HashMap;
43-
import java.util.HashSet;
44-
import java.util.Map;
45-
import java.util.Set;
43+
import java.util.*;
4644
import java.util.concurrent.ConcurrentHashMap;
4745
import java.util.concurrent.CountDownLatch;
4846
import java.util.concurrent.TimeUnit;
@@ -128,6 +126,15 @@ public void setChromeTab(ChromeTab chromeTab) {
128126

129127
@Override
130128
public <T> T invoke(String returnProperty, Class<T> clazz, MethodInvocation methodInvocation) {
129+
return invoke(returnProperty, clazz, null, methodInvocation);
130+
}
131+
132+
@Override
133+
public <T> T invoke(
134+
String returnProperty,
135+
Class<T> clazz,
136+
Class<?>[] returnTypeClasses,
137+
MethodInvocation methodInvocation) {
131138
try {
132139
InvocationResult invocationResult = new InvocationResult(returnProperty);
133140
invocationResultMap.put(methodInvocation.getId(), invocationResult);
@@ -148,7 +155,11 @@ public <T> T invoke(String returnProperty, Class<T> clazz, MethodInvocation meth
148155
return null;
149156
}
150157

151-
return readJsonObject(clazz, invocationResult.getResult());
158+
if (returnTypeClasses != null) {
159+
return readJsonObject(returnTypeClasses, clazz, invocationResult.getResult());
160+
} else {
161+
return readJsonObject(clazz, invocationResult.getResult());
162+
}
152163
} else {
153164
ErrorObject error = readJsonObject(ErrorObject.class, invocationResult.getResult());
154165
StringBuilder errorMessageBuilder = new StringBuilder(error.getMessage());
@@ -302,6 +313,39 @@ private void handleEvent(String name, JsonNode params) {
302313
}
303314
}
304315

316+
private <T> T readJsonObject(
317+
Class<?>[] classParameters, Class<T> parameterizedClazz, JsonNode jsonNode)
318+
throws IOException {
319+
if (jsonNode == null) {
320+
throw new ChromeDevToolsInvocationException(
321+
"Failed converting null response to clazz " + parameterizedClazz.getName());
322+
}
323+
324+
final TypeFactory typeFactory = OBJECT_MAPPER.getTypeFactory();
325+
JavaType javaType = null;
326+
327+
if (classParameters.length > 1) {
328+
for (int i = classParameters.length - 2; i >= 0; i--) {
329+
if (javaType == null) {
330+
javaType =
331+
typeFactory.constructParametricType(classParameters[i], classParameters[i + 1]);
332+
} else {
333+
javaType = typeFactory.constructParametricType(classParameters[i], javaType);
334+
}
335+
}
336+
337+
javaType =
338+
OBJECT_MAPPER.getTypeFactory().constructParametricType(parameterizedClazz, javaType);
339+
} else {
340+
javaType =
341+
OBJECT_MAPPER
342+
.getTypeFactory()
343+
.constructParametricType(parameterizedClazz, classParameters[0]);
344+
}
345+
346+
return OBJECT_MAPPER.readerFor(javaType).readValue(jsonNode);
347+
}
348+
305349
private <T> T readJsonObject(Class<T> clazz, JsonNode jsonNode) throws IOException {
306350
if (jsonNode == null) {
307351
throw new ChromeDevToolsInvocationException(

cdt-java-client/src/main/java/com/github/kklisura/cdt/services/invocation/CommandInvocationHandler.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.github.kklisura.cdt.protocol.support.annotations.EventName;
2424
import com.github.kklisura.cdt.protocol.support.annotations.ParamName;
25+
import com.github.kklisura.cdt.protocol.support.annotations.ReturnTypeParameter;
2526
import com.github.kklisura.cdt.protocol.support.annotations.Returns;
2627
import com.github.kklisura.cdt.protocol.support.types.EventHandler;
2728
import com.github.kklisura.cdt.protocol.support.types.EventListener;
@@ -70,14 +71,21 @@ public Object invoke(Object unused, Method method, Object[] args) throws Throwab
7071

7172
Class<?> returnType = method.getReturnType();
7273

74+
Class<?>[] returnTypeClasses = null;
75+
ReturnTypeParameter returnTypeParameter = method.getAnnotation(ReturnTypeParameter.class);
76+
if (returnTypeParameter != null) {
77+
returnTypeClasses = returnTypeParameter.value();
78+
}
79+
7380
String returnProperty = null;
7481
Returns returnsAnnotation = method.getAnnotation(Returns.class);
7582
if (returnsAnnotation != null) {
7683
returnProperty = returnsAnnotation.value();
7784
}
7885

7986
MethodInvocation methodInvocation = createMethodInvocation(method, args);
80-
return chromeDevToolsService.invoke(returnProperty, returnType, methodInvocation);
87+
return chromeDevToolsService.invoke(
88+
returnProperty, returnType, returnTypeClasses, methodInvocation);
8189
}
8290

8391
/**

cdt-java-client/src/test/java/com/github/kklisura/cdt/services/impl/ChromeDevToolsServiceImplTest.java

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,8 @@
2020
* #L%
2121
*/
2222

23-
import static org.easymock.EasyMock.anyObject;
24-
import static org.easymock.EasyMock.capture;
25-
import static org.easymock.EasyMock.expectLastCall;
26-
import static org.easymock.EasyMock.replay;
27-
import static org.easymock.EasyMock.verify;
28-
import static org.junit.Assert.assertEquals;
29-
import static org.junit.Assert.assertFalse;
30-
import static org.junit.Assert.assertNotNull;
31-
import static org.junit.Assert.assertNull;
32-
import static org.junit.Assert.assertTrue;
23+
import static org.easymock.EasyMock.*;
24+
import static org.junit.Assert.*;
3325

3426
import com.fasterxml.jackson.databind.ObjectMapper;
3527
import com.github.kklisura.cdt.protocol.support.types.EventHandler;
@@ -45,6 +37,7 @@
4537
import com.github.kklisura.cdt.services.utils.ProxyUtils;
4638
import java.io.IOException;
4739
import java.util.HashMap;
40+
import java.util.List;
4841
import org.easymock.Capture;
4942
import org.easymock.EasyMockRunner;
5043
import org.easymock.EasyMockSupport;
@@ -122,7 +115,7 @@ public void testInvokeVoidMethodThrowsWebSocketException()
122115

123116
ChromeDevToolsInvocationException capturedException = null;
124117
try {
125-
service.invoke(null, Void.TYPE, methodInvocation);
118+
service.invoke(null, Void.TYPE, null, methodInvocation);
126119
} catch (ChromeDevToolsInvocationException ex) {
127120
capturedException = ex;
128121
}
@@ -167,7 +160,7 @@ public void testInvokeVoidMethodInterruptsWaiting()
167160

168161
ChromeDevToolsInvocationException capturedException = null;
169162
try {
170-
service.invoke(null, Void.TYPE, methodInvocation);
163+
service.invoke(null, Void.TYPE, null, methodInvocation);
171164
} catch (ChromeDevToolsInvocationException ex) {
172165
capturedException = ex;
173166
}
@@ -198,7 +191,7 @@ public void testInvokeVoidMethod() throws WebSocketServiceException, IOException
198191
replayAll();
199192

200193
resolveMessage("{\"id\":1,\"result\":{}}");
201-
assertNull(service.invoke(null, Void.TYPE, methodInvocation));
194+
assertNull(service.invoke(null, Void.TYPE, null, methodInvocation));
202195

203196
verifyAll();
204197

@@ -223,7 +216,86 @@ public void testInvokeStringMethod() throws WebSocketServiceException, IOExcepti
223216
replayAll();
224217

225218
resolveMessage("{\"id\":1,\"result\":{\"resultProperty\":\"resultValue\"}}");
226-
assertEquals("resultValue", service.invoke("resultProperty", String.class, methodInvocation));
219+
assertEquals(
220+
"resultValue", service.invoke("resultProperty", String.class, null, methodInvocation));
221+
222+
verifyAll();
223+
224+
MethodInvocation sentInvocation =
225+
OBJECT_MAPPER.readerFor(MethodInvocation.class).readValue(messageCapture.getValue());
226+
assertEquals(methodInvocation.getId(), sentInvocation.getId());
227+
assertEquals(methodInvocation.getMethod(), sentInvocation.getMethod());
228+
assertEquals(methodInvocation.getParams(), sentInvocation.getParams());
229+
}
230+
231+
@Test
232+
public void testInvokeMethodReturningListOfComplexObjects()
233+
throws WebSocketServiceException, IOException {
234+
MethodInvocation methodInvocation = new MethodInvocation();
235+
methodInvocation.setId(1L);
236+
methodInvocation.setMethod("SomeMethod");
237+
methodInvocation.setParams(new HashMap<>());
238+
methodInvocation.getParams().put("param", "value");
239+
240+
Capture<String> messageCapture = Capture.newInstance();
241+
webSocketService.send(capture(messageCapture));
242+
243+
replayAll();
244+
245+
resolveMessage("{\"id\":1,\"result\":[{\"testProperty\":\"1\"},{\"testProperty\":\"2\"}]}");
246+
247+
List<TestMessage> result =
248+
(List<TestMessage>)
249+
service.invoke(null, List.class, new Class[] {TestMessage.class}, methodInvocation);
250+
251+
assertNotNull(result);
252+
assertEquals(2, result.size());
253+
assertNotNull(result.get(0));
254+
assertNotNull(result.get(1));
255+
assertEquals("1", result.get(0).getTestProperty());
256+
assertEquals("2", result.get(1).getTestProperty());
257+
258+
verifyAll();
259+
260+
MethodInvocation sentInvocation =
261+
OBJECT_MAPPER.readerFor(MethodInvocation.class).readValue(messageCapture.getValue());
262+
assertEquals(methodInvocation.getId(), sentInvocation.getId());
263+
assertEquals(methodInvocation.getMethod(), sentInvocation.getMethod());
264+
assertEquals(methodInvocation.getParams(), sentInvocation.getParams());
265+
}
266+
267+
@Test
268+
public void testInvokeMethodReturningListOfComplexObjects2()
269+
throws WebSocketServiceException, IOException {
270+
MethodInvocation methodInvocation = new MethodInvocation();
271+
methodInvocation.setId(1L);
272+
methodInvocation.setMethod("SomeMethod");
273+
methodInvocation.setParams(new HashMap<>());
274+
methodInvocation.getParams().put("param", "value");
275+
276+
Capture<String> messageCapture = Capture.newInstance();
277+
webSocketService.send(capture(messageCapture));
278+
279+
replayAll();
280+
281+
resolveMessage("{\"id\":1,\"result\":[[{\"testProperty\":\"1\"},{\"testProperty\":\"2\"}]]}");
282+
283+
List<List<TestMessage>> resultWrapper =
284+
(List<List<TestMessage>>)
285+
service.invoke(
286+
null, List.class, new Class[] {List.class, TestMessage.class}, methodInvocation);
287+
288+
assertNotNull(resultWrapper);
289+
assertEquals(1, resultWrapper.size());
290+
291+
List<TestMessage> result = resultWrapper.get(0);
292+
293+
assertNotNull(result);
294+
assertEquals(2, result.size());
295+
assertNotNull(result.get(0));
296+
assertNotNull(result.get(1));
297+
assertEquals("1", result.get(0).getTestProperty());
298+
assertEquals("2", result.get(1).getTestProperty());
227299

228300
verifyAll();
229301

@@ -248,7 +320,7 @@ public void testInvokeStringMethodWithNullResult() throws WebSocketServiceExcept
248320
replayAll();
249321

250322
resolveMessage("{\"id\":1}");
251-
service.invoke("resultProperty", String.class, methodInvocation);
323+
service.invoke("resultProperty", String.class, null, methodInvocation);
252324
}
253325

254326
@Test
@@ -266,7 +338,7 @@ public void testInvokeTestMessageMethod() throws WebSocketServiceException, IOEx
266338

267339
resolveMessage(
268340
"{\"id\":1,\"result\":{\"testProperty\":\"resultValue\",\"testProperty2\":\"resultValue2\"}}");
269-
TestMessage testMessage = service.invoke(null, TestMessage.class, methodInvocation);
341+
TestMessage testMessage = service.invoke(null, TestMessage.class, null, methodInvocation);
270342

271343
verifyAll();
272344

@@ -296,7 +368,7 @@ public void testInvokeTestMessageMethodWithUnknownProperty()
296368

297369
resolveMessage(
298370
"{\"id\":1,\"result\":{\"testProperty\":\"resultValue\",\"testProperty2\":\"resultValue2\",\"unknownProperty\":false}}");
299-
TestMessage testMessage = service.invoke(null, TestMessage.class, methodInvocation);
371+
TestMessage testMessage = service.invoke(null, TestMessage.class, null, methodInvocation);
300372

301373
verifyAll();
302374

@@ -346,7 +418,7 @@ public void testInvokeTestMessageMethodWithBadJson()
346418
"{\"id\":1,\"result\":{\"testProperty\":\"resultValue\",\"testProperty2\"-\"resultValue2\"}}");
347419
ChromeDevToolsInvocationException capturedException = null;
348420
try {
349-
service.invoke(null, Void.TYPE, methodInvocation);
421+
service.invoke(null, Void.TYPE, null, methodInvocation);
350422
} catch (ChromeDevToolsInvocationException ex) {
351423
capturedException = ex;
352424
}
@@ -381,7 +453,7 @@ public void testInvokeVoidMethodWithError() throws WebSocketServiceException, IO
381453

382454
ChromeDevToolsInvocationException capturedException = null;
383455
try {
384-
service.invoke(null, Void.TYPE, methodInvocation);
456+
service.invoke(null, Void.TYPE, null, methodInvocation);
385457
} catch (ChromeDevToolsInvocationException ex) {
386458
capturedException = ex;
387459
}
@@ -412,7 +484,7 @@ public void testInvokeVoidMethodWithErrorNoTestData()
412484

413485
ChromeDevToolsInvocationException capturedException = null;
414486
try {
415-
service.invoke(null, Void.TYPE, methodInvocation);
487+
service.invoke(null, Void.TYPE, null, methodInvocation);
416488
} catch (ChromeDevToolsInvocationException ex) {
417489
capturedException = ex;
418490
}
@@ -591,7 +663,7 @@ public void testEventReceivedWithUnsubscribeOnService() throws InterruptedExcept
591663
}
592664

593665
@Test
594-
public void testEventReceivedHandlerThrowsExeption() throws InterruptedException {
666+
public void testEventReceivedHandlerThrowsException() throws InterruptedException {
595667
// Non existing event handler
596668
service.accept("{\"method\":\"Domain.name\",\"params\":{\"testProperty\":\"testValue\"}}");
597669

0 commit comments

Comments
 (0)