aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.classpath9
-rw-r--r--.gitignore5
-rw-r--r--.project15
-rw-r--r--.settings/org.eclipse.core.resources.prefs3
-rw-r--r--.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--.settings/org.eclipse.jdt.ui.prefs9
-rw-r--r--COPYING202
-rw-r--r--Makefile66
-rw-r--r--README152
-rw-r--r--README_ECLIPSE18
-rw-r--r--lib/commons-codec.jarbin0 -> 46725 bytes
-rw-r--r--lib/gson.jarbin0 -> 138568 bytes
-rw-r--r--src/com/google/gwtjsonrpc/GWTJSONRPC.gwt.xml24
-rw-r--r--src/com/google/gwtjsonrpc/client/AbstractJsonProxy.java50
-rw-r--r--src/com/google/gwtjsonrpc/client/ArraySerializer.java70
-rw-r--r--src/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java36
-rw-r--r--src/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java48
-rw-r--r--src/com/google/gwtjsonrpc/client/JsonCall.java141
-rw-r--r--src/com/google/gwtjsonrpc/client/JsonSerializer.java69
-rw-r--r--src/com/google/gwtjsonrpc/client/ListSerializer.java71
-rw-r--r--src/com/google/gwtjsonrpc/client/RemoteJsonService.java51
-rw-r--r--src/com/google/gwtjsonrpc/client/SetSerializer.java71
-rw-r--r--src/com/google/gwtjsonrpc/client/Shared.java35
-rw-r--r--src/com/google/gwtjsonrpc/rebind/ProxyCreator.java281
-rw-r--r--src/com/google/gwtjsonrpc/rebind/RemoteJsonServiceProxyGenerator.java58
-rw-r--r--src/com/google/gwtjsonrpc/rebind/SerializerCreator.java497
-rw-r--r--src/com/google/gwtjsonrpc/server/ActiveCall.java68
-rw-r--r--src/com/google/gwtjsonrpc/server/JsonServlet.java305
-rw-r--r--src/com/google/gwtjsonrpc/server/MethodHandle.java88
-rw-r--r--src/com/google/gwtjsonrpc/server/NoSuchRemoteMethodException.java19
-rw-r--r--src/com/google/gwtjsonrpc/server/RpcRequestDeserializer.java92
-rw-r--r--src/com/google/gwtjsonrpc/server/XsrfException.java26
-rw-r--r--src/com/google/gwtjsonrpc/server/XsrfUtil.java215
34 files changed, 3065 insertions, 0 deletions
diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..37472fd
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/GWT"/>
+ <classpathentry kind="lib" path="lib/gson.jar" sourcepath="lib/gson_src.zip"/>
+ <classpathentry kind="lib" path="lib/commons-codec.jar"/>
+ <classpathentry kind="output" path="classes"/>
+</classpath>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..12e933d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/.bin
+/classes
+/lib/*_src.zip
+/lib/gwtjsonrpc.jar
+/config.mak
diff --git a/.project b/.project
new file mode 100644
index 0000000..44da701
--- /dev/null
+++ b/.project
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<projectDescription>
+ <name>GwtJsonRpc</name>
+ <comment>GWT-JSON-RPC</comment>
+ <projects/>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments/>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..82eb859
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..8667cfd
--- /dev/null
+++ b/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..fa64d21
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Thu Sep 04 11:18:51 PDT 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..f37f6f0
--- /dev/null
+++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,9 @@
+#Tue Sep 02 17:00:18 PDT 2008
+eclipse.preferences.version=1
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..885f65b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,66 @@
+# JSON-RPC for Google Web Toolkit
+#
+# Copyright 2008 Google Inc.
+#
+# 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.
+#
+# Define GWT_SDK to the location of the Google Web Toolkit SDK.
+#
+# Define GWT_OS to your operating system, e.g. 'linux', 'mac'.
+#
+
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+
+JAVAC = javac
+GWT_OS = unknown
+
+ifeq ($(uname_S),Darwin)
+ GWT_OS = mac
+endif
+ifeq ($(uname_S),Linux)
+ GWT_OS = linux
+endif
+ifeq ($(uname_S),Cygwin)
+ GWT_OS = win
+endif
+
+-include config.mak
+
+GWT_CP = $(GWT_SDK)/gwt-user.jar:$(GWT_SDK)/gwt-dev-$(GWT_OS).jar
+MY_JAR = lib/gwtjsonrpc.jar
+MY_JAVA = $(shell find src -name \*.java)
+MY_GWT_XML = com/google/gwtjsonrpc/GWTJSONRPC.gwt.xml
+
+all: $(MY_JAR)
+
+clean:
+ rm -f $(MY_JAR)
+ rm -rf classes .bin
+
+$(MY_JAR): $(MY_JAVA) src/$(MY_GWT_XML)
+ rm -rf .bin
+ mkdir .bin
+ cd src && $(JAVAC) \
+ -encoding utf-8 \
+ -source 1.5 \
+ -target 1.5 \
+ -g \
+ -cp $(GWT_CP):../lib/gson.jar:../lib/commons-codec.jar \
+ -d ../.bin \
+ $(patsubst src/%,%,$(MY_JAVA))
+ cd .bin && jar cf ../$(MY_JAR) .
+ cd src && jar uf ../$(MY_JAR) .
+ rm -rf .bin
+
+.PHONY: all
+.PHONY: clean
diff --git a/README b/README
new file mode 100644
index 0000000..b081689
--- /dev/null
+++ b/README
@@ -0,0 +1,152 @@
+JSON-RPC for Google Web Toolkit (GWT)
+-------------------------------------
+
+The implementation is as close to the JSON-RPC 1.1 working draft[1]
+as possible, while retaining a simple code base in both the GWT
+client and the Java based server.
+
+To use this module, build lib/gwtjsonrpc.jar (type "make"), place
+the JAR into your classpath and inherit the module in your gwt.xml:
+
+ <inherits name='com.google.gwtjsonrpc.GWTJSONRPC'/>
+
+Java based JSON services should extend JsonServlet and directly
+implement the service interface. You will also need to include
+lib/gson.jar and lib/commons-codec.jar in the server's CLASSPATH.
+
+[1] http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html
+
+Services may be implemented in any language that supports JSON-RPC
+1.1, including Python, PHP, Perl, etc. This package includes an
+example Java based server to make development easier in a pure
+Java application.
+
+
+Differences from JSON-RPC 1.1
+-----------------------------
+
+Only positional parameters are supported. Within a request the
+'params' member must be missing, or must be a JSON array of the
+positional parameters.
+
+Only Java-style method names are supported. A method name must
+conform to ^[a-zA-Z_$][a-zA-Z_$0-9]*$ and thus any of the standard
+"system.*" methods (e.g. "system.describe") is not supported.
+
+Call approximation is not supported. The 'params' member must
+*exactly* match the declared parameters of the method being called.
+
+Error codes sent by the Java server are always 999 as the JSON RPC
+specification does not call out specific errors.
+
+
+Differences from GWT-RPC
+------------------------
+
+This package uses the standard JSON-RPC 1.1 for wire encoding,
+rather than a custom object serialization standard.
+
+Benefits:
+
+- Clients are not tied to GWT:
+
+ Clients may be written in any language that has a JSON parser
+ library available. Objects are proper JSON objects with field
+ names as declared in the Java classes being serialized.
+
+- Servers are not tied to GWT:
+
+ Servers may be written in any language, as the only requirement
+ is that the server can create a properly formatted JSON string
+ with the expected field names. If the Java field names are easily
+ recognized by a "subject matter expert" (or are at least documented
+ in the Java class definition) it is easy to implement a server.
+
+- Automatic XSRF (cross-site request forgery) protection:
+
+ When using the Java based server implementation in this package
+ automatic XSRF protection is enabled for every RPC.
+
+
+Drawbacks:
+
+- Object field names are exposed verbatim on the wire:
+
+ GWT-RPC protects the field names by not including them in the
+ JSON output. If you are using GWT to obfuscate your JavaScript
+ and hide intellectual property, this package isn't for you.
+
+- Slightly larger object transfers:
+
+ GWT-RPC recognizes fields by the position they appear in the JSON
+ parse tree (the entire stream is encoded as one giant JSON array).
+ This package includes field names in every object instance, as that
+ is required by the JSON format. The resulting string to be sent in
+ either direction is larger. This increase in size may be negated
+ by the automatic "gzip" encoding, if the browser supports it.
+
+- Exceptions are not "thrown" to the client:
+
+ GWT-RPC supports declared exceptions using two interfaces; this
+ package sends only the exception message back to the client
+ and does not support throwing checked exceptions from service
+ implementation methods.
+
+- Method overloading is not supported:
+
+ Only one method of each name can be declared in the interface.
+ Thus "void foo(int a)" and "void foo(String a)" cannot be used.
+ A simple (but annoying) work around is to add a unique suffix to
+ each method name.
+
+
+
+Example
+-------
+
+Define the service:
+
+ import com.google.gwt.user.client.rpc.AsyncCallback;
+ import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+ public interface StringService extends RemoteJsonService {
+ public void append(String a, String b, AsyncCallback<String> ac);
+ }
+
+Configure GWTJSONRPC in Application.gwt.xml:
+
+ <inherits name='com.google.gwtjsonrpc.GWTJSONRPC'/>
+ <servlet path='/StringService'
+ class='example.StringServiceImpl'/>
+
+Implement the service in Java as a servlet:
+
+ import com.google.gwt.user.client.rpc.AsyncCallback;
+ import com.google.gwtjsonrpc.server.JsonServlet;
+
+ public StringServiceImpl extends JsonServlet
+ implements StringService
+ {
+ public void append(String a, String b, AsyncCallback<String> ac)
+ {
+ if (a != null && b != null)
+ ac.onSuccess(a + b);
+ else
+ ac.onFailure(new IllegalArgumentException("Null input"));
+ }
+ }
+
+Call the service from the browser:
+
+ StringService cs = GWT.create(StringService.class);
+ ((ServiceDefTarget) cs).setServiceEntryPoint(
+ GWT.getModuleBaseURL() + "StringService");
+
+ cs.append("foo", "bar", new AsyncCallback<String>() {
+ public void onSuccess(String result) {
+ GWT.log("append = " + result, null);
+ }
+ public void onFailure(Throwable why) {
+ GWT.log("append failure", why);
+ }
+ });
diff --git a/README_ECLIPSE b/README_ECLIPSE
new file mode 100644
index 0000000..ba31783
--- /dev/null
+++ b/README_ECLIPSE
@@ -0,0 +1,18 @@
+Eclipse Setup
+=============
+
+User Library
+------------
+
+ Window > Preferences
+ Java > Build Path > User Libraries
+
+Create a User Library called "GWT":
+
+ New
+
+ Name: GWT
+ Add JARs...
+
+ * Select gwt-user.jar from the $(GWT_SDK) directory.
+ * Select gwt-dev-$(GWT_OS).jar from the $(GWT_SDK) directory.
diff --git a/lib/commons-codec.jar b/lib/commons-codec.jar
new file mode 100644
index 0000000..957b675
--- /dev/null
+++ b/lib/commons-codec.jar
Binary files differ
diff --git a/lib/gson.jar b/lib/gson.jar
new file mode 100644
index 0000000..0498cf2
--- /dev/null
+++ b/lib/gson.jar
Binary files differ
diff --git a/src/com/google/gwtjsonrpc/GWTJSONRPC.gwt.xml b/src/com/google/gwtjsonrpc/GWTJSONRPC.gwt.xml
new file mode 100644
index 0000000..adc8479
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/GWTJSONRPC.gwt.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright 2008 Google Inc.
+
+ 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.
+-->
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name='com.google.gwt.http.HTTP'/>
+ <inherits name='com.google.gwt.json.JSON'/>
+
+ <generate-with class="com.google.gwtjsonrpc.rebind.RemoteJsonServiceProxyGenerator">
+ <when-type-assignable class="com.google.gwtjsonrpc.client.RemoteJsonService" />
+ </generate-with>
+</module>
diff --git a/src/com/google/gwtjsonrpc/client/AbstractJsonProxy.java b/src/com/google/gwtjsonrpc/client/AbstractJsonProxy.java
new file mode 100644
index 0000000..a5d1b7e
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/AbstractJsonProxy.java
@@ -0,0 +1,50 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.ServiceDefTarget;
+
+/**
+ * Base class for generated {@link RemoteJsonService} implementations.
+ * <p>
+ * At runtime <code>GWT.create(Foo.class)</code> returns a subclass of this
+ * class, implementing the Foo and {@link ServiceDefTarget} interfaces.
+ */
+public abstract class AbstractJsonProxy implements ServiceDefTarget {
+ /** URL of the service implementation. */
+ String url;
+
+ /** Current XSRF token associated with this service. */
+ String xsrfKey;
+
+ public String getServiceEntryPoint() {
+ return url;
+ }
+
+ public void setServiceEntryPoint(final String address) {
+ url = address;
+ }
+
+ protected <T> void doInvoke(final String requestData,
+ final JsonSerializer<T> resultSerializer, final AsyncCallback<T> callback)
+ throws InvocationException {
+ if (url == null) {
+ throw new NoServiceEntryPointSpecifiedException();
+ }
+ new JsonCall<T>(this, requestData, resultSerializer, callback).send();
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/client/ArraySerializer.java b/src/com/google/gwtjsonrpc/client/ArraySerializer.java
new file mode 100644
index 0000000..c15d191
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/ArraySerializer.java
@@ -0,0 +1,70 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Default serialization for any Object[] sort of type.
+ * <p>
+ * Primitive array types (like <code>int[]</code>) are not supported.
+ */
+public class ArraySerializer<T> extends JsonSerializer<T[]> {
+ private final JsonSerializer<T> serializer;
+
+ public ArraySerializer(final JsonSerializer<T> s) {
+ serializer = s;
+ }
+
+ @Override
+ public void printJson(final StringBuffer sb, final T[] o) {
+ if (o != null) {
+ sb.append('[');
+ for (int i = 0, n = o.length; i < n; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ serializer.printJson(sb, o[i]);
+ }
+ sb.append(']');
+ } else {
+ sb.append(JS_NULL);
+ }
+ }
+
+ @Override
+ public T[] fromJson(final Object o) {
+ if (o == null) {
+ return null;
+ }
+
+ final JavaScriptObject jso = (JavaScriptObject) o;
+ final int n = size(jso);
+ final T[] r = ArraySerializer.<T> newArray(n);
+ for (int i = 0; i < n; i++) {
+ r[i] = serializer.fromJson(get(jso, i));
+ }
+ return r;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static final <T> T[] newArray(final int sz) {
+ return (T[]) new Object[sz];
+ }
+
+ private static final native int size(JavaScriptObject o)/*-{ return o.length; }-*/;
+
+ private static final native JavaScriptObject get(JavaScriptObject o, int i)/*-{ return o[i]; }-*/;
+}
diff --git a/src/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java b/src/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java
new file mode 100644
index 0000000..87dffe5
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java
@@ -0,0 +1,36 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+/** Default serialization for a String. */
+public final class JavaLangString_JsonSerializer extends
+ JsonSerializer<java.lang.String> {
+ public static final JavaLangString_JsonSerializer INSTANCE =
+ new JavaLangString_JsonSerializer();
+
+ @Override
+ public java.lang.String fromJson(final Object o) {
+ return (String) o;
+ }
+
+ @Override
+ public void printJson(final StringBuffer sb, final java.lang.String o) {
+ if (o != null) {
+ sb.append(escapeString(o));
+ } else {
+ sb.append(JS_NULL);
+ }
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java b/src/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java
new file mode 100644
index 0000000..c70b1a2
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java
@@ -0,0 +1,48 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import java.util.Date;
+
+/** Default serialization for a {@link java.util.Date}. */
+public final class JavaUtilDate_JsonSerializer extends
+ JsonSerializer<java.util.Date> {
+ public static final JavaUtilDate_JsonSerializer INSTANCE =
+ new JavaUtilDate_JsonSerializer();
+
+ @Override
+ public java.util.Date fromJson(final Object o) {
+ if (o != null) {
+ return parse((String) o);
+ }
+ return null;
+ }
+
+ @Override
+ public void printJson(final StringBuffer sb, final java.util.Date o) {
+ if (o != null) {
+ sb.append('"');
+ sb.append(o);
+ sb.append('"');
+ } else {
+ sb.append(JS_NULL);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private static Date parse(final String o) {
+ return new java.util.Date(o);
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/client/JsonCall.java b/src/com/google/gwtjsonrpc/client/JsonCall.java
new file mode 100644
index 0000000..37f484c
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/JsonCall.java
@@ -0,0 +1,141 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.StatusCodeException;
+
+class JsonCall<T> implements RequestCallback {
+ private final AbstractJsonProxy proxy;
+ private final String requestData;
+ private final JsonSerializer<T> resultSerializer;
+ private final AsyncCallback<T> callback;
+ private int attempts;
+
+ JsonCall(final AbstractJsonProxy abstractJsonProxy, final String requestData,
+ final JsonSerializer<T> resultSerializer, final AsyncCallback<T> callback) {
+ this.proxy = abstractJsonProxy;
+ this.requestData = requestData;
+ this.resultSerializer = resultSerializer;
+ this.callback = callback;
+ }
+
+ void send() {
+ final RequestBuilder rb;
+
+ rb = new RequestBuilder(RequestBuilder.POST, proxy.url);
+ rb.setHeader("Content-Type", Shared.JSON_TYPE);
+ rb.setHeader("Accept", Shared.JSON_TYPE);
+ rb.setRequestData(requestData);
+ rb.setCallback(this);
+ if (proxy.xsrfKey != null) {
+ rb.setHeader(Shared.XSRF_HEADER, proxy.xsrfKey);
+ }
+
+ try {
+ attempts++;
+ rb.send();
+ } catch (RequestException e) {
+ callback.onFailure(e);
+ }
+ }
+
+ public void onResponseReceived(final Request req, final Response rsp) {
+ final int sc = rsp.getStatusCode();
+
+ rememberXsrfKey(rsp);
+
+ if (sc == Shared.SC_INVALID_XSRF
+ && Shared.SM_INVALID_XSRF.equals(rsp.getText())) {
+ // The XSRF cookie was invalidated (or didn't exist) and the
+ // service demands we have one in place to make calls to it.
+ // A new token was returned to us, so start the request over.
+ //
+ if (attempts < 2) {
+ send();
+ } else {
+ callback.onFailure(new InvocationException(rsp.getText()));
+ }
+ return;
+ }
+
+ if (isJsonBody(rsp)) {
+ final RpcResult r = parse(rsp.getText());
+ if (r.error() != null) {
+ callback.onFailure(new InvocationException(r.error().message()));
+ return;
+ }
+
+ if (sc == Response.SC_OK) {
+ callback.onSuccess(resultSerializer.fromJson(r.result()));
+ return;
+ }
+ }
+
+ if (sc == Response.SC_OK) {
+ callback.onFailure(new InvocationException("No JSON response"));
+ } else {
+ callback.onFailure(new StatusCodeException(sc, rsp.getStatusText()));
+ }
+ }
+
+ public void onError(final Request request, final Throwable exception) {
+ callback.onFailure(exception);
+ }
+
+ private void rememberXsrfKey(final Response rsp) {
+ final String v = rsp.getHeader(Shared.XSRF_HEADER);
+ if (v != null) {
+ proxy.xsrfKey = v;
+ }
+ }
+
+ private static boolean isJsonBody(final Response rsp) {
+ String type = rsp.getHeader("Content-Type");
+ if (type == null) {
+ return false;
+ }
+ int semi = type.indexOf(';');
+ if (semi >= 0) {
+ type = type.substring(0, semi).trim();
+ }
+ return Shared.JSON_TYPE.equals(type);
+ }
+
+ private static final native RpcResult parse(String json)/*-{ return eval('('+json+')'); }-*/;
+
+ private static class RpcResult extends JavaScriptObject {
+ protected RpcResult() {
+ }
+
+ final native RpcError error()/*-{ return this.error; }-*/;
+
+ final native JavaScriptObject result()/*-{ return this.result; }-*/;
+ }
+
+ private static class RpcError extends JavaScriptObject {
+ protected RpcError() {
+ }
+
+ final native String message()/*-{ return this.message; }-*/;
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/client/JsonSerializer.java b/src/com/google/gwtjsonrpc/client/JsonSerializer.java
new file mode 100644
index 0000000..ab5b4f9
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/JsonSerializer.java
@@ -0,0 +1,69 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+/**
+ * Converter between JSON and Java object representations.
+ * <p>
+ * Implementors must provide bidirectional conversion, typically using the GWT
+ * JavaScriptObject and native JavaScript magic to read the JSON data structure.
+ * <p>
+ * Most implementations are generated automatically at compile-time by the
+ * <code>RemoteJsonServiceProxyGenerator</code>.
+ *
+ * @param <T> type of Java class this type works on.
+ */
+public abstract class JsonSerializer<T> {
+ /** Magic constant in JSON to mean the same as Java null. */
+ public static final String JS_NULL = "null";
+
+ /**
+ * Convert a Java object to JSON text.
+ * <p>
+ * Implementations should recursively call any nested object or collection at
+ * the appropriate time to append the nested item's JSON text.
+ *
+ * @param sb the output string buffer the JSON text should be appended onto
+ * the end of.
+ * @param o the Java instance being converted. May be null, in which case
+ * {@link #JS_NULL} should be appended instead.
+ */
+ public abstract void printJson(StringBuffer sb, T o);
+
+ /**
+ * Convert from JSON (stored as a JavaScriptObject) into a new Java instance.
+ *
+ * @param o the JSON object instance; typically this should be downcast to
+ * JavaScriptObject. May be null, in which case null should be returned
+ * instead of an instance.
+ * @return null if <code>o</code> was null; otherwise the new object instance
+ * with the data copied over form the JSON representation.
+ */
+ public abstract T fromJson(Object o);
+
+ /**
+ * Utility function to convert a String to safe JSON text.
+ * <p>
+ * For example, if <code>val = "b\nb"</code> this method returns the value
+ * <code>"\"b\\nb\""</code>.
+ * <p>
+ * Typically called by {@link #printJson(StringBuffer, Object)}, or through
+ * {@link JavaLangString_JsonSerializer}.
+ *
+ * @param val string text requiring escaping support. Must not be null.
+ * @return a JSON literal text value, surrounded with double quotes.
+ */
+ public static final native String escapeString(String val)/*-{ return @com.google.gwt.json.client.JSONString::escapeValue(Ljava/lang/String;)(val); }-*/;
+}
diff --git a/src/com/google/gwtjsonrpc/client/ListSerializer.java b/src/com/google/gwtjsonrpc/client/ListSerializer.java
new file mode 100644
index 0000000..cc22aad
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/ListSerializer.java
@@ -0,0 +1,71 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+import java.util.ArrayList;
+
+/**
+ * Serialization for a {@link java.util.List}.
+ * <p>
+ * When deserialized from JSON the List implementation is always an
+ * {@link ArrayList}. When serializing to JSON any List is permitted.
+ */
+public class ListSerializer<T> extends JsonSerializer<java.util.List<T>> {
+ private final JsonSerializer<T> serializer;
+
+ public ListSerializer(final JsonSerializer<T> s) {
+ serializer = s;
+ }
+
+ @Override
+ public void printJson(final StringBuffer sb, final java.util.List<T> o) {
+ if (o != null) {
+ sb.append('[');
+ boolean first = true;
+ for (final T item : o) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ serializer.printJson(sb, item);
+ }
+ sb.append(']');
+ } else {
+ sb.append(JS_NULL);
+ }
+ }
+
+ @Override
+ public java.util.List<T> fromJson(final Object o) {
+ if (o == null) {
+ return null;
+ }
+
+ final JavaScriptObject jso = (JavaScriptObject) o;
+ final int n = size(jso);
+ final ArrayList<T> r = new ArrayList<T>(n);
+ for (int i = 0; i < n; i++) {
+ r.add(serializer.fromJson(get(jso, i)));
+ }
+ return r;
+ }
+
+ private static final native int size(JavaScriptObject o)/*-{ return o.length; }-*/;
+
+ private static final native JavaScriptObject get(JavaScriptObject o, int i)/*-{ return o[i]; }-*/;
+}
diff --git a/src/com/google/gwtjsonrpc/client/RemoteJsonService.java b/src/com/google/gwtjsonrpc/client/RemoteJsonService.java
new file mode 100644
index 0000000..dd7795b
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/RemoteJsonService.java
@@ -0,0 +1,51 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * Marker interface for JSON based RPC.
+ * <p>
+ * Application service interfaces should extend this interface:
+ *
+ * <pre>
+ * public interface FooService extends RemoteJsonService ...
+ * </pre>
+ * <p>
+ * and declare each method as returning void and accepting {@link AsyncCallback}
+ * as the final parameter, with a concrete type specified as the result type:
+ *
+ * <pre>
+ * public interface FooService extends RemoteJsonService {
+ * public void fooItUp(AsyncCallback&lt;ResultType&gt; callback);
+ * }
+ * </pre>
+ * <p>
+ * Instances of the interface can be obtained in the client and configured to
+ * reference a particular JSON server:
+ *
+ * <pre>
+ * FooService mysvc = GWT.create(FooService.class);
+ * ((ServiceDefTarget) mysvc).setServiceEntryPoint(GWT.getModuleBaseURL()
+ * + &quot;FooService&quot;);
+ *</pre>
+ * <p>
+ * Calling conventions match the JSON-RPC 1.1 working draft from 7 August 2006
+ * (<a href="http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html">draft</a>).
+ * Only positional parameters are supported.
+ */
+public interface RemoteJsonService {
+}
diff --git a/src/com/google/gwtjsonrpc/client/SetSerializer.java b/src/com/google/gwtjsonrpc/client/SetSerializer.java
new file mode 100644
index 0000000..9cb2177
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/SetSerializer.java
@@ -0,0 +1,71 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+import java.util.HashSet;
+
+/**
+ * Serialization for a {@link java.util.Set}.
+ * <p>
+ * When deserialized from JSON the List implementation is always a
+ * {@link HashSet}. When serializing to JSON any Set is permitted.
+ */
+public class SetSerializer<T> extends JsonSerializer<java.util.Set<T>> {
+ private final JsonSerializer<T> serializer;
+
+ public SetSerializer(final JsonSerializer<T> s) {
+ serializer = s;
+ }
+
+ @Override
+ public void printJson(final StringBuffer sb, final java.util.Set<T> o) {
+ if (o != null) {
+ sb.append('[');
+ boolean first = true;
+ for (final T item : o) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ serializer.printJson(sb, item);
+ }
+ sb.append(']');
+ } else {
+ sb.append(JS_NULL);
+ }
+ }
+
+ @Override
+ public java.util.Set<T> fromJson(final Object o) {
+ if (o == null) {
+ return null;
+ }
+
+ final JavaScriptObject jso = (JavaScriptObject) o;
+ final int n = size(jso);
+ final HashSet<T> r = new HashSet<T>(n);
+ for (int i = 0; i < n; i++) {
+ r.add(serializer.fromJson(get(jso, i)));
+ }
+ return r;
+ }
+
+ private static final native int size(JavaScriptObject o)/*-{ return o.length; }-*/;
+
+ private static final native JavaScriptObject get(JavaScriptObject o, int i)/*-{ return o[i]; }-*/;
+}
diff --git a/src/com/google/gwtjsonrpc/client/Shared.java b/src/com/google/gwtjsonrpc/client/Shared.java
new file mode 100644
index 0000000..56b037a
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/client/Shared.java
@@ -0,0 +1,35 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.client;
+
+/** Shared constants between client and server implementations. */
+public class Shared {
+ /** Proper Content-Type header value for JSON encoded data. */
+ public static final String JSON_TYPE = "application/json";
+
+ /**
+ * Name of the HTTP header holding the XSRF token is inserted into.
+ */
+ public static final String XSRF_HEADER = "X-RPC-XSRF-Token";
+
+ /** HTTP status code when the XSRF token is missing or invalid. */
+ public static final int SC_INVALID_XSRF = 400; // aka SC_BAD_REQUEST
+
+ /** Complete content when the XSRF token is missing or invalid. */
+ public static final String SM_INVALID_XSRF = "INVALID_XSRF";
+
+ private Shared() {
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/rebind/ProxyCreator.java b/src/com/google/gwtjsonrpc/rebind/ProxyCreator.java
new file mode 100644
index 0000000..fdb88e0
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/rebind/ProxyCreator.java
@@ -0,0 +1,281 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.rebind;
+
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPackage;
+import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.generator.NameFactory;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwtjsonrpc.client.AbstractJsonProxy;
+import com.google.gwtjsonrpc.client.JsonSerializer;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+class ProxyCreator {
+ private static final String PROXY_SUFFIX = "_JsonProxy";
+ private JClassType svcInf;
+ private JClassType asyncCallbackClass;
+ private SerializerCreator serializerCreator;
+
+ ProxyCreator(final JClassType remoteService) {
+ svcInf = remoteService;
+ }
+
+ String create(final TreeLogger logger, final GeneratorContext context)
+ throws UnableToCompleteException {
+ serializerCreator = new SerializerCreator(context);
+ final TypeOracle typeOracle = context.getTypeOracle();
+ try {
+ asyncCallbackClass = typeOracle.getType(AsyncCallback.class.getName());
+ } catch (NotFoundException e) {
+ logger.log(TreeLogger.ERROR, null, e);
+ throw new UnableToCompleteException();
+ }
+ checkMethods(logger, context);
+
+ final SourceWriter srcWriter = getSourceWriter(logger, context);
+ if (srcWriter == null) {
+ return getProxyQualifiedName();
+ }
+
+ generateProxyMethods(logger, srcWriter);
+ srcWriter.commit(logger);
+
+ return getProxyQualifiedName();
+ }
+
+ private void checkMethods(final TreeLogger logger,
+ final GeneratorContext context) throws UnableToCompleteException {
+ final Set<String> declaredNames = new HashSet<String>();
+ final JMethod[] methodList = svcInf.getOverridableMethods();
+ for (final JMethod m : methodList) {
+ if (!declaredNames.add(m.getName())) {
+ invalid(logger, "Overloading method " + m.getName() + " not supported");
+ }
+
+ if (m.getReturnType() != JPrimitiveType.VOID) {
+ invalid(logger, "Method " + m.getName() + " must return void");
+ }
+
+ final JParameter[] params = m.getParameters();
+ if (params.length == 0) {
+ invalid(logger, "Method " + m.getName() + " requires "
+ + AsyncCallback.class.getName() + " as last parameter");
+ }
+
+ final JParameter callback = params[params.length - 1];
+ if (!callback.getType().getErasedType().getQualifiedSourceName().equals(
+ asyncCallbackClass.getQualifiedSourceName())) {
+ invalid(logger, "Method " + m.getName() + " requires "
+ + AsyncCallback.class.getName() + " as last parameter");
+ }
+ if (callback.getType().isParameterized() == null) {
+ invalid(logger, "Callback " + callback.getName()
+ + " must have a type parameter");
+ }
+
+ for (int i = 0; i < params.length - 1; i++) {
+ final JParameter p = params[i];
+ final TreeLogger branch =
+ logger.branch(TreeLogger.DEBUG, m.getName() + ", parameter "
+ + p.getName());
+ serializerCreator.checkCanSerialize(branch, p.getType());
+ if (p.getType().isPrimitive() == null) {
+ serializerCreator.create((JClassType) p.getType(), branch);
+ }
+ }
+
+ final JClassType resultType =
+ callback.getType().isParameterized().getTypeArgs()[0];
+ final TreeLogger branch =
+ logger.branch(TreeLogger.DEBUG, m.getName() + ", result "
+ + resultType.getQualifiedSourceName());
+ serializerCreator.checkCanSerialize(branch, resultType);
+ serializerCreator.create(resultType, branch);
+ }
+ }
+
+ private void invalid(final TreeLogger logger, final String what)
+ throws UnableToCompleteException {
+ logger.log(TreeLogger.ERROR, what, null);
+ throw new UnableToCompleteException();
+ }
+
+ private SourceWriter getSourceWriter(final TreeLogger logger,
+ final GeneratorContext ctx) {
+ final JPackage servicePkg = svcInf.getPackage();
+ final String pkgName = servicePkg == null ? "" : servicePkg.getName();
+ final PrintWriter pw;
+ final ClassSourceFileComposerFactory cf;
+
+ pw = ctx.tryCreate(logger, pkgName, getProxySimpleName());
+ if (pw == null) {
+ return null;
+ }
+
+ cf = new ClassSourceFileComposerFactory(pkgName, getProxySimpleName());
+ cf.addImport(AbstractJsonProxy.class.getCanonicalName());
+ cf.addImport(JsonSerializer.class.getCanonicalName());
+ cf.setSuperclass(AbstractJsonProxy.class.getSimpleName());
+ cf.addImplementedInterface(svcInf.getErasedType().getQualifiedSourceName());
+ return cf.createSourceWriter(ctx, pw);
+ }
+
+ private void generateProxyMethods(final TreeLogger logger,
+ final SourceWriter srcWriter) throws UnableToCompleteException {
+ final JMethod[] methodList = svcInf.getOverridableMethods();
+ for (final JMethod m : methodList) {
+ generateProxyMethod(logger, m, srcWriter);
+ }
+ }
+
+ private void generateProxyMethod(final TreeLogger logger,
+ final JMethod method, final SourceWriter w)
+ throws UnableToCompleteException {
+ w.println();
+
+ w.print("public void " + method.getName() + "(");
+ boolean needsComma = false;
+ final NameFactory nameFactory = new NameFactory();
+ final JParameter[] params = method.getParameters();
+ final JParameter callback = params[params.length - 1];
+ final JClassType resultType =
+ callback.getType().isParameterized().getTypeArgs()[0];
+ for (int i = 0; i < params.length; i++) {
+ final JParameter param = params[i];
+
+ if (needsComma) {
+ w.print(", ");
+ } else {
+ needsComma = true;
+ }
+
+ final JType paramType = param.getType().getErasedType();
+ w.print(paramType.getQualifiedSourceName());
+ w.print(" ");
+
+ nameFactory.addName(param.getName());
+ w.print(param.getName());
+ }
+
+ w.println(") {");
+ w.indent();
+
+ final String rVersion = "\\\"version\\\":\\\"1.1\\\"";
+ final String rMethod = "\\\"method\\\":\\\"" + method.getName() + "\\\"";
+ final String reqDataStr;
+ if (params.length == 1) {
+ reqDataStr = "\"{" + rVersion + "," + rMethod + "}\"";
+ } else {
+ final String reqData = nameFactory.createName("reqData");
+ w.println("final StringBuffer " + reqData + " = new StringBuffer();");
+ w.println(reqData + ".append(\"{" + rVersion + "," + rMethod
+ + ",\\\"params\\\":[\");");
+
+ needsComma = false;
+ for (int i = 0; i < params.length - 1; i++) {
+ if (needsComma) {
+ w.println(reqData + ".append(\",\");");
+ } else {
+ needsComma = true;
+ }
+
+ final JType pType = params[i].getType();
+ final String pName = params[i].getName();
+ if (SerializerCreator.isJsonPrimitive(pType)
+ && !SerializerCreator.isJsonString(pType)) {
+ w.println(reqData + ".append(" + pName + ");");
+ } else {
+ if (pType.isParameterized() != null) {
+ serializerCreator.generateSerializerReference(pType
+ .isParameterized(), w);
+ } else {
+ w.print(serializerCreator.create((JClassType) pType, logger)
+ + ".INSTANCE");
+ }
+ w.println(".printJson(" + reqData + ", " + pName + ");");
+ }
+ }
+ w.println(reqData + ".append(\"]}\");");
+ reqDataStr = reqData + ".toString()";
+ }
+
+ w.print("doInvoke(");
+ w.print(reqDataStr);
+ w.print(", " + serializerCreator.create(resultType, logger) + ".INSTANCE");
+ w.print(", " + callback.getName());
+ w.println(");");
+
+ w.outdent();
+ w.println("}");
+ }
+
+ private String getProxyQualifiedName() {
+ final String[] name = synthesizeTopLevelClassName(svcInf, PROXY_SUFFIX);
+ return name[0].length() == 0 ? name[1] : name[0] + "." + name[1];
+ }
+
+ private String getProxySimpleName() {
+ return synthesizeTopLevelClassName(svcInf, PROXY_SUFFIX)[1];
+ }
+
+ static String[] synthesizeTopLevelClassName(JClassType type, String suffix) {
+ // Gets the basic name of the type. If it's a nested type, the type name
+ // will contains dots.
+ //
+ String className;
+ String packageName;
+
+ JType leafType = type.getLeafType();
+ if (leafType.isPrimitive() != null) {
+ className = leafType.getSimpleSourceName();
+ packageName = "";
+ } else {
+ JClassType classOrInterface = leafType.isClassOrInterface();
+ assert (classOrInterface != null);
+ className = classOrInterface.getName();
+ packageName = classOrInterface.getPackage().getName();
+ }
+
+ JArrayType isArray = type.isArray();
+ if (isArray != null) {
+ className += "_Array_Rank_" + isArray.getRank();
+ }
+
+ // Add the meaningful suffix.
+ //
+ className += suffix;
+
+ // Make it a top-level name.
+ //
+ className = className.replace('.', '_');
+
+ return new String[] {packageName, className};
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/rebind/RemoteJsonServiceProxyGenerator.java b/src/com/google/gwtjsonrpc/rebind/RemoteJsonServiceProxyGenerator.java
new file mode 100644
index 0000000..0f9892f
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/rebind/RemoteJsonServiceProxyGenerator.java
@@ -0,0 +1,58 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.rebind;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+/**
+ * Generates proxy implementations of {@link RemoteJsonService}.
+ */
+public class RemoteJsonServiceProxyGenerator extends Generator {
+ @Override
+ public String generate(final TreeLogger logger, final GeneratorContext ctx,
+ final String requestedClass) throws UnableToCompleteException {
+
+ final TypeOracle typeOracle = ctx.getTypeOracle();
+ assert (typeOracle != null);
+
+ final JClassType remoteService = typeOracle.findType(requestedClass);
+ if (remoteService == null) {
+ logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+ + requestedClass + "'", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (remoteService.isInterface() == null) {
+ logger.log(TreeLogger.ERROR, remoteService.getQualifiedSourceName()
+ + " is not an interface", null);
+ throw new UnableToCompleteException();
+ }
+
+ ProxyCreator proxyCreator = new ProxyCreator(remoteService);
+
+ TreeLogger proxyLogger =
+ logger.branch(TreeLogger.DEBUG,
+ "Generating client proxy for remote service interface '"
+ + remoteService.getQualifiedSourceName() + "'", null);
+
+ return proxyCreator.create(proxyLogger, ctx);
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/rebind/SerializerCreator.java b/src/com/google/gwtjsonrpc/rebind/SerializerCreator.java
new file mode 100644
index 0000000..6c1b2c7
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/rebind/SerializerCreator.java
@@ -0,0 +1,497 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.rebind;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JPackage;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwtjsonrpc.client.ArraySerializer;
+import com.google.gwtjsonrpc.client.JavaLangString_JsonSerializer;
+import com.google.gwtjsonrpc.client.JavaUtilDate_JsonSerializer;
+import com.google.gwtjsonrpc.client.JsonSerializer;
+import com.google.gwtjsonrpc.client.ListSerializer;
+import com.google.gwtjsonrpc.client.SetSerializer;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+
+class SerializerCreator {
+ private static final String SER_SUFFIX = "_JsonSerializer";
+ private static final Comparator<JField> FIELD_COMP =
+ new Comparator<JField>() {
+ public int compare(final JField o1, final JField o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ };
+
+ private static final HashMap<String, String> defaultSerializers;
+ private static final HashMap<String, String> parameterizedSerializers;
+ static {
+ defaultSerializers = new HashMap<String, String>();
+ parameterizedSerializers = new HashMap<String, String>();
+
+ defaultSerializers.put(java.lang.String.class.getCanonicalName(),
+ JavaLangString_JsonSerializer.class.getCanonicalName());
+ defaultSerializers.put(java.util.Date.class.getCanonicalName(),
+ JavaUtilDate_JsonSerializer.class.getCanonicalName());
+
+ parameterizedSerializers.put(java.util.List.class.getCanonicalName(),
+ ListSerializer.class.getCanonicalName());
+ parameterizedSerializers.put(java.util.Set.class.getCanonicalName(),
+ SetSerializer.class.getCanonicalName());
+ }
+
+ private final HashMap<String, String> generatedSerializers;
+ private final GeneratorContext context;
+ private JClassType targetType;
+
+ SerializerCreator(final GeneratorContext c) {
+ context = c;
+ generatedSerializers = new HashMap<String, String>();
+ }
+
+ String create(final JClassType targetType, final TreeLogger logger)
+ throws UnableToCompleteException {
+ String sClassName = serializerFor(targetType);
+ if (sClassName != null) {
+ return sClassName;
+ }
+
+ checkCanSerialize(logger, targetType);
+ recursivelyCreateSerializers(logger, targetType);
+
+ this.targetType = targetType;
+ final TypeOracle typeOracle = context.getTypeOracle();
+ final SourceWriter srcWriter = getSourceWriter(logger, context);
+ if (srcWriter == null) {
+ return getSerializerQualifiedName();
+ }
+
+ if (targetType.isParameterized() == null) {
+ generateSingleton(srcWriter);
+ }
+ generateInstanceMembers(srcWriter);
+ generatePrintJson(srcWriter);
+ generateFromJson(srcWriter);
+ generateGetSets(srcWriter);
+
+ srcWriter.commit(logger);
+ final String sn = getSerializerQualifiedName();
+ generatedSerializers.put(targetType.getQualifiedSourceName(), sn);
+ return sn;
+ }
+
+ private void recursivelyCreateSerializers(final TreeLogger logger,
+ final JType targetType) throws UnableToCompleteException {
+ if (targetType.isPrimitive() != null) {
+ return;
+ }
+
+ for (final JField f : sortFields((JClassType) targetType)) {
+ final JType type = f.getType();
+ if (isJsonPrimitive(type)) {
+ continue;
+ }
+
+ if (type.isArray() != null) {
+ create((JClassType) type.isArray().getComponentType(), logger);
+ continue;
+ }
+
+ if (type.isParameterized() != null) {
+ final JClassType[] typeArgs = type.isParameterized().getTypeArgs();
+ for (final JClassType t : typeArgs) {
+ create(t, logger);
+ }
+ }
+
+ final String qsn = type.getQualifiedSourceName();
+ if (defaultSerializers.containsKey(qsn)
+ || parameterizedSerializers.containsKey(qsn)) {
+ continue;
+ }
+
+ create((JClassType) type, logger);
+ }
+ }
+
+ void checkCanSerialize(final TreeLogger logger, final JType type)
+ throws UnableToCompleteException {
+ if (type.isPrimitive() == JPrimitiveType.LONG) {
+ logger.log(TreeLogger.ERROR,
+ "Type 'long' not supported in JSON encoding", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (type.isPrimitive() == JPrimitiveType.VOID) {
+ logger.log(TreeLogger.ERROR,
+ "Type 'void' not supported in JSON encoding", null);
+ throw new UnableToCompleteException();
+ }
+
+ final String qsn = type.getQualifiedSourceName();
+ if (type.isEnum() != null) {
+ logger.log(TreeLogger.ERROR, "Java enum " + qsn
+ + " not supported in JSON encoding", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (isJsonPrimitive(type)) {
+ return;
+ }
+
+ if (type.isArray() != null) {
+ if (type.isArray().getComponentType().isPrimitive() != null) {
+ logger.log(TreeLogger.ERROR,
+ "Primitive array not supported in JSON encoding", null);
+ throw new UnableToCompleteException();
+ }
+ checkCanSerialize(logger, type.isArray().getComponentType());
+ return;
+ }
+
+ if (defaultSerializers.containsKey(qsn)) {
+ return;
+ }
+
+ if (type.isParameterized() != null) {
+ final JClassType[] typeArgs = type.isParameterized().getTypeArgs();
+ for (final JClassType t : typeArgs) {
+ checkCanSerialize(logger, t);
+ }
+ if (parameterizedSerializers.containsKey(qsn)) {
+ return;
+ }
+ } else if (parameterizedSerializers.containsKey(qsn)) {
+ logger.log(TreeLogger.ERROR,
+ "Type " + qsn + " requires type paramter(s)", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (qsn.startsWith("java.") || qsn.startsWith("javax.")) {
+ logger.log(TreeLogger.ERROR, "Standard type " + qsn
+ + " not supported in JSON encoding", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (type.isInterface() != null) {
+ logger.log(TreeLogger.ERROR, "Interface " + qsn
+ + " not supported in JSON encoding", null);
+ throw new UnableToCompleteException();
+ }
+
+ final JClassType ct = (JClassType) type;
+ final TreeLogger branch = logger.branch(TreeLogger.DEBUG, "In type " + qsn);
+ for (final JField f : sortFields(ct)) {
+ checkCanSerialize(branch, f.getType());
+ }
+ }
+
+ String serializerFor(final JClassType t) {
+ if (t.isArray() != null) {
+ return ArraySerializer.class.getCanonicalName();
+ }
+
+ final String qsn = t.getQualifiedSourceName();
+ if (defaultSerializers.containsKey(qsn)) {
+ return defaultSerializers.get(qsn);
+ }
+
+ if (parameterizedSerializers.containsKey(qsn)) {
+ return parameterizedSerializers.get(qsn);
+ }
+
+ return generatedSerializers.get(qsn);
+ }
+
+ private void generateSingleton(final SourceWriter w) {
+ w.print("public static final ");
+ w.print(getSerializerSimpleName());
+ w.print(" INSTANCE = new ");
+ w.print(getSerializerSimpleName());
+ w.println("();");
+ w.println();
+ }
+
+ private void generateInstanceMembers(final SourceWriter w) {
+ for (final JField f : sortFields(targetType)) {
+ final JParameterizedType pt = f.getType().isParameterized();
+ if (pt == null) {
+ continue;
+ }
+
+ final String serType = serializerFor(pt);
+ w.print("private final ");
+ w.print(serType);
+ w.print(" ");
+ w.print("ser_" + f.getName());
+ w.print(" = ");
+ generateSerializerReference(pt, w);
+ w.println(";");
+ }
+ w.println();
+ }
+
+ void generateSerializerReference(final JParameterizedType type,
+ final SourceWriter w) {
+ w.print("new " + serializerFor(type) + "(");
+ boolean first = true;
+ for (final JClassType t : type.isParameterized().getTypeArgs()) {
+ if (first) {
+ first = false;
+ } else {
+ w.print(", ");
+ }
+
+ if (t.isParameterized() == null) {
+ w.print(serializerFor(t) + ".INSTANCE");
+ } else {
+ generateSerializerReference(t.isParameterized(), w);
+ }
+ }
+ w.print(")");
+ }
+
+ private void generateGetSets(final SourceWriter w) {
+ for (final JField f : sortFields(targetType)) {
+ if (f.isPrivate()) {
+ w.print("private static final native ");
+ w.print(f.getType().getQualifiedSourceName());
+ w.print(" objectGet_" + f.getName());
+ w.print("(");
+ w.print(targetType.getQualifiedSourceName() + " instance");
+ w.print(")");
+ w.println("/*-{ ");
+ w.indent();
+
+ w.print("return instance.@");
+ w.print(targetType.getQualifiedSourceName());
+ w.print("::");
+ w.print(f.getName());
+ w.println(";");
+
+ w.outdent();
+ w.println("}-*/;");
+
+ w.print("private static final native void ");
+ w.print(" objectSet_" + f.getName());
+ w.print("(");
+ w.print(targetType.getQualifiedSourceName() + " instance, ");
+ w.print(f.getType().getQualifiedSourceName() + " value");
+ w.print(")");
+ w.println("/*-{ ");
+ w.indent();
+
+ w.print("instance.@");
+ w.print(targetType.getQualifiedSourceName());
+ w.print("::");
+ w.print(f.getName());
+ w.println(" = value;");
+
+ w.outdent();
+ w.println("}-*/;");
+ }
+
+ w.print("private static final native ");
+ if (isJsonPrimitive(f.getType())) {
+ w.print(f.getType().getQualifiedSourceName());
+ } else {
+ w.print("Object");
+ }
+ w.print(" jsonGet_" + f.getName());
+ w.print("(JavaScriptObject instance)");
+ w.println("/*-{ ");
+ w.indent();
+
+ w.print("return instance.");
+ w.print(f.getName());
+ w.println(";");
+
+ w.outdent();
+ w.println("}-*/;");
+
+ w.println();
+ }
+ }
+
+ private void generatePrintJson(final SourceWriter w) {
+ w.print("public void printJson(StringBuffer sb, ");
+ w.print(targetType.getQualifiedSourceName());
+ w.println(" src) {");
+ w.indent();
+
+ w.println("int fieldCount = -1;");
+ final String docomma = "if (++fieldCount > 0) sb.append(\",\");";
+
+ w.println("sb.append(\"{\");");
+
+ for (final JField f : sortFields(targetType)) {
+ final String doget;
+ if (f.isPrivate()) {
+ doget = "objectGet_" + f.getName() + "(src)";
+ } else {
+ doget = "src." + f.getName();
+ }
+
+ final String doname = "sb.append(\"\\\"" + f.getName() + "\\\":\");";
+ if (isJsonString(f.getType())) {
+ w.println("if (" + doget + " != null) {");
+ w.indent();
+ w.println(docomma);
+ w.println(doname);
+ w.println("sb.append(" + JsonSerializer.class.getSimpleName()
+ + ".escapeString(" + doget + "));");
+ w.outdent();
+ w.println("}");
+ w.println();
+ } else if (isJsonPrimitive(f.getType())) {
+ w.println(docomma);
+ w.println(doname);
+ w.println("sb.append(" + doget + ");");
+ w.println();
+ } else {
+ w.println("if (" + doget + " != null) {");
+ w.indent();
+ w.println(docomma);
+ w.println(doname);
+ if (f.getType().isParameterized() != null) {
+ w.print("ser_" + f.getName());
+ } else {
+ w.print(serializerFor((JClassType) f.getType()) + ".INSTANCE");
+ }
+ w.println(".printJson(sb, " + doget + ");");
+ w.outdent();
+ w.println("}");
+ w.println();
+ }
+ }
+
+ w.println("sb.append(\"}\");");
+ w.outdent();
+ w.println("}");
+ w.println();
+ }
+
+ private void generateFromJson(final SourceWriter w) {
+ w.print("public ");
+ w.print(targetType.getQualifiedSourceName());
+ w.println(" fromJson(Object in) {");
+ w.indent();
+
+ w.println("if (in == null) return null;");
+ w.print("final JavaScriptObject jso = (JavaScriptObject)in;");
+ w.print("final ");
+ w.print(targetType.getQualifiedSourceName());
+ w.print(" dst = new ");
+ w.println(targetType.getQualifiedSourceName() + "();");
+
+ for (final JField f : sortFields(targetType)) {
+ final String doget = "jsonGet_" + f.getName() + "(jso)";
+ final String doset0, doset1;
+
+ if (f.isPrivate()) {
+ doset0 = "objectSet_" + f.getName() + "(dst, ";
+ doset1 = ")";
+ } else {
+ doset0 = "dst." + f.getName() + " = ";
+ doset1 = "";
+ }
+
+ if (isJsonPrimitive(f.getType())) {
+ w.print(doset0);
+ w.print(doget);
+ w.print(doset1);
+ w.println(";");
+ } else {
+ w.print(doset0);
+ if (f.getType().isParameterized() != null) {
+ w.print("ser_" + f.getName());
+ } else {
+ w.print(serializerFor((JClassType) f.getType()) + ".INSTANCE");
+ }
+ w.print(".fromJson(" + doget + ")");
+ w.print(doset1);
+ w.println(";");
+ }
+ }
+
+ w.println("return dst;");
+ w.outdent();
+ w.println("}");
+ w.println();
+ }
+
+ static boolean isJsonPrimitive(final JType t) {
+ return t.isPrimitive() != null || isJsonString(t);
+ }
+
+ static boolean isJsonString(final JType t) {
+ return t.getQualifiedSourceName().equals(String.class.getCanonicalName());
+ }
+
+ private SourceWriter getSourceWriter(final TreeLogger logger,
+ final GeneratorContext ctx) {
+ final JPackage targetPkg = targetType.getPackage();
+ final String pkgName = targetPkg == null ? "" : targetPkg.getName();
+ final PrintWriter pw;
+ final ClassSourceFileComposerFactory cf;
+
+ pw = ctx.tryCreate(logger, pkgName, getSerializerSimpleName());
+ if (pw == null) {
+ return null;
+ }
+
+ cf = new ClassSourceFileComposerFactory(pkgName, getSerializerSimpleName());
+ cf.addImport(JavaScriptObject.class.getCanonicalName());
+ cf.addImport(JsonSerializer.class.getCanonicalName());
+ cf.setSuperclass(JsonSerializer.class.getSimpleName() + "<"
+ + targetType.getQualifiedSourceName() + ">");
+ return cf.createSourceWriter(ctx, pw);
+ }
+
+ private String getSerializerQualifiedName() {
+ final String[] name;
+ name = ProxyCreator.synthesizeTopLevelClassName(targetType, SER_SUFFIX);
+ return name[0].length() == 0 ? name[1] : name[0] + "." + name[1];
+ }
+
+ private String getSerializerSimpleName() {
+ return ProxyCreator.synthesizeTopLevelClassName(targetType, SER_SUFFIX)[1];
+ }
+
+ private static JField[] sortFields(final JClassType targetType) {
+ final ArrayList<JField> r = new ArrayList<JField>();
+ for (final JField f : targetType.getFields()) {
+ if (!f.isStatic() && !f.isTransient() && !f.isFinal()) {
+ r.add(f);
+ }
+ }
+ Collections.sort(r, FIELD_COMP);
+ return r.toArray(new JField[r.size()]);
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/server/ActiveCall.java b/src/com/google/gwtjsonrpc/server/ActiveCall.java
new file mode 100644
index 0000000..0cf1031
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/ActiveCall.java
@@ -0,0 +1,68 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+import com.google.gson.JsonElement;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/** An active RPC call. */
+public class ActiveCall {
+ HttpServletRequest httpRequest;
+ HttpServletResponse httpResponse;
+ JsonElement id;
+ MethodHandle method;
+ Object[] params;
+ Object result;
+ Throwable error;
+
+ /**
+ * Get the HTTP request that is attempting this RPC call.
+ *
+ * @return the original servlet HTTP request.
+ */
+ public HttpServletRequest getHttpServletRequest() {
+ return httpRequest;
+ }
+
+ /**
+ * Get the HTTP response that will be returned from this call.
+ *
+ * @return the original servlet HTTP response.
+ */
+ public HttpServletResponse getHttpServletResponse() {
+ return httpResponse;
+ }
+
+ /**
+ * Get the method this request is asking to invoke.
+ *
+ * @return the requested method handle.
+ */
+ public MethodHandle getMethod() {
+ return method;
+ }
+
+ /**
+ * Get the actual parameter values to be supplied to the method.
+ *
+ * @return the parameter array; never null but may be 0-length if the method
+ * takes no parameters.
+ */
+ public Object[] getParams() {
+ return params;
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/server/JsonServlet.java b/src/com/google/gwtjsonrpc/server/JsonServlet.java
new file mode 100644
index 0000000..03e57ae
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/JsonServlet.java
@@ -0,0 +1,305 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+import static com.google.gwtjsonrpc.client.Shared.XSRF_HEADER;
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.Shared;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Basic HTTP servlet to forward JSON based RPC requests onto services.
+ * <p>
+ * Implementors of a JSON-RPC service should extend this servlet and implement
+ * any interface(s) that extend from {@link RemoteJsonService}. Clients may
+ * invoke methods declared in any implemented interface.
+ * <p>
+ * Calling conventions match the JSON-RPC 1.1 working draft from 7 August 2006
+ * (<a href="http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html">draft</a>).
+ * Only positional parameters are supported.
+ * <p>
+ * When supported by the browser/client, the "gzip" encoding is used to compress
+ * the resulting JSON, reducing transfer time for the response data.
+ */
+public abstract class JsonServlet extends HttpServlet {
+ static final Object[] NO_PARAMS = {};
+ static final String RPC_VERSION = "1.1";
+ private static final String ENC = "UTF-8";
+
+ private final HashMap<String, MethodHandle> myMethods;
+ private XsrfUtil xsrf;
+
+ protected JsonServlet() {
+ myMethods = methods(this);
+ }
+
+ @Override
+ public void init(final ServletConfig config) throws ServletException {
+ super.init(config);
+ try {
+ xsrf = xsrfInit();
+ } catch (XsrfException e) {
+ throw new ServletException("Cannot initialize XSRF", e);
+ }
+ }
+
+ /**
+ * Initialize the XSRF state for this service.
+ * <p>
+ * By default this method creates a unique XSRF key for this service. Service
+ * implementors may wish to override this method to use a pooled instance that
+ * relies upon a stable private key.
+ *
+ * @return new XSRF implementation. Null if the caller has overridden all
+ * relevant XSRF methods and is implementing their own XSRF protection
+ * algorithm.
+ * @throws XsrfException the XSRF utility could not be created.
+ */
+ protected XsrfUtil xsrfInit() throws XsrfException {
+ return new XsrfUtil();
+ }
+
+ /**
+ * Get the user specific token to protect per-user XSRF keys.
+ * <p>
+ * By default this method uses <code>getRemoteUser()</code>. Services may
+ * override this method to acquire a different property of the request, such
+ * as data from an HTTP cookie or an extended HTTP header.
+ *
+ * @param call current RPC being processed.
+ * @return the user identity; null if the user is anonymous.
+ */
+ protected String xsrfUser(final ActiveCall call) {
+ return call.httpRequest.getRemoteUser();
+ }
+
+ /**
+ * Verify the XSRF token submitted is valid.
+ * <p>
+ * By default this method validates the token, and refreshes it with a new
+ * token for the currently authenticated user.
+ *
+ * @param call current RPC being processed.
+ * @return true if the token was supplied and is valid; false otherwise.
+ * @throws XsrfException the token could not be validated due to an error that
+ * the client cannot recover from.
+ */
+ protected boolean xsrfValidate(final ActiveCall call) throws XsrfException {
+ final HttpServletRequest req = call.httpRequest;
+ final String user = xsrfUser(call);
+ final String path = req.getServletPath();
+ call.httpResponse.addHeader(XSRF_HEADER, xsrf.newToken(user, path));
+ return xsrf.checkToken(req.getHeader(XSRF_HEADER), user, path);
+ }
+
+ /**
+ * Lookup a method implemented by this servlet.
+ *
+ * @param methodName name of the method.
+ * @return the method handle; null if the method is not declared.
+ */
+ protected MethodHandle lookupMethod(final String methodName) {
+ return myMethods.get(methodName);
+ }
+
+ @Override
+ protected void doPost(final HttpServletRequest req,
+ final HttpServletResponse resp) throws IOException {
+ noCache(resp);
+
+ if (!Shared.JSON_TYPE.equals(req.getContentType())) {
+ error(resp, SC_BAD_REQUEST, "Invalid request Content-Type");
+ return;
+ }
+ if (!Shared.JSON_TYPE.equals(req.getHeader("Accept"))) {
+ error(resp, SC_BAD_REQUEST, "Must accept " + Shared.JSON_TYPE);
+ return;
+ }
+ if (req.getContentLength() == 0) {
+ error(resp, SC_BAD_REQUEST, "POST body required");
+ return;
+ }
+
+ final ActiveCall call;
+ try {
+ call = parseRequest(req);
+ } catch (NoSuchRemoteMethodException err) {
+ error(resp, SC_NOT_FOUND, "Not Found");
+ return;
+ }
+
+ if (call.method != null) {
+ call.httpRequest = req;
+ call.httpResponse = resp;
+
+ try {
+ if (!xsrfValidate(call)) {
+ error(resp, Shared.SC_INVALID_XSRF, Shared.SM_INVALID_XSRF);
+ return;
+ }
+ } catch (XsrfException e) {
+ error(resp, Shared.SC_INVALID_XSRF, Shared.SM_INVALID_XSRF);
+ return;
+ }
+
+ call.method.invoke(call.params, new AsyncCallback<Object>() {
+ public void onFailure(final Throwable c) {
+ call.error = c;
+ }
+
+ public void onSuccess(final Object r) {
+ call.result = r;
+ }
+ });
+ }
+
+ final String out = formatResult(call);
+ if (call.error != null) {
+ resp.setStatus(SC_INTERNAL_SERVER_ERROR);
+ }
+ RPCServletUtils.writeResponse(getServletContext(), resp, out,
+ RPCServletUtils.acceptsGzipEncoding(req)
+ && RPCServletUtils.exceedsUncompressedContentLengthLimit(out));
+ }
+
+ private static void noCache(final HttpServletResponse resp) {
+ resp.setHeader("Cache-Control", "no-cache");
+ resp.addDateHeader("Expires", System.currentTimeMillis());
+ }
+
+ private ActiveCall parseRequest(final HttpServletRequest req)
+ throws UnsupportedEncodingException, IOException {
+ final Reader in = new InputStreamReader(req.getInputStream(), ENC);
+ try {
+ try {
+ return parseRequest(in);
+ } catch (JsonParseException err) {
+ final ActiveCall call = new ActiveCall();
+ call.error = err;
+ return call;
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ private ActiveCall parseRequest(final Reader in) {
+ final GsonBuilder gb = new GsonBuilder();
+ gb.registerTypeAdapter(ActiveCall.class, new RpcRequestDeserializer(this));
+ return gb.create().fromJson(in, ActiveCall.class);
+ }
+
+ private String formatResult(final ActiveCall result)
+ throws UnsupportedEncodingException, IOException {
+ final StringWriter o = new StringWriter();
+ formatResult(result, o);
+ o.close();
+ return o.toString();
+ }
+
+ private void formatResult(final ActiveCall result, final Writer o) {
+ final GsonBuilder gb = new GsonBuilder();
+ gb.registerTypeAdapter(ActiveCall.class, new JsonSerializer<ActiveCall>() {
+ public JsonElement serialize(final ActiveCall src, final Type typeOfSrc,
+ final JsonSerializationContext context) {
+ final JsonObject r = new JsonObject();
+ r.addProperty("version", RPC_VERSION);
+ if (src.id != null) {
+ r.add("id", src.id);
+ }
+ if (src.error != null) {
+ final JsonObject error = new JsonObject();
+ error.addProperty("name", "JSONRPCError");
+ error.addProperty("code", 999);
+ error.addProperty("message", src.error.getMessage());
+ r.add("error", error);
+ } else {
+ r.add("result", context.serialize(src.result));
+ }
+ return r;
+ }
+ });
+ gb.create().toJson(result, o);
+ }
+
+ private static void error(final HttpServletResponse resp, final int status,
+ final String message) throws IOException {
+ resp.setStatus(status);
+ resp.setContentType("text/plain; charset=" + ENC);
+
+ final Writer w = new OutputStreamWriter(resp.getOutputStream(), ENC);
+ try {
+ w.write(message);
+ } finally {
+ w.close();
+ }
+ }
+
+ private static HashMap<String, MethodHandle> methods(final JsonServlet impl) {
+ final HashMap<String, MethodHandle> r = new HashMap<String, MethodHandle>();
+ for (final Method m : impl.getClass().getMethods()) {
+ if (!Modifier.isPublic(m.getModifiers())) {
+ continue;
+ }
+
+ if (m.getReturnType() != Void.TYPE) {
+ continue;
+ }
+
+ final Class<?>[] params = m.getParameterTypes();
+ if (params.length < 1) {
+ continue;
+ }
+
+ if (!params[params.length - 1].isAssignableFrom(AsyncCallback.class)) {
+ continue;
+ }
+
+ final MethodHandle h = new MethodHandle(impl, m);
+ r.put(h.getName(), h);
+ }
+ return r;
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/server/MethodHandle.java b/src/com/google/gwtjsonrpc/server/MethodHandle.java
new file mode 100644
index 0000000..6e746c1
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/MethodHandle.java
@@ -0,0 +1,88 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Pairing of a specific {@link JsonServlet} implementation and method.
+ */
+public class MethodHandle {
+ private final JsonServlet imp;
+ private final Method method;
+ private final Class<?>[] parameterTypes;
+
+ /**
+ * Create a new handle for a specific service implementation and method.
+ *
+ * @param imp instance of the service all calls will be made on.
+ * @param method Java method to invoke on <code>imp</code>. The last parameter
+ * of the method must accept an {@link AsyncCallback} and the method
+ * must return void.
+ */
+ MethodHandle(final JsonServlet imp, final Method method) {
+ this.imp = imp;
+ this.method = method;
+
+ final Class<?>[] args = method.getParameterTypes();
+ parameterTypes = new Class<?>[args.length - 1];
+ System.arraycopy(args, 0, parameterTypes, 0, parameterTypes.length);
+ }
+
+ /**
+ * @return unique name of the method within the service.
+ */
+ public String getName() {
+ return method.getName();
+ }
+
+ /**
+ * @return true if this method requires positional arguments.
+ */
+ public Class<?>[] getParamTypes() {
+ return parameterTypes;
+ }
+
+ /**
+ * Invoke this method with the specified arguments, updating the callback.
+ *
+ * @param arguments arguments to the method. May be the empty array if no
+ * parameters are declared beyond the AsyncCallback, but must not be
+ * null.
+ * @param callback the callback the implementation will invoke onSuccess or
+ * onFailure on as it performs its work. Only the last onSuccess or
+ * onFailure invocation matters.
+ */
+ public void invoke(final Object[] arguments,
+ final AsyncCallback<Object> callback) {
+ try {
+ final Object[] p = new Object[arguments.length + 1];
+ System.arraycopy(arguments, 0, p, 0, arguments.length);
+ p[p.length - 1] = callback;
+ method.invoke(imp, p);
+ } catch (IllegalAccessException e) {
+ callback.onFailure(e);
+ } catch (InvocationTargetException e) {
+ callback.onFailure(e);
+ } catch (RuntimeException e) {
+ callback.onFailure(e);
+ } catch (Error e) {
+ callback.onFailure(e);
+ }
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/server/NoSuchRemoteMethodException.java b/src/com/google/gwtjsonrpc/server/NoSuchRemoteMethodException.java
new file mode 100644
index 0000000..aee78a1
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/NoSuchRemoteMethodException.java
@@ -0,0 +1,19 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+/** Indicates the requested method is not known. */
+class NoSuchRemoteMethodException extends RuntimeException {
+}
diff --git a/src/com/google/gwtjsonrpc/server/RpcRequestDeserializer.java b/src/com/google/gwtjsonrpc/server/RpcRequestDeserializer.java
new file mode 100644
index 0000000..187f1c7
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/RpcRequestDeserializer.java
@@ -0,0 +1,92 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+final class RpcRequestDeserializer implements JsonDeserializer<ActiveCall> {
+ private static final String RPC_VERSION = JsonServlet.RPC_VERSION;
+ private final JsonServlet server;
+
+ RpcRequestDeserializer(final JsonServlet jsonServlet) {
+ server = jsonServlet;
+ }
+
+ public ActiveCall deserialize(final JsonElement json, final Type typeOfT,
+ final JsonDeserializationContext context) throws JsonParseException,
+ NoSuchRemoteMethodException {
+ if (!json.isJsonObject()) {
+ throw new JsonParseException("Expected object");
+ }
+
+ final JsonObject in = json.getAsJsonObject();
+ final ActiveCall req = new ActiveCall();
+ req.id = in.get("id");
+
+ final JsonElement version = in.get("version");
+ if (!isString(version) || !RPC_VERSION.equals(version.getAsString())) {
+ throw new JsonParseException("Expected version = " + RPC_VERSION);
+ }
+
+ final JsonElement method = in.get("method");
+ if (!isString(method)) {
+ throw new JsonParseException("Exepected method name as string");
+ }
+
+ req.method = server.lookupMethod(method.getAsString());
+ if (req.method == null) {
+ throw new NoSuchRemoteMethodException();
+ }
+
+ final Class<?>[] paramTypes = req.method.getParamTypes();
+ final JsonElement params = in.get("params");
+ if (params != null) {
+ if (!params.isJsonArray()) {
+ throw new JsonParseException("Expected params array");
+ }
+
+ final JsonArray paramsArray = params.getAsJsonArray();
+ if (paramsArray.size() != paramTypes.length) {
+ throw new JsonParseException("Expected " + paramTypes.length
+ + " parameter values in params array");
+ }
+
+ final Object[] r = new Object[paramTypes.length];
+ for (int i = 0; i < r.length; i++) {
+ r[i] = context.deserialize(paramsArray.get(i), paramTypes[i]);
+ }
+ req.params = r;
+ } else {
+ if (paramTypes.length != 0) {
+ throw new JsonParseException("Expected params array");
+ }
+ req.params = JsonServlet.NO_PARAMS;
+ }
+
+ return req;
+ }
+
+ private static boolean isString(final JsonElement e) {
+ return e != null && e.isJsonPrimitive()
+ && e.getAsJsonPrimitive().isString();
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/server/XsrfException.java b/src/com/google/gwtjsonrpc/server/XsrfException.java
new file mode 100644
index 0000000..7cf1622
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/XsrfException.java
@@ -0,0 +1,26 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+/** Indicates the requested method is not known. */
+class XsrfException extends Exception {
+ public XsrfException(final String message) {
+ super(message);
+ }
+
+ public XsrfException(final String message, final Throwable why) {
+ super(message, why);
+ }
+}
diff --git a/src/com/google/gwtjsonrpc/server/XsrfUtil.java b/src/com/google/gwtjsonrpc/server/XsrfUtil.java
new file mode 100644
index 0000000..f73b61d
--- /dev/null
+++ b/src/com/google/gwtjsonrpc/server/XsrfUtil.java
@@ -0,0 +1,215 @@
+// Copyright 2008 Google Inc.
+//
+// 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.google.gwtjsonrpc.server;
+
+import com.google.gwtjsonrpc.client.Shared;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import javax.crypto.Mac;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Utility function to compute and verify XSRF tokens.
+ * <p>
+ * {@link JsonServlet} uses this class to verify tokens appearing in the custom
+ * {@link Shared#XSRF_HEADER} HTTP header. The tokens protect against cross-site
+ * request forgery by depending upon the browser's security model. The classic
+ * browser security model prohibits a script from site A from reading any data
+ * received from site B. By sending unforgeable tokens from the server and
+ * asking the client to return them to us, the client script must have had read
+ * access to the token at some point and is therefore also from our server.
+ */
+public class XsrfUtil {
+ private static final int INT_SZ = 4;
+ private static final int MAX_XSRF_WINDOW = 4 * 60 * 60; // seconds
+ private static final String MAC_ALG = "HmacSHA1";
+
+ /**
+ * Generate a random key for use with the XSRF library.
+ *
+ * @return a new private key, base 64 encoded.
+ */
+ public static String generateRandomKey() {
+ final byte[] r = new byte[26];
+ new SecureRandom().nextBytes(r);
+ return encodeBase64(r);
+ }
+
+ private final SecretKeySpec key;
+ private final int tokenLength;
+
+ /**
+ * Create a new utility, using a randomly generated key.
+ *
+ * @throws XsrfException the JVM doesn't support the necessary algorithms.
+ */
+ public XsrfUtil() throws XsrfException {
+ this(generateRandomKey());
+ }
+
+ /**
+ * Create a new utility, using the specific key.
+ *
+ * @param keyBase64 base 64 encoded representation of the key.
+ * @throws XsrfException the JVM doesn't support the necessary algorithms.
+ */
+ public XsrfUtil(final String keyBase64) throws XsrfException {
+ key = new SecretKeySpec(decodeBase64(keyBase64), MAC_ALG);
+ tokenLength = INT_SZ + newMac().getMacLength();
+ }
+
+ /**
+ * Create a new token for the user and the resource.
+ *
+ * @param user name or other unique identification of the user.
+ * @param resource resource (e.g. servlet path) the token protects.
+ * @return a new token string, typically base 64 encoded.
+ * @throws XsrfException the JVM doesn't support the necessary algorithms to
+ * generate a token. XSRF services are simply not available.
+ */
+ public String newToken(final String user, final String resource)
+ throws XsrfException {
+ final byte[] buf = new byte[tokenLength];
+ encodeInt(buf, now());
+ computeToken(buf, user, resource);
+ return encodeBase64(buf);
+ }
+
+ /**
+ * Validate a returned token.
+ *
+ * @param tokenString a token string previously created by this class.
+ * @param user name or other unique identification of the user.
+ * @param resource resource (e.g. servlet path) the token protects.
+ * @return true if the token is valid; false if the token is null, the empty
+ * string, has expired, does not match the user and resource
+ * combination supplied, or is a forged token.
+ * @throws XsrfException the JVM doesn't support the necessary algorithms to
+ * generate a token. XSRF services are simply not available.
+ */
+ public boolean checkToken(final String tokenString, final String user,
+ final String resource) throws XsrfException {
+ if (tokenString == null || tokenString.length() == 0) {
+ return false;
+ }
+
+ final byte[] in = decodeBase64(tokenString);
+ if (in.length != tokenLength) {
+ return false;
+ }
+
+ if (Math.abs(decodeInt(in) - now()) > MAX_XSRF_WINDOW) {
+ return false;
+ }
+
+ final byte[] gen = new byte[tokenLength];
+ System.arraycopy(in, 0, gen, 0, INT_SZ);
+ computeToken(gen, user, resource);
+ return Arrays.equals(gen, in);
+ }
+
+ private void computeToken(final byte[] buf, final String user,
+ final String resource) throws XsrfException {
+ final Mac m = newMac();
+
+ m.update(buf, 0, INT_SZ);
+
+ m.update((byte) ':');
+ if (user != null) {
+ m.update(toBytes(user));
+ }
+
+ m.update((byte) ':');
+ if (resource != null) {
+ m.update(toBytes(resource));
+ }
+
+ try {
+ m.doFinal(buf, INT_SZ);
+ } catch (ShortBufferException e) {
+ throw new XsrfException("Unexpected token overflow", e);
+ }
+ }
+
+ private Mac newMac() throws XsrfException {
+ try {
+ final Mac m = Mac.getInstance(MAC_ALG);
+ m.init(key);
+ return m;
+ } catch (NoSuchAlgorithmException e) {
+ throw new XsrfException(MAC_ALG + " not supported", e);
+ } catch (InvalidKeyException e) {
+ throw new XsrfException("Invalid private key", e);
+ }
+ }
+
+ private static int now() {
+ return (int) (System.currentTimeMillis() / 1000L);
+ }
+
+ private static byte[] decodeBase64(final String s) {
+ return Base64.decodeBase64(toBytes(s));
+ }
+
+ private static String encodeBase64(final byte[] buf) {
+ return toString(Base64.encodeBase64(buf));
+ }
+
+ private static void encodeInt(final byte[] buf, int v) {
+ buf[3] = (byte) v;
+ v >>>= 8;
+
+ buf[2] = (byte) v;
+ v >>>= 8;
+
+ buf[1] = (byte) v;
+ v >>>= 8;
+
+ buf[0] = (byte) v;
+ }
+
+ private static int decodeInt(final byte[] buf) {
+ int r = buf[0] << 8;
+
+ r |= buf[1] & 0xff;
+ r <<= 8;
+
+ r |= buf[2] & 0xff;
+ return (r << 8) | (buf[3] & 0xff);
+ }
+
+ private static byte[] toBytes(final String s) {
+ final byte[] r = new byte[s.length()];
+ for (int k = r.length - 1; k >= 0; k--) {
+ r[k] = (byte) s.charAt(k);
+ }
+ return r;
+ }
+
+ private static String toString(final byte[] b) {
+ final StringBuilder r = new StringBuilder(b.length);
+ for (int i = 0; i < b.length; i++) {
+ r.append((char) b[i]);
+ }
+ return r.toString();
+ }
+}