Fast forward existing prototype work

pull/1/merge
Dave Syer 12 years ago
parent 80b151e2b3
commit fb6b224470

11
.gitignore vendored

@ -0,0 +1,11 @@
.gradle
*.sw?
.#*
*#
/build
.classpath
.project
.settings
bin
build
target

@ -0,0 +1,5 @@
language: java
install: mvn -U install --quiet -DskipTests=true
script: mvn clean test

@ -0,0 +1,111 @@
# Spring Bootstrap
Experimental work based on discussions at SpringOne2GX 2012. See also the 'bootstrap' branch of Spring.
## Elevator Pitch
Opinionated view of the Spring family so that new users can quickly get to the 'meat and potatoes'. Assumes no knowledge of the Java development ecosystem. Absolutely no code generation and no XML.
## Installing
You need to build from source for now, but when it's done instructions will look like this:
1) Get Java
Download and install the Java SDK from www.java.com
2) Get Spring
`curl -s try.springsource.org | bash` or use the Windows installer
3) Get to Work!
spr run yoursourcefile.groovy
## What? It's Groovy then? or like Grails? or another Roo?
There is a command line tool that uses Groovy underneath so that we can present simple snippets that can just run:
@Controller
class ThisWillActuallyRun {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!"
}
}
By inspecting the code for well known annotations we can `@Grab` appropriate dependencies and also dynamically add `import` statements. Groovy makes this really easy.
If you don't want to use the command line tool, and you would rather work using Java and an IDE you can. Just add a `main()` method that calls `SpringApplication` and add `@EnableAutoConfiguration`:
import org.springframework.bootstrap.*;
import org.springframework.context.annotation.*;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class MyApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApplication.class, args);
}
}
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@Controller
public class SampleController {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!"
}
}
## Under the hood
There are a number of disparate parts of Bootstrap. Here are the important classes:
### The Spring CLI
The 'spr' command line application compiles and runs Groovy source, adding `import` statements and `@Grab` annotations. The application can also watch files, automatically recompiling and restarting when they change.
### SpringApplication
The `SpringApplication` class provides the main entry point for a standalone Spring Application. Its sole job is to create and refresh an appropriate Spring `ApplicationContext`. Any contained beans that implements `CommandLineRunner` will be executed after the context has started. A `SpringApplication` can load beans from a number of different sources, including classes, packages (scanned) or XML files. By default a `AnnotationConfigApplicationContext` or `AnnotationConfigEmbeddedWebApplicationContext` depending on your classpath.
### EmbeddedWebApplicationContext
The `EmbeddedWebApplicationContext` will probably be part of Spring 4.0. It provides a Spring 'WebApplicationContext' that can bootstrap itself and start and embedded servlet container. Support is provided for Tomcat and Jetty.
### @EnableAutoConfigure
The `@EnableAutoConfigure` can be used on a `@Configuration` class to trigger auto-configuration of the Spring context. Auto-configuration attempts to guess what beans a user might want based on their classpath. For example, If a 'HSQLDB' is on the classpath the user probably wants an in-memory database to be defined. Auto-configuration will back away as the user starts to define their own beans.
### @Conditional
The `@Conditional` annotation will probably be part of Spring 4.0. It provides allows `@Configuration` classes to be skipped depending on conditions. Bootstrap provides `@ConditionalOnBean`, `@ConditionalOnMissingBean` and `@ConditionalOnClass` annotations are used when defining auto-configuration classes.
## Building the code
Use maven to build the source code.
mvn clean install
## Importing into eclipse
You can use m2e or `maven eclipse:eclipse`.
Project specific settings are configured for source formatting. If you are using m2e please follow these steps to install eclipse support:
* Select `Install new software` from the `help` menu
* Click `Add...` to add a new repository
* Click the `Archive...` button
* Select `org.eclipse.m2e.maveneclipse.site-0.0.1-SNAPSHOT-site.zip` from the `eclipse` folder in this checkout
* Install "Maven Integration for the maven-eclipse-plugin"
If you prefer you can import settings manually from the `/eclipse` folder.
## Samples
The following samples are included. To run use `java -jar <archive>-full.jar`
* spring-bootstrap-simple-sample - A simple command line application
* spring-bootstrap-jetty-sample - Embedded Jetty
* spring-bootstrap-tomcat-sample - Embedded Tomcat
* spring-bootstrap-data-sample - Spring Data JPA + Hibernate + HSQLDB

@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="Spring Bootstrap" version="12">
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="90"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="90"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
</profile>
</profiles>

@ -0,0 +1,385 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.codeComplete.argumentPrefixes=
org.eclipse.jdt.core.codeComplete.argumentSuffixes=
org.eclipse.jdt.core.codeComplete.fieldPrefixes=
org.eclipse.jdt.core.codeComplete.fieldSuffixes=
org.eclipse.jdt.core.codeComplete.localPrefixes=
org.eclipse.jdt.core.codeComplete.localSuffixes=
org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
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.doc.comment.support=enabled
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
org.eclipse.jdt.core.compiler.problem.deadCode=warning
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=default
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nullReference=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.6
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_annotation=0
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=0
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=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
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_resources_in_try=80
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.alignment_for_union_type_in_multicatch=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=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
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=1
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=false
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=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
org.eclipse.jdt.core.formatter.comment.line_length=90
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
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.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
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=false
org.eclipse.jdt.core.formatter.indentation.size=8
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
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_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=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_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=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=do not 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=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=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=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_try=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_semicolon_in_try_resources=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=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_try=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_try=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_semicolon_in_try_resources=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.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
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=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=90
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=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true

File diff suppressed because one or more lines are too long

@ -0,0 +1,398 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<prerequisites>
<maven>3.0.0</maven>
</prerequisites>
<modules>
<module>spring-bootstrap</module>
<module>spring-bootstrap-service</module>
<module>spring-bootstrap-applications</module>
<module>spring-bootstrap-launcher</module>
<module>spring-bootstrap-samples</module>
<module>spring-bootstrap-cli</module>
</modules>
<properties>
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<dependency.springframework.version>4.0.0.BOOTSTRAP-SNAPSHOT</dependency.springframework.version>
<dependency.security.javaconfig.version>1.0.0.CI-SNAPSHOT</dependency.security.javaconfig.version>
<main.basedir>${project.basedir}</main.basedir>
</properties>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://maven.springframework.org/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.13</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.13</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.2</version>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<useProjectReferences>false</useProjectReferences>
<additionalConfig>
<file>
<name>.settings/org.eclipse.jdt.ui.prefs</name>
<location>${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs</location>
</file>
<file>
<name>.settings/org.eclipse.jdt.core.prefs</name>
<location>${main.basedir}/eclipse/org.eclipse.jdt.core.prefs</location>
</file>
</additionalConfig>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>verify</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
<junitArtifactName>junit:junit</junitArtifactName>
<systemPropertyVariables>
<java.security.egd>file:/dev/./urandom</java.security.egd>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.0</version>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>7.0.39</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>7.0.39</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>7.0.39</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>8.1.9.v20130131</version>
<exclusions>
<exclusion>
<artifactId>javax.servlet</artifactId>
<groupId>org.eclipse.jetty.orbit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>8.1.9.v20130131</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp</artifactId>
<version>8.1.9.v20130131</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.9.Final</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-javaconfig</artifactId>
<version>${dependency.security.javaconfig.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${dependency.springframework.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.3.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.12</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-applications</artifactId>
<packaging>pom</packaging>
<properties>
<main.basedir>${project.basedir}/..</main.basedir>
</properties>
<modules>
<module>spring-bootstrap-application</module>
<module>spring-bootstrap-batch-application</module>
<module>spring-bootstrap-integration-application</module>
<module>spring-bootstrap-jpa-application</module>
<module>spring-bootstrap-web-application</module>
</modules>
</project>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-applications</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-application</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-applications</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-batch-application</artifactId>
<packaging>pom</packaging>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-application</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</project>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-applications</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-integration-application</artifactId>
<packaging>pom</packaging>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-application</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-applications</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-jpa-application</artifactId>
<packaging>pom</packaging>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-application</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-applications</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-web-application</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-application</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>spring-bootstrap-parent</artifactId>
<groupId>org.springframework.bootstrap</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-bootstrap-cli</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer>
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer>
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer />
<transformer>
<mainClass>${start-class}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-distribution</id>
<phase>install</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<inherited>false</inherited>
<configuration>
<descriptors>
<descriptor>src/main/assembly/descriptor.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>objenesis</artifactId>
<groupId>org.objenesis</groupId>
</exclusion>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<properties>
<main.basedir>${project.basedir}/..</main.basedir>
<start-class>org.springframework.bootstrap.cli.SpringBootstrapCli</start-class>
</properties>
</project>

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-cli</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${project.basedir}/..</main.basedir>
<start-class>org.springframework.bootstrap.cli.SpringBootstrapCli</start-class>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer
implementation="org.springframework.bootstrap.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${start-class}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<descriptors>
<descriptor>src/main/assembly/descriptor.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-distribution</id>
<phase>install</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ivy</groupId>
<artifactId>ivy</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,29 @@
package org.test
@GrabResolver(name='spring-milestone', root='http://repo.springframework.org/milestone')
@GrabResolver(name='spring-snapshot', root='http://repo.springframework.org/snapshot')
@Grab("org.springframework.bootstrap:spring-bootstrap:0.0.1-SNAPSHOT")
@Grab("org.springframework:spring-context:4.0.0.BOOTSTRAP-SNAPSHOT")
@org.springframework.bootstrap.context.annotation.EnableAutoConfiguration
@org.springframework.stereotype.Component
class Example implements org.springframework.bootstrap.CommandLineRunner {
@org.springframework.beans.factory.annotation.Autowired
private MyService myService;
public void run(String... args) {
print "Hello " + this.myService.sayWorld();
}
}
@org.springframework.stereotype.Service
class MyService {
public String sayWorld() {
return "World!";
}
}

@ -0,0 +1,24 @@
@org.springframework.bootstrap.context.annotation.EnableAutoConfiguration
@Controller
class Example {
@Autowired
private MyService myService;
@RequestMapping("/")
@ResponseBody
public String helloWorld() {
return myService.sayWorld();
}
}
@Service
class MyService {
public String sayWorld() {
return "World!";
}
}

@ -0,0 +1,30 @@
<assembly>
<id>dist</id>
<formats>
<format>zip</format>
<format>dir</format>
</formats>
<baseDirectory>spring-${project.version}</baseDirectory>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/scripts</directory>
<outputDirectory>bin</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>bin</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<filtered>true</filtered>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<includes>
<include>org.springframework.bootstrap:spring-bootstrap-cli:jar:*</include>
</includes>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>

@ -0,0 +1,81 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import java.io.IOException;
import java.io.PrintStream;
/**
* Abstract {@link Command} implementation.
*
* @author Phillip Webb
*/
public abstract class AbstractCommand implements Command {
private String name;
private boolean optionCommand;
private String description;
/**
* Create a new {@link AbstractCommand} instance.
* @param name the name of the command
* @param description the command description
*/
public AbstractCommand(String name, String description) {
this(name, description, false);
}
/**
* Create a new {@link AbstractCommand} instance.
* @param name the name of the command
* @param description the command description
* @param optionCommand if this command is an option command (see
* {@link Command#isOptionCommand()}
*/
public AbstractCommand(String name, String description, boolean optionCommand) {
this.name = name;
this.description = description;
this.optionCommand = optionCommand;
}
@Override
public String getName() {
return this.name;
}
@Override
public String getDescription() {
return this.description;
}
@Override
public boolean isOptionCommand() {
return this.optionCommand;
}
@Override
public String getUsageHelp() {
return null;
}
@Override
public void printHelp(PrintStream out) throws IOException {
}
}

@ -0,0 +1,96 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
/**
* Runtime exception wrapper that defines additional {@link Option}s that are understood
* by the {@link SpringBootstrapCli}.
*
* @author Phillip Webb
*/
public class BootstrapCliException extends RuntimeException {
private static final long serialVersionUID = 0L;
private final EnumSet<Option> options;
/**
* Create a new {@link BootstrapCliException} with the specified options.
* @param options the exception options
*/
public BootstrapCliException(Option... options) {
this.options = asEnumSet(options);
}
/**
* Create a new {@link BootstrapCliException} with the specified options.
* @param message the exception message to display to the user
* @param options the exception options
*/
public BootstrapCliException(String message, Option... options) {
super(message);
this.options = asEnumSet(options);
}
/**
* Create a new {@link BootstrapCliException} with the specified options.
* @param message the exception message to display to the user
* @param cause the underlying cause
* @param options the exception options
*/
public BootstrapCliException(String message, Throwable cause, Option... options) {
super(message, cause);
this.options = asEnumSet(options);
}
private EnumSet<Option> asEnumSet(Option[] options) {
if (options == null || options.length == 0) {
return EnumSet.noneOf(Option.class);
}
return EnumSet.copyOf(Arrays.asList(options));
}
/**
* Returns options a set of options that are understood by the
* {@link SpringBootstrapCli}.
*/
public Set<Option> getOptions() {
return Collections.unmodifiableSet(this.options);
}
/**
* Specific options understood by the {@link SpringBootstrapCli}.
*/
public static enum Option {
/**
* Print basic CLI usage information.
*/
SHOW_USAGE,
/**
* Print the stack-trace of the exception.
*/
STACK_TRACE
}
}

@ -0,0 +1,68 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import java.io.IOException;
import java.io.PrintStream;
/**
* A single command that can be run from the CLI.
*
* @author Phillip Webb
* @see #run(String...)
*/
public interface Command {
/**
* Returns the name of the command.
*/
String getName();
/**
* Returns {@code true} if this is an 'option command'. An option command is a special
* type of command that usually makes more sense to present as if it is an option. For
* example '--help'.
*/
boolean isOptionCommand();
/**
* Returns a description of the command.
*/
String getDescription();
/**
* Returns usage help for the command. This should be a simple one-line string
* describing basic usage. eg. '[options] &lt;file&gt;'. Do not include the name of
* the command in this string.
*/
String getUsageHelp();
/**
* Prints help for the command.
* @param out the output writer to display help
* @throws IOException
*/
void printHelp(PrintStream out) throws IOException;
/**
* Run the command.
* @param args command arguments (this will not include the command itself)
* @throws Exception
*/
void run(String... args) throws Exception;
}

@ -0,0 +1,54 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import static java.util.Arrays.*;
/**
* {@link Command} to 'create' a new spring groovy script.
*
* @author Phillip Webb
*/
public class CreateCommand extends OptionParsingCommand {
public CreateCommand() {
super("create", "Create an new spring groovy script");
}
@Override
public String getUsageHelp() {
return "[options] <file>";
}
@Override
protected OptionParser createOptionParser() {
OptionParser parser = new OptionParser();
parser.acceptsAll(asList("overwite", "f"), "Overwrite any existing file");
parser.accepts("type", "Create a specific application type").withOptionalArg()
.ofType(String.class).describedAs("web, batch, integration");
return parser;
}
@Override
protected void run(OptionSet options) {
throw new IllegalStateException("Not implemented"); // FIXME
}
}

@ -0,0 +1,28 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
/**
* Exception thrown when no CLI options are specified.
*
* @author Phillip Webb
*/
class NoArgumentsException extends BootstrapCliException {
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,32 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
/**
* Exception thrown when the 'help' command is issued without any arguments.
*
* @author Phillip Webb
*/
class NoHelpCommandArgumentsException extends BootstrapCliException {
private static final long serialVersionUID = 1L;
public NoHelpCommandArgumentsException() {
super(Option.SHOW_USAGE);
}
}

@ -0,0 +1,33 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
/**
* Exception thrown when an unknown command is specified.
*
* @author Phillip Webb
*/
class NoSuchCommandException extends BootstrapCliException {
private static final long serialVersionUID = 1L;
public NoSuchCommandException(String name) {
super(String.format("%1$s: '%2$s' is not a valid command. See '%1$s --help'.",
SpringBootstrapCli.CLI_APP, name));
}
}

@ -0,0 +1,32 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
/**
* Exception thrown when an unknown root option is specified. This only applies to
* {@link Command#isOptionCommand() option command}.
*
* @author Phillip Webb
*/
class NoSuchOptionException extends BootstrapCliException {
private static final long serialVersionUID = 1L;
public NoSuchOptionException(String name) {
super("Unknown option: --" + name, Option.SHOW_USAGE);
}
}

@ -0,0 +1,54 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import java.io.IOException;
import java.io.PrintStream;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
/**
* Base class for any {@link Command}s that use an {@link OptionParser}.
*
* @author Phillip Webb
*/
public abstract class OptionParsingCommand extends AbstractCommand {
private OptionParser parser;
public OptionParsingCommand(String name, String description) {
super(name, description);
this.parser = createOptionParser();
}
protected abstract OptionParser createOptionParser();
@Override
public void printHelp(PrintStream out) throws IOException {
this.parser.printHelpOn(out);
}
@Override
public final void run(String... args) throws Exception {
OptionSet options = parser.parse(args);
run(options);
}
protected abstract void run(OptionSet options) throws Exception;
}

@ -0,0 +1,146 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import java.awt.Desktop;
import java.io.File;
import java.util.List;
import java.util.logging.Level;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.springframework.bootstrap.cli.runner.BootstrapRunner;
import org.springframework.bootstrap.cli.runner.BootstrapRunnerConfiguration;
import static java.util.Arrays.asList;
/**
* {@link Command} to 'run' a spring groovy script.
*
* @author Phillip Webb
* @see BootstrapRunner
*/
public class RunCommand extends OptionParsingCommand {
private OptionSpec<Void> noWatchOption; // FIXME
private OptionSpec<Void> editOption;
private OptionSpec<Void> noGuessImportsOption;
private OptionSpec<Void> noGuessDependenciesOption;
private OptionSpec<Void> verboseOption;
private OptionSpec<Void> quiteOption;
public RunCommand() {
super("run", "Run a spring groovy script");
}
@Override
public String getUsageHelp() {
return "[options] <file>";
}
@Override
protected OptionParser createOptionParser() {
OptionParser parser = new OptionParser();
this.noWatchOption = parser.accepts("no-watch",
"Do not watch the specified file for changes");
this.editOption = parser.acceptsAll(asList("edit", "e"),
"Open the file with the default system editor");
this.noGuessImportsOption = parser.accepts("no-guess-imports",
"Do not attempt to guess imports");
this.noGuessDependenciesOption = parser.accepts("no-guess-dependencies",
"Do not attempt to guess dependencies");
this.verboseOption = parser.acceptsAll(asList("verbose", "v"), "Verbose logging");
this.quiteOption = parser.acceptsAll(asList("quiet", "q"), "Quiet logging");
return parser;
}
@Override
protected void run(OptionSet options) throws Exception {
List<String> nonOptionArguments = options.nonOptionArguments();
File file = getFileArgument(nonOptionArguments);
List<String> args = nonOptionArguments.subList(1, nonOptionArguments.size());
if (options.has(this.editOption)) {
Desktop.getDesktop().edit(file);
}
BootstrapRunnerConfiguration configuration = new BootstrapRunnerConfigurationAdapter(
options);
new BootstrapRunner(configuration, file, args.toArray(new String[args.size()]))
.compileAndRun();
}
private File getFileArgument(List<String> nonOptionArguments) {
if (nonOptionArguments.size() == 0) {
throw new RuntimeException("Please specify a file to run");
}
String filename = nonOptionArguments.get(0);
File file = new File(filename);
if (!file.isFile() || !file.canRead()) {
throw new RuntimeException("Unable to read '" + filename + "'");
}
return file;
}
/**
* Simple adapter class to present the {@link OptionSet} as a
* {@link BootstrapRunnerConfiguration}.
*/
private class BootstrapRunnerConfigurationAdapter implements
BootstrapRunnerConfiguration {
private OptionSet options;
public BootstrapRunnerConfigurationAdapter(OptionSet options) {
this.options = options;
}
@Override
public boolean isWatchForFileChanges() {
return !this.options.has(RunCommand.this.noWatchOption);
}
@Override
public boolean isGuessImports() {
return !this.options.has(RunCommand.this.noGuessImportsOption);
}
@Override
public boolean isGuessDependencies() {
return !this.options.has(RunCommand.this.noGuessDependenciesOption);
}
@Override
public Level getLogLevel() {
if (this.options.has(RunCommand.this.verboseOption)) {
return Level.FINEST;
}
if (this.options.has(RunCommand.this.quiteOption)) {
return Level.OFF;
}
return Level.INFO;
}
}
}

@ -0,0 +1,214 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
/**
* Spring Bootstrap Command Line Interface. This is the main entry-point for the spring
* bootstrap command line application. This class will parse input arguments and delegate
* to a suitable {@link Command} implementation based on the first argument.
*
* <p>
* The '-d' and '--debug' switches are handled by this class, however, most argument
* parsing is left to the {@link Command} implementation. The {@link OptionParsingCommand}
* class provides a convenient base for command that need to parse arguments.
*
* @author Phillip Webb
* @see #main(String...)
* @see BootstrapCliException
* @see Command
* @see OptionParsingCommand
*/
public class SpringBootstrapCli {
public static final String CLI_APP = "spr";
private static final Set<BootstrapCliException.Option> NO_EXCEPTION_OPTIONS = EnumSet
.noneOf(BootstrapCliException.Option.class);
private List<Command> commands;
/**
* Create a new {@link SpringBootstrapCli} implementation with the default set of
* commands.
*/
public SpringBootstrapCli() {
setCommands(Arrays.asList(new VersionCommand(), new RunCommand(),
new CreateCommand()));
}
/**
* Set the command available to the CLI. Primarily used to support testing. NOTE: The
* 'help' command will be automatically provided in addition to this list.
* @param commands the commands to add
*/
protected void setCommands(List<? extends Command> commands) {
this.commands = new ArrayList<Command>(commands);
this.commands.add(0, new HelpCommand());
}
/**
* Run the CLI and handle and errors.
* @param args the input arguments
* @return a return status code (non zero is used to indicate an error)
*/
public int runAndHandleErrors(String... args) {
String[] argsWithoutDebugFlags = removeDebugFlags(args);
boolean debug = argsWithoutDebugFlags.length != args.length;
try {
run(argsWithoutDebugFlags);
return 0;
} catch (NoArgumentsException ex) {
showUsage();
return 1;
} catch (Exception ex) {
Set<BootstrapCliException.Option> options = NO_EXCEPTION_OPTIONS;
if (ex instanceof BootstrapCliException) {
options = ((BootstrapCliException) ex).getOptions();
}
errorMessage(ex.getMessage());
if (options.contains(BootstrapCliException.Option.SHOW_USAGE)) {
showUsage();
}
if (debug || options.contains(BootstrapCliException.Option.STACK_TRACE)) {
printStackTrace(ex);
}
return 1;
}
}
/**
* Parse the arguments and run a suitable command.
* @param args the arguments
* @throws Exception
*/
protected void run(String... args) throws Exception {
if (args.length == 0) {
throw new NoArgumentsException();
}
String commandName = args[0];
String[] commandArguments = Arrays.copyOfRange(args, 1, args.length);
find(commandName).run(commandArguments);
}
private Command find(String name) {
boolean isOption = name.startsWith("--");
if (isOption) {
name = name.substring(2);
}
for (Command candidate : this.commands) {
if ((isOption && candidate.isOptionCommand() || !isOption)
&& candidate.getName().equals(name)) {
return candidate;
}
}
throw (isOption ? new NoSuchOptionException(name) : new NoSuchCommandException(
name));
}
protected void showUsage() {
System.out.print("usage: " + CLI_APP + " ");
for (Command command : this.commands) {
if (command.isOptionCommand()) {
System.out.print("[--" + command.getName() + "] ");
}
}
System.out.println("");
System.out.println(" <command> [<args>]");
System.out.println("");
System.out.println("Available commands are:");
for (Command command : this.commands) {
if (!command.isOptionCommand()) {
System.out.println(String.format(" %1$-15s %2$s", command.getName(),
command.getDescription()));
}
}
System.out.println("");
System.out
.println("See 'spr help <command>' for more information on a specific command.");
}
protected void errorMessage(String message) {
System.err.println(message == null ? "Unexpected error" : message);
}
protected void printStackTrace(Exception ex) {
System.err.println("");
ex.printStackTrace(System.err);
System.err.println("");
}
private String[] removeDebugFlags(String[] args) {
List<String> rtn = new ArrayList<String>(args.length);
for (String arg : args) {
if (!("-d".equals(arg) || "--debug".equals(arg))) {
rtn.add(arg);
}
}
return rtn.toArray(new String[rtn.size()]);
}
/**
* Internal {@link Command} used for 'help' and '--help' requests.
*/
private class HelpCommand extends AbstractCommand {
public HelpCommand() {
super("help", "Show command help", true);
}
@Override
public void run(String... args) throws Exception {
if (args.length == 0) {
throw new NoHelpCommandArgumentsException();
}
String commandName = args[0];
for (Command command : SpringBootstrapCli.this.commands) {
if (!command.isOptionCommand() && command.getName().equals(commandName)) {
System.out.println(CLI_APP + " " + command.getName() + " - "
+ command.getDescription());
System.out.println();
if (command.getUsageHelp() != null) {
System.out.println("usage: " + CLI_APP + " " + command.getName()
+ " " + command.getUsageHelp());
System.out.println();
}
command.printHelp(System.out);
return;
}
}
throw new NoSuchCommandException(commandName);
}
}
/**
* The main CLI entry-point.
* @param args CLI arguments
*/
public static void main(String... args) {
int exitCode = new SpringBootstrapCli().runAndHandleErrors(args);
if (exitCode != 0) {
System.exit(exitCode);
}
}
}

@ -0,0 +1,35 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli;
/**
* {@link Command} to displat the 'version' number.
*
* @author Phillip Webb
*/
public class VersionCommand extends AbstractCommand {
public VersionCommand() {
super("version", "Show the version", true);
}
@Override
public void run(String... args) {
throw new IllegalStateException("Not implemented"); // FIXME
}
}

@ -0,0 +1,45 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
/**
* General purpose AST utilities.
*
* @author Phillip Webb
*/
public abstract class AstUtils {
/**
* Determine if an {@link AnnotatedNode} has one or more of the specified annotations.
*/
public static boolean hasLeastOneAnnotation(AnnotatedNode node, String... annotations) {
for (AnnotationNode annotationNode : node.getAnnotations()) {
for (String annotation : annotations) {
if (annotation.equals(annotationNode.getClassNode().getName())) {
return true;
}
}
}
return false;
}
}

@ -0,0 +1,83 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
/**
* Strategy that can be used to apply some auto-configuration during the
* {@link CompilePhase#CONVERSION} Groovy compile phase.
*
* @author Phillip Webb
*/
public abstract class CompilerAutoConfiguration {
/**
* Strategy method used to determine when compiler auto-configuration should be
* applied. Defaults to always.
* @param classNode the class node
* @return {@code true} if the compiler should be auto configured using this class. If
* this method returns {@code false} no other strategy methods will be called.
*/
public boolean matches(ClassNode classNode) {
return true;
}
/**
* Apply any dependency customizations. This method will only be called if
* {@link #matches} returns {@code true}.
* @param dependencies dependency customizer
* @throws CompilationFailedException
*/
public void applyDependencies(DependencyCustomizer dependencies)
throws CompilationFailedException {
}
/**
* Apply any import customizations. This method will only be called if
* {@link #matches} returns {@code true}.
* @param imports import customizer
* @throws CompilationFailedException
*/
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
}
/**
* Apply any customizations to the main class. This method will only be called if
* {@link #matches} returns {@code true}. This method is useful when a groovy file
* defines more than one class but customization only applies to the first class.
*/
public void applyToMainClass(GroovyClassLoader loader,
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
}
/**
* Apply any additional configuration.
*/
public void apply(GroovyClassLoader loader,
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
}
}

@ -0,0 +1,130 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler;
import groovy.grape.Grape;
import groovy.lang.Grapes;
import groovy.lang.GroovyClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Customizer that allows dependencies to be added during compilation. Delegates to Groovy
* {@link Grapes} to actually resolve dependencies. This class provides a fluent API for
* conditionally adding dependencies. For example:
* {@code dependencies.ifMissing("com.corp.SomeClass").add(group, module, version)}.
*
* @author Phillip Webb
*/
public class DependencyCustomizer {
private final GroovyClassLoader loader;
private final List<Map<String, Object>> dependencies;
/**
* Create a new {@link DependencyCustomizer} instance. The {@link #call()} method must
* be used to actually resolve dependencies.
* @param loader
*/
public DependencyCustomizer(GroovyClassLoader loader) {
this.loader = loader;
this.dependencies = new ArrayList<Map<String, Object>>();
}
/**
* Create a new nested {@link DependencyCustomizer}.
* @param parent
*/
protected DependencyCustomizer(DependencyCustomizer parent) {
this.loader = parent.loader;
this.dependencies = parent.dependencies;
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if the specified
* class names are not on the class path.
* @param classNames the class names to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifMissingClasses(final String... classNames) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String classname : classNames) {
try {
DependencyCustomizer.this.loader.loadClass(classname);
return false;
} catch (Exception e) {
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Add a single dependencies.
* @param group the group ID
* @param module the module ID
* @param version the version
* @return this {@link DependencyCustomizer} for continued use
*/
@SuppressWarnings("unchecked")
public DependencyCustomizer add(String group, String module, String version) {
if (canAdd()) {
Map<String, Object> dependency = new HashMap<String, Object>();
dependency.put("group", group);
dependency.put("module", module);
dependency.put("version", version);
dependency.put("transitive", true);
return add(dependency);
}
return this;
}
/**
* Add a dependencies.
* @param dependencies a map of the dependencies to add.
* @return this {@link DependencyCustomizer} for continued use
*/
public DependencyCustomizer add(Map<String, Object>... dependencies) {
this.dependencies.addAll(Arrays.asList(dependencies));
return this;
}
/**
* Strategy called to test if dependencies can be added. Subclasses override as
* requred.
*/
protected boolean canAdd() {
return true;
}
/**
* Apply the dependencies.
*/
void call() {
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("classLoader", this.loader);
Grape.grab(args, this.dependencies.toArray(new Map[this.dependencies.size()]));
}
}

@ -0,0 +1,88 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler;
import groovy.lang.GroovyClassLoader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.SourceUnit;
/**
* Extension of the {@link GroovyClassLoader} that support for obtaining '.class' files as
* resources.
*
* @author Phillip Webb
*/
class ExtendedGroovyClassLoader extends GroovyClassLoader {
private Map<String, byte[]> classResources = new HashMap<String, byte[]>();
public ExtendedGroovyClassLoader(ClassLoader loader, CompilerConfiguration config) {
super(loader, config);
}
@Override
public InputStream getResourceAsStream(String name) {
InputStream resourceStream = super.getResourceAsStream(name);
if (resourceStream == null) {
byte[] bytes = this.classResources.get(name);
resourceStream = bytes == null ? null : new ByteArrayInputStream(bytes);
}
return resourceStream;
}
@Override
protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
InnerLoader loader = AccessController
.doPrivileged(new PrivilegedAction<InnerLoader>() {
@Override
public InnerLoader run() {
return new InnerLoader(ExtendedGroovyClassLoader.this);
}
});
return new ExtendedClassCollector(loader, unit, su);
}
/**
* Inner collector class used to track as classes are added.
*/
protected class ExtendedClassCollector extends ClassCollector {
protected ExtendedClassCollector(InnerLoader loader, CompilationUnit unit,
SourceUnit su) {
super(loader, unit, su);
}
@Override
protected Class<?> createClass(byte[] code, ClassNode classNode) {
Class<?> createdClass = super.createClass(code, classNode);
ExtendedGroovyClassLoader.this.classResources.put(classNode.getName()
.replace(".", "/") + ".class", code);
return createdClass;
}
}
}

@ -0,0 +1,148 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler;
import groovy.lang.GroovyClassLoader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.bootstrap.cli.compiler.autoconfigure.SpringBootstrapCompilerAutoConfiguration;
import org.springframework.bootstrap.cli.compiler.autoconfigure.SpringMvcCompilerAutoConfiguration;
/**
* Compiler for Groovy source files. Primarily a simple Facade for
* {@link GroovyClassLoader#parseClass(File)} with the following additional features:
* <ul>
* <li>{@link CompilerAutoConfiguration} strategies will de applied during compilation</li>
*
* <li>Multiple classes can be returned if the Groovy source defines more than one Class</li>
*
* <li>Generated class files can also be loaded using
* {@link ClassLoader#getResource(String)}</li>
* <ul>
*
* @author Phillip Webb
*/
public class GroovyCompiler {
// FIXME could be a strategy
private static final CompilerAutoConfiguration[] COMPILER_AUTO_CONFIGURATIONS = {
new SpringBootstrapCompilerAutoConfiguration(),
new SpringMvcCompilerAutoConfiguration(),
new SpringBootstrapCompilerAutoConfiguration() };
private GroovyCompilerConfiguration configuration;
private ExtendedGroovyClassLoader loader;
/**
* Create a new {@link GroovyCompiler} instance.
* @param configuration the compiler configuration
*/
public GroovyCompiler(final GroovyCompilerConfiguration configuration) {
this.configuration = configuration;
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
this.loader = new ExtendedGroovyClassLoader(getClass().getClassLoader(),
compilerConfiguration);
compilerConfiguration
.addCompilationCustomizers(new CompilerAutoConfigureCustomizer());
}
/**
* Compile the specified Groovy source files, applying any
* {@link CompilerAutoConfiguration}s. All classes defined in the file will be
* returned from this method with the first item being the primary class (defined at
* the top of the file).
* @param file the file to compile
* @return compiled classes
* @throws CompilationFailedException
* @throws IOException
*/
public Class<?>[] compile(File file) throws CompilationFailedException, IOException {
this.loader.clearCache();
List<Class<?>> classes = new ArrayList<Class<?>>();
Class<?> mainClass = this.loader.parseClass(file);
for (Class<?> loadedClass : this.loader.getLoadedClasses()) {
classes.add(loadedClass);
}
classes.remove(mainClass);
classes.add(0, mainClass);
return classes.toArray(new Class<?>[classes.size()]);
}
/**
* {@link CompilationCustomizer} to call {@link CompilerAutoConfiguration}s.
*/
private class CompilerAutoConfigureCustomizer extends CompilationCustomizer {
public CompilerAutoConfigureCustomizer() {
super(CompilePhase.CONVERSION);
}
@Override
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
throws CompilationFailedException {
ImportCustomizer importCustomizer = new ImportCustomizer();
// Early sweep to get dependencies
DependencyCustomizer dependencyCustomizer = new DependencyCustomizer(
GroovyCompiler.this.loader);
for (CompilerAutoConfiguration autoConfiguration : COMPILER_AUTO_CONFIGURATIONS) {
if (autoConfiguration.matches(classNode)) {
if (GroovyCompiler.this.configuration.isGuessDependencies()) {
autoConfiguration.applyDependencies(dependencyCustomizer);
}
}
}
dependencyCustomizer.call();
// Additional auto configuration
for (CompilerAutoConfiguration autoConfiguration : COMPILER_AUTO_CONFIGURATIONS) {
if (autoConfiguration.matches(classNode)) {
if (GroovyCompiler.this.configuration.isGuessImports()) {
autoConfiguration.applyImports(importCustomizer);
importCustomizer.call(source, context, classNode);
}
if (source.getAST().getClasses().size() > 0
&& classNode.equals(source.getAST().getClasses().get(0))) {
autoConfiguration.applyToMainClass(GroovyCompiler.this.loader,
GroovyCompiler.this.configuration, context, source,
classNode);
}
autoConfiguration
.apply(GroovyCompiler.this.loader,
GroovyCompiler.this.configuration, context, source,
classNode);
}
}
importCustomizer.call(source, context, classNode);
}
}
}

@ -0,0 +1,36 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler;
/**
* Configuration for the {@link GroovyCompiler}.
*
* @author Phillip Webb
*/
public interface GroovyCompilerConfiguration {
/**
* Returns if import declarations should be guessed.
*/
boolean isGuessImports();
/**
* Returns if jar dependencies should be guessed.
*/
boolean isGuessDependencies();
}

@ -0,0 +1,64 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.bootstrap.cli.compiler.AstUtils;
import org.springframework.bootstrap.cli.compiler.CompilerAutoConfiguration;
import org.springframework.bootstrap.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Batch.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class SpringBatchCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasLeastOneAnnotation(classNode, "EnableBatchProcessing");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifMissingClasses("org.springframework.batch.core.Job").add(
"org.springframework.batch", "spring-batch-core", "2.1.9.RELEASE");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports(
"org.springframework.batch.repeat.RepeatStatus",
"org.springframework.batch.core.scope.context.ChunkContext",
"org.springframework.batch.core.step.tasklet.Tasklet",
"org.springframework.batch.core.configuration.annotation.StepScope",
"org.springframework.batch.core.configuration.annotation.JobBuilderFactory",
"org.springframework.batch.core.configuration.annotation.StepBuilderFactory",
"org.springframework.batch.core.configuration.annotation.EnableBatchProcessing",
"org.springframework.batch.core.Step",
"org.springframework.batch.core.StepExecution",
"org.springframework.batch.core.StepContribution",
"org.springframework.batch.core.Job",
"org.springframework.batch.core.JobExecution",
"org.springframework.batch.core.JobParameter",
"org.springframework.batch.core.JobParameters",
"org.springframework.batch.core.launch.JobLauncher",
"org.springframework.batch.core.converter.DefaultJobParametersConverter");
}
}

@ -0,0 +1,97 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler.autoconfigure;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.bootstrap.cli.compiler.CompilerAutoConfiguration;
import org.springframework.bootstrap.cli.compiler.DependencyCustomizer;
import org.springframework.bootstrap.cli.compiler.GroovyCompilerConfiguration;
/**
* {@link CompilerAutoConfiguration} for Spring Bootstrap.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class SpringBootstrapCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifMissingClasses("org.springframework.bootstrap.SpringApplication")
.add("org.springframework.bootstrap", "spring-bootstrap-application",
"0.0.1-SNAPSHOT");
// FIXME get the version
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("javax.sql.DataSource",
"org.springframework.beans.factory.annotation.Autowired",
"org.springframework.beans.factory.annotation.Value",
"org.springframework.context.annotation.Import",
"org.springframework.context.annotation.ImportResource",
"org.springframework.context.annotation.Profile",
"org.springframework.context.annotation.Scope",
"org.springframework.context.annotation.Configuration",
"org.springframework.context.annotation.Bean",
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
imports.addStarImports("org.springframework.stereotype");
}
@Override
public void applyToMainClass(GroovyClassLoader loader,
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
if (true) {
addEnableAutoConfigurationAnnotation(source, classNode);
}
}
private void addEnableAutoConfigurationAnnotation(SourceUnit source,
ClassNode classNode) {
if (!hasEnableAutoConfigureAnnotation(classNode)) {
try {
Class<?> annotationClass = source
.getClassLoader()
.loadClass(
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
AnnotationNode annotationNode = new AnnotationNode(new ClassNode(
annotationClass));
classNode.addAnnotation(annotationNode);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
}
private boolean hasEnableAutoConfigureAnnotation(ClassNode classNode) {
for (AnnotationNode node : classNode.getAnnotations()) {
if ("EnableAutoConfiguration".equals(node.getClassNode()
.getNameWithoutPackage())) {
return true;
}
}
return false;
}
}

@ -0,0 +1,60 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.bootstrap.cli.compiler.AstUtils;
import org.springframework.bootstrap.cli.compiler.CompilerAutoConfiguration;
import org.springframework.bootstrap.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring MVC.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class SpringMvcCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifMissingClasses("org.springframework.web.servlet.mvc.Controller")
.add("org.springframework", "spring-webmvc", "4.0.0.BOOTSTRAP-SNAPSHOT");
dependencies.ifMissingClasses("org.apache.catalina.startup.Tomcat",
"org.eclipse.jetty.server.Server").add("org.eclipse.jetty",
"jetty-webapp", "8.1.10.v20130312");
// FIXME restore Tomcat when we can get reload to work
// dependencies.ifMissingClasses("org.apache.catalina.startup.Tomcat",
// "org.eclipse.jetty.server.Server")
// .add("org.apache.tomcat.embed", "tomcat-embed-core", "7.0.37")
// .add("org.apache.tomcat.embed", "tomcat-embed-logging-juli", "7.0.37");
}
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasLeastOneAnnotation(classNode, "Controller", "EnableWebMvc");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("org.springframework.web.bind.annotation",
"org.springframework.web.servlet.config.annotation");
}
}

@ -0,0 +1,191 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.runner;
import java.io.File;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.springframework.bootstrap.cli.compiler.GroovyCompiler;
/**
* Compiles Groovy code running the resulting classes using a {@code SpringApplication}.
* Takes care of threading and class-loading issues and can optionally monitor files for
* changes.
*
* @author Phillip Webb
*/
public class BootstrapRunner {
// FIXME logging
private BootstrapRunnerConfiguration configuration;
private final File file;
private final String[] args;
private final GroovyCompiler compiler;
private RunThread runThread;
private FileWatchThread fileWatchThread;
/**
* Create a new {@link BootstrapRunner} instance.
* @param configuration the configuration
* @param file the file to compile/watch
* @param args input arguments
*/
public BootstrapRunner(final BootstrapRunnerConfiguration configuration, File file,
String... args) {
this.configuration = configuration;
this.file = file;
this.args = args;
this.compiler = new GroovyCompiler(configuration);
if (configuration.getLogLevel().intValue() <= Level.FINE.intValue()) {
System.setProperty("groovy.grape.report.downloads", "true");
}
}
/**
* Compile and run the application. This method is synchronized as it can be called by
* file monitoring threads.
* @throws Exception
*/
public synchronized void compileAndRun() throws Exception {
try {
// Shutdown gracefully any running container
if (this.runThread != null) {
this.runThread.shutdown();
this.runThread = null;
}
// Compile
Class<?>[] classes = this.compiler.compile(this.file);
if (classes.length == 0) {
throw new RuntimeException("No classes found in '" + this.file + "'");
}
// Run in new thread to ensure that the context classloader is setup
this.runThread = new RunThread(classes);
this.runThread.start();
this.runThread.join();
// Start monitoring for changes
if (this.fileWatchThread == null
&& this.configuration.isWatchForFileChanges()) {
this.fileWatchThread = new FileWatchThread();
this.fileWatchThread.start();
}
} catch (Exception ex) {
if (this.fileWatchThread == null) {
throw ex;
} else {
ex.printStackTrace();
}
}
}
/**
* Thread used to launch the Spring Application with the correct context classloader.
*/
private class RunThread extends Thread {
private final Class<?>[] classes;
private Object applicationContext;
/**
* Create a new {@link RunThread} instance.
* @param classes the classes to launch
*/
public RunThread(Class<?>... classes) {
this.classes = classes;
if (classes.length != 0) {
setContextClassLoader(classes[0].getClassLoader());
}
}
@Override
public void run() {
try {
// User reflection to load and call Spring
Class<?> application = getContextClassLoader().loadClass(
"org.springframework.bootstrap.SpringApplication");
Method method = application.getMethod("run", Object[].class,
String[].class);
this.applicationContext = method.invoke(null, this.classes,
BootstrapRunner.this.args);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Shutdown the thread, closing any previously opened appplication context.
*/
public synchronized void shutdown() {
if (this.applicationContext != null) {
try {
Method method = this.applicationContext.getClass().getMethod("close");
method.invoke(this.applicationContext);
} catch (NoSuchMethodException ex) {
// Not an application context that we can close
} catch (Exception ex) {
ex.printStackTrace();
} finally {
this.applicationContext = null;
}
}
}
}
/**
* Thread to watch for file changes and trigger recompile/reload.
*/
private class FileWatchThread extends Thread {
private long previous;
public FileWatchThread() {
this.previous = BootstrapRunner.this.file.lastModified();
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
long current = BootstrapRunner.this.file.lastModified();
if (this.previous < current) {
this.previous = current;
compileAndRun();
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
} catch (Exception ex) {
// Swallow, will be reported by compileAndRun
}
}
}
}
}

@ -0,0 +1,41 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.cli.runner;
import java.util.logging.Level;
import org.springframework.bootstrap.cli.compiler.GroovyCompilerConfiguration;
/**
* Configuration for the {@link BootstrapRunner}.
*
* @author Phillip Webb
*/
public interface BootstrapRunnerConfiguration extends GroovyCompilerConfiguration {
/**
* Returns {@code true} if the source file should be monitored for changes and
* automatically recompiled.
*/
boolean isWatchForFileChanges();
/**
* Returns the logging level to use.
*/
Level getLogLevel();
}

@ -0,0 +1,186 @@
package org.springframework.bootstrap.cli;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link SpringBootstrapCli}.
*
* @author Phillip Webb
*/
public class SpringBootstrapCliTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
private SpringBootstrapCli cli;
@Mock
private Command regularCommand;
@Mock
private Command optionCommand;
private Set<Call> calls = EnumSet.noneOf(Call.class);
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.cli = new SpringBootstrapCli() {
@Override
protected void showUsage() {
SpringBootstrapCliTests.this.calls.add(Call.SHOW_USAGE);
super.showUsage();
};
@Override
protected void errorMessage(String message) {
SpringBootstrapCliTests.this.calls.add(Call.ERROR_MESSAGE);
super.errorMessage(message);
}
@Override
protected void printStackTrace(Exception ex) {
SpringBootstrapCliTests.this.calls.add(Call.PRINT_STACK_TRACE);
super.printStackTrace(ex);
}
};
given(this.regularCommand.getName()).willReturn("command");
given(this.regularCommand.getDescription()).willReturn("A regular command");
given(this.optionCommand.getName()).willReturn("option");
given(this.optionCommand.getDescription()).willReturn("An optional command");
given(this.optionCommand.isOptionCommand()).willReturn(true);
this.cli.setCommands(Arrays.asList(this.regularCommand, this.optionCommand));
}
@Test
public void runWithoutArguments() throws Exception {
this.thrown.expect(NoArgumentsException.class);
this.cli.run();
}
@Test
public void runCommand() throws Exception {
this.cli.run("command", "--arg1", "arg2");
verify(this.regularCommand).run("--arg1", "arg2");
}
@Test
public void runOptionCommand() throws Exception {
this.cli.run("--option", "--arg1", "arg2");
verify(this.optionCommand).run("--arg1", "arg2");
}
@Test
public void runOptionCommandWithoutOption() throws Exception {
this.cli.run("option", "--arg1", "arg2");
verify(this.optionCommand).run("--arg1", "arg2");
}
@Test
public void runOptionOnNonOptionCommand() throws Exception {
this.thrown.expect(NoSuchOptionException.class);
this.cli.run("--command", "--arg1", "arg2");
}
@Test
public void missingCommand() throws Exception {
this.thrown.expect(NoSuchCommandException.class);
this.cli.run("missing");
}
@Test
public void handlesSuccess() throws Exception {
int status = this.cli.runAndHandleErrors("--option");
assertThat(status, equalTo(0));
assertThat(this.calls, equalTo((Set<Call>) EnumSet.noneOf(Call.class)));
}
@Test
public void handlesNoArgumentsException() throws Exception {
int status = this.cli.runAndHandleErrors();
assertThat(status, equalTo(1));
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.SHOW_USAGE)));
}
@Test
public void handlesNoSuchOptionException() throws Exception {
int status = this.cli.runAndHandleErrors("--missing");
assertThat(status, equalTo(1));
assertThat(this.calls,
equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE, Call.SHOW_USAGE)));
}
@Test
public void handlesRegularException() throws Exception {
willThrow(new RuntimeException()).given(this.regularCommand).run();
int status = this.cli.runAndHandleErrors("command");
assertThat(status, equalTo(1));
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE)));
}
@Test
public void handlesExceptionWithDashD() throws Exception {
willThrow(new RuntimeException()).given(this.regularCommand).run();
int status = this.cli.runAndHandleErrors("command", "-d");
assertThat(status, equalTo(1));
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE,
Call.PRINT_STACK_TRACE)));
}
@Test
public void handlesExceptionWithDashDashDebug() throws Exception {
willThrow(new RuntimeException()).given(this.regularCommand).run();
int status = this.cli.runAndHandleErrors("command", "--debug");
assertThat(status, equalTo(1));
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE,
Call.PRINT_STACK_TRACE)));
}
@Test
public void exceptionMessages() throws Exception {
assertThat(new NoSuchOptionException("name").getMessage(),
equalTo("Unknown option: --name"));
assertThat(new NoSuchCommandException("name").getMessage(),
equalTo("spr: 'name' is not a valid command. See 'spr --help'."));
}
@Test
public void help() throws Exception {
this.cli.run("help", "command");
verify(this.regularCommand).printHelp((PrintStream) anyObject());
}
@Test
public void helpNoCommand() throws Exception {
this.thrown.expect(NoHelpCommandArgumentsException.class);
this.cli.run("help");
}
@Test
public void helpUnknownCommand() throws Exception {
this.thrown.expect(NoSuchCommandException.class);
this.cli.run("help", "missing");
}
private static enum Call {
SHOW_USAGE, ERROR_MESSAGE, PRINT_STACK_TRACE
}
}

@ -0,0 +1,27 @@
# Spring Bootstrap Groovy
Spring Bootstrap Groovy gives you the quickest possible getting
started experience with Spring apps, whether you are writing a web
app, a batch job or a standalone java app.
## Building and Testing
To avoid problems with classpaths and existing JVM-based build tools,
Spring Bootstrap Groovy uses an exec plugin call to launch `groovyc`.
You need to have a `sh` on your path along with `groovyc` (2.1.x),
`find` and `xargs`. These tools are standard on a Mac or Linux
distribution, and available using Cygwin on Windows. Once it is
built, the zip file is portable.
Here are the steps to build and test:
$ mvn install
The `spring` executable is then available at
`spring-bootstrap-groovy/target/spring-<VERSION>`. There is also a jar
file with the Groovy Bootstrap components. The `spring` executable
includes jars from `SPRING_HOME` in the classpath so you can run it
while you are developing like this
$ export SPRING_HOME=<spring-bootstrap-groovy>/target
$ <spring-bootstrap-groovy>/src/main/scripts/spring App.groovy

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-groovy</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${dependency.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ivy</groupId>
<artifactId>ivy</artifactId>
<version>2.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>grapes</id>
<build>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<configuration>
<filesets>
<fileset>
<directory>${user.home}/.groovy/grapes</directory>
<includes>
<include>org.springframework*/**</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/groovy</directory>
<filtering>true</filtering>
<targetPath>../generated-sources/groovy</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<!-- Using exec plugin to compile groovy because it needs to be forked
cleanly. Only works if sh, find, xargs and groovyc are on PATH -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<id>compile-plugin</id>
<phase>compile</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>sh</executable>
<arguments>
<argument>-c</argument>
<argument>find target/generated-sources/groovy -name *.groovy |
xargs groovyc -d target/classes</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<descriptors>
<descriptor>src/main/assembly/descriptor.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-distribution</id>
<phase>install</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerId>groovy-eclipse-compiler</compilerId>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.7.0-01</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<versionRange>[3.0,)</versionRange>
<goals>
<goal>testCompile</goal>
<goal>compile</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<delimiters>
<delimiter>@@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

@ -0,0 +1,30 @@
<assembly>
<id>dist</id>
<formats>
<format>zip</format>
<format>dir</format>
</formats>
<baseDirectory>spring-${project.version}</baseDirectory>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/scripts</directory>
<outputDirectory>bin</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>bin</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<filtered>true</filtered>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<includes>
<include>org.springframework.bootstrap:spring-bootstrap-groovy:jar:*</include>
</includes>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>

@ -0,0 +1,52 @@
package org.springframework.bootstrap.grapes
@GrabResolver(name='spring-milestone', root='http://repo.springframework.org/milestone')
@GrabResolver(name='spring-snapshot', root='http://repo.springframework.org/snapshot')
@GrabConfig(systemClassLoader=true)
@Grab("org.springframework.bootstrap:spring-bootstrap:@@version@@")
@Grab("org.springframework.batch:spring-batch-core:2.2.0.M1")
@Grab("org.springframework:spring-context:@@dependency.springframework.version@@")
class BatchGrapes {
}
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean
import org.springframework.bootstrap.CommandLineRunner
import org.springframework.batch.core.Job
import org.springframework.batch.core.converter.DefaultJobParametersConverter
import org.springframework.batch.core.converter.JobParametersConverter
import org.springframework.batch.core.launch.JobLauncher
import org.springframework.context.annotation.Configuration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.util.StringUtils
import groovy.util.logging.Log
@Configuration
@ConditionalOnMissingBean(CommandLineRunner)
@Log
class BatchCommand {
@Autowired(required=false)
private JobParametersConverter converter = new DefaultJobParametersConverter()
@Autowired
private JobLauncher jobLauncher
@Autowired
private Job job
@Bean
CommandLineRunner batchCommandLineRunner() {
return new CommandLineRunner() {
void run(String... args) {
log.info("Running default command line with: ${args}")
launchJobFromProperties(StringUtils.splitArrayElementsIntoProperties(args, "="))
}
}
}
protected void launchJobFromProperties(Properties properties) {
jobLauncher.run(job, converter.getJobParameters(properties))
}
}

@ -0,0 +1,40 @@
package org.springframework.bootstrap.grapes
@GrabResolver(name='spring-milestone', root='http://repo.springframework.org/milestone')
@GrabConfig(systemClassLoader=true)
@Grab("org.springframework:spring-jdbc:4.0.0.BOOTSTRAP-SNAPSHOT")
@Grab("org.springframework.batch:spring-batch-core:2.2.0.M1")
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
import javax.annotation.PostConstruct
import javax.sql.DataSource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.core.io.ResourceLoader
@Configuration // TODO: make this conditional
class BatchInitializationGrapes {
@Autowired
private DataSource dataSource
@Autowired
private Environment environment
@Autowired
private ResourceLoader resourceLoader
@PostConstruct
protected void initialize() {
String platform = org.springframework.batch.support.DatabaseType.fromMetaData(dataSource).toString().toLowerCase()
if (platform=="hsql") {
platform = "hsqldb"
}
ResourceDatabasePopulator populator = new ResourceDatabasePopulator()
populator.addScript(resourceLoader.getResource("org/springframework/batch/core/schema-${platform}.sql"))
populator.setContinueOnError(true)
DatabasePopulatorUtils.execute(populator, dataSource)
}
}

@ -0,0 +1,20 @@
package org.springframework.bootstrap.grapes
// Spring stuff needs to be on the system classloader apparently (when using @Configuration)
@GrabResolver(name='spring-snapshot', root='http://repo.springframework.org/snapshot')
@GrabConfig(systemClassLoader=true)
@Grab("org.springframework:spring-context:@@dependency.springframework.version@@")
@Grab("org.springframework.bootstrap:spring-bootstrap:@@version@@")
@GrabExclude("commons-logging:commons-logging")
@Grab("org.slf4j:jcl-over-slf4j:1.6.1")
@Grab("org.slf4j:slf4j-jdk14:1.6.1")
class BootstrapGrapes {
}
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration
import org.springframework.context.annotation.Configuration
@Configuration
// @EnableAutoConfiguration
class BootstrapAutoConfiguration {
}

@ -0,0 +1,92 @@
package org.springframework.bootstrap.grapes
import org.springframework.core.type.StandardAnnotationMetadata
import org.springframework.util.ClassUtils
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory
import groovy.util.logging.Log
@GrabResolver(name='spring-snapshot', root='http://repo.springframework.org/snapshot')
@GrabConfig(systemClassLoader=true)
@Grab("org.springframework:spring-core:4.0.0.BOOTSTRAP-SNAPSHOT")
@GrabExclude("commons-logging:commons-logging")
@Grab("org.slf4j:jcl-over-slf4j:1.6.1")
@Grab("org.slf4j:slf4j-jdk14:1.6.1")
@Log
class Dependencies {
static List<String> defaults() {
return ["org.springframework.bootstrap.grapes.BootstrapGrapes"]
}
static List<String> dependencies(Collection<String> configs) {
def result = []
if (isWeb(configs)) {
log.info("Adding web dependencies.")
result.addAll(web())
}
if (isBatch(configs)) {
log.info("Adding batch dependencies.")
result.addAll(batch())
result << "org.springframework.bootstrap.grapes.BatchCommand"
result << "org.springframework.bootstrap.grapes.BatchInitializationGrapes"
}
if (isHadoop(configs)) {
log.info("Adding info dependencies.")
result.addAll(hadoop())
result << "org.springframework.bootstrap.grapes.HadoopContext"
}
return result
}
static String[] web() {
def result = []
result << "org.springframework.bootstrap.grapes.WebGrapes"
if (!isEmbeddedServerAvailable()) { result << "org.springframework.bootstrap.grapes.TomcatGrapes" }
return result
}
static String[] batch() {
def result = []
result << "org.springframework.bootstrap.grapes.BatchGrapes"
return result
}
static String[] hadoop() {
def result = []
result << "org.springframework.bootstrap.grapes.HadoopGrapes"
return result
}
static boolean isWeb(Collection<String> configs) {
SimpleMetadataReaderFactory factory = new SimpleMetadataReaderFactory()
return configs.any { config ->
def meta = factory.getMetadataReader(config).getAnnotationMetadata()
meta.hasAnnotation("org.springframework.stereotype.Controller") || meta.hasAnnotation("org.springframework.web.servlet.config.annotation.EnableWebMvc")
}
}
static boolean isHadoop(Collection<String> configs) {
SimpleMetadataReaderFactory factory = new SimpleMetadataReaderFactory()
return configs.any { config ->
config.contains("Hadoop")
}
}
static boolean isBatch(Collection<String> configs) {
SimpleMetadataReaderFactory factory = new SimpleMetadataReaderFactory()
return configs.any { config ->
def meta = factory.getMetadataReader(config).getAnnotationMetadata()
meta.hasAnnotation("org.springframework.batch.core.configuration.annotation.EnableBatchProcessing")
}
}
static boolean isEmbeddedServerAvailable() {
return ClassUtils.isPresent("org.apache.catalina.startup.Tomcat") || ClassUtils.isPresent("org.mortbay.jetty.Server")
}
}

@ -0,0 +1,26 @@
package org.springframework.bootstrap.grapes
@GrabConfig(systemClassLoader=true)
@Grab("org.springframework.data:spring-data-hadoop:1.0.0.RELEASE")
@Grab("org.springframework.bootstrap:spring-bootstrap:@@version@@")
@Grab("org.springframework:spring-context:4.0.0.BOOTSTRAP-SNAPSHOT")
@Grab("org.apache.hadoop:hadoop-examples:1.0.4")
@GrabExclude("org.mortbay.jetty:sevlet-api-2.5")
@GrabExclude("org.mortbay.jetty:jetty")
@GrabExclude("org.mortbay.jetty:jetty-util")
@GrabExclude("org.mortbay.jetty:jsp-2.1")
@GrabExclude("org.mortbay.jetty:jsp-api-2.1")
@GrabExclude("tomcat:jasper-runtime")
@GrabExclude("tomcat:jasper-compiler")
class HadoopGrapes {
}
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.ImportResource
@Configuration
@ConditionalOnMissingBean(org.apache.hadoop.conf.Configuration)
@ImportResource("hadoop-context.xml")
class HadoopContext {
}

@ -0,0 +1,9 @@
package org.springframework.bootstrap.grapes
@GrabConfig(systemClassLoader=true)
// Grab some Tomcat dependencies
@Grab("org.apache.tomcat.embed:tomcat-embed-core:7.0.32")
// JULI logging has sensible defaults in JAVA_HOME, so no need for user to create it
@Grab("org.apache.tomcat.embed:tomcat-embed-logging-juli:7.0.32")
class TomcatGrapes {
}

@ -0,0 +1,5 @@
package org.springframework.bootstrap.grapes
@Grab("org.springframework:spring-webmvc:4.0.0.BOOTSTRAP-SNAPSHOT")
class WebGrapes {
}

@ -0,0 +1,20 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:hdp="http://www.springframework.org/schema/hadoop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/hadoop http://www.springframework.org/schema/hadoop/spring-hadoop.xsd">
<hdp:resource-loader id="hadoopResourceLoader" />
<hdp:configuration>
fs.default.name=${hd.fs:hdfs://localhost:9000}
</hdp:configuration>
<bean id="defaultResourceLoaders" class="org.springframework.data.hadoop.fs.CustomResourceLoaderRegistrar" p:loader-ref="hadoopResourceLoader" />
</beans>

@ -0,0 +1,100 @@
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.ast.ClassHelper
import groovy.util.logging.Log
def bootstrap = 'org.springframework.bootstrap.grapes.BootstrapGrapes' as Class
void addImport(module, path) {
def name = path.lastIndexOf('.').with {it != -1 ? path[it+1..<path.length()] : path}
if (name=="*") {
// Doesn't work?
name = path.lastIndexOf('.').with {path[0..<it] }
module.addStarImport(name, [])
} else {
module.addImport(name, ClassHelper.make(path), [])
}
}
withConfig(configuration) {
ast(Log)
imports {
normal 'javax.sql.DataSource'
normal 'org.springframework.stereotype.Component'
normal 'org.springframework.stereotype.Controller'
normal 'org.springframework.stereotype.Repository'
normal 'org.springframework.stereotype.Service'
normal 'org.springframework.beans.factory.annotation.Autowired'
normal 'org.springframework.beans.factory.annotation.Value'
normal 'org.springframework.context.annotation.Import'
normal 'org.springframework.context.annotation.ImportResource'
normal 'org.springframework.context.annotation.Profile'
normal 'org.springframework.context.annotation.Scope'
normal 'org.springframework.context.annotation.Configuration'
normal 'org.springframework.context.annotation.Bean'
normal 'org.springframework.bootstrap.CommandLineRunner'
}
def dependencySource = "org.springframework.bootstrap.grapes.Dependencies" as Class // TODO: maybe strategise this
inline(phase:'CONVERSION') { source, context, classNode ->
def module = source.getAST()
if (classNode.name.contains("Hadoop")) {
def hadoop = dependencySource.hadoop() as Class[]
['org.springframework.data.hadoop.mapreduce.JobRunner',
'org.springframework.data.hadoop.mapreduce.JobFactoryBean'
].each { path -> addImport(module, path) }
module.addImport("HadoopConfiguration", ClassHelper.make("org.apache.hadoop.conf.Configuration"), [])
}
classNode.annotations.each {
def name = it.classNode.name
if (name=='Controller' || name=='EnableWebMvc') {
def web = dependencySource.web() as Class[]
['org.springframework.web.bind.annotation.RequestBody',
'org.springframework.web.bind.annotation.RequestParam',
'org.springframework.web.bind.annotation.PathVariable',
'org.springframework.web.bind.annotation.RequestHeader',
'org.springframework.web.bind.annotation.RequestMethod',
'org.springframework.web.bind.annotation.RequestBody',
'org.springframework.web.bind.annotation.ResponseBody',
'org.springframework.web.bind.annotation.ResponseStatus',
'org.springframework.web.bind.annotation.RequestMapping',
'org.springframework.web.bind.annotation.ExceptionHandler',
'org.springframework.web.bind.annotation.ModelAttribute',
'org.springframework.web.bind.annotation.CookieValue',
'org.springframework.web.servlet.config.annotation.EnableWebMvc',
'org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry',
'org.springframework.web.servlet.config.annotation.ViewControllerRegistry',
'org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter'].each { path -> addImport(module, path) }
}
if (name=='EnableBatchProcessing') {
def batch = dependencySource.batch() as Class[]
['org.springframework.batch.repeat.RepeatStatus',
'org.springframework.batch.core.scope.context.ChunkContext',
'org.springframework.batch.core.step.tasklet.Tasklet',
'org.springframework.batch.core.configuration.annotation.StepScope',
'org.springframework.batch.core.configuration.annotation.JobBuilderFactory',
'org.springframework.batch.core.configuration.annotation.StepBuilderFactory',
'org.springframework.batch.core.configuration.annotation.EnableBatchProcessing',
'org.springframework.batch.core.Step',
'org.springframework.batch.core.StepExecution',
'org.springframework.batch.core.StepContribution',
'org.springframework.batch.core.Job',
'org.springframework.batch.core.JobExecution',
'org.springframework.batch.core.JobParameter',
'org.springframework.batch.core.JobParameters',
'org.springframework.batch.core.launch.JobLauncher',
'org.springframework.batch.core.converter.DefaultJobParametersConverter'].each { path -> addImport(module, path) }
}
}
}
}

@ -0,0 +1,33 @@
##############################################################################
## ##
## Groovy Classloading Configuration ##
## ##
##############################################################################
##
## $Revision$ $Date$
##
## Note: do not add classes from java.lang here. No rt.jar and on some
## platforms no tools.jar
##
## See http://groovy.codehaus.org/api/org/codehaus/groovy/tools/LoaderConfiguration.html
## for the file format
# load required libraries
load !{groovy.home}/lib/a*.jar
load !{groovy.home}/lib/b*.jar
load !{groovy.home}/lib/c*.jar
load !{groovy.home}/lib/g*.jar
load !{groovy.home}/lib/h*.jar
load !{groovy.home}/lib/i*.jar
load !{groovy.home}/lib/j*.jar
load !{groovy.home}/lib/q*.jar
load !{groovy.home}/lib/t*.jar
load !{groovy.home}/lib/x*.jar
# load user specific libraries
load !{user.home}/.groovy/lib/*.jar
# tools.jar for ant tasks
load ${tools.jar}

@ -0,0 +1,163 @@
#!/bin/bash
# OS specific support (must be 'true' or 'false').
cygwin=false;
darwin=false;
case "`uname`" in
CYGWIN*)
cygwin=true
;;
Darwin*)
darwin=true
;;
esac
if [ "$SPRING_HOME" == "" ]; then
SPRING_HOME=`cd "$(dirname $0)"/.. && pwd`
fi
SPRING_BIN=$(dirname $0)
export GROOVY_CONF="${SPRING_BIN}"/groovy.conf
SPRING_HANDLER=auto
TARGETDIR=target/classes
if [ -f build.gradle ]; then
TARGETDIR=build/classes/main
fi
mkdir -p "${TARGETDIR%/}"
function find_classfile {
classname="$( echo ${1%%.groovy} | sed -e 's,.*/,,')"
package="$( grep ^package ${1} | sed -e 's,package\s,,g' -e 's,;,,g' -e 's,\.,/,g')"
if [ "${package}" != "" ]; then package="${package}/"; fi
for f in $( find "${TARGETDIR}" -name "${classname}.class" ); do
if [ "${f}" == "${TARGETDIR}/${package}${classname}.class" ]; then
echo $f; return 0
fi
done
}
function is_compile_needed {
config=$1
STATOPTS="-c %X"
if $darwin; then STATOPTS="-f %Dm"; fi
# Compile .groovy files if necessary
if [ ! -f ${config} ]; then
echo "File ${config} does not exist. Did you point at the wrong file?"
exit 3
else
classfile=$( find_classfile ${config} )
if [ ! -f "${classfile}" -o $(stat "${STATOPTS}" ${config}) -gt $(stat "${STATOPTS}" ${classfile} 2>/dev/null || echo 0) ]; then
return 0
fi
fi
return 1
}
function is_option {
echo "$1" | grep -q "^--.*"
}
function is_groovy {
[ "${1%%.groovy}" != "${1}" ]
}
function convert_config_to_class {
classfile=$( find_classfile ${config} )
if [ -z "${classfile}" ]; then
echo "No class found for ${config}. Compiler failed or class not defined?"
exit 1
fi
config="${classfile#${TARGETDIR}/}"
while [ "${config%/*}" != "${config}" ]; do
config="${config%/*}"."${config##*/}"
done
}
config=$1; shift
configs=()
compilables=()
while [ "$config" != "" ]; do
if is_groovy "$config"; then
if is_compile_needed "${config}"; then
compilables[${#compilables[@]}]="${config}"
fi
configs[${#configs[@]}]="${config}"
elif is_option "${config}"; then
case "${config%=*}" in
"--handler") SPRING_HANDLER="${config#*=}";;
esac
else
args[${#args[@]}]="${config}"
fi
config=$1; shift
done
CLASSPATH="${SPRING_BIN}":"${TARGETDIR}"
for f in "${SPRING_HOME}"/lib/*.jar; do
CLASSPATH="${CLASSPATH}":$f
done
for f in "${SPRING_HOME}"/*.jar; do
CLASSPATH="${CLASSPATH}":$f
done
if $cygwin; then
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
fi
if [ "${#compilables[@]}" -gt 0 ]; then
groovyc -cp "${CLASSPATH}" --configscript "$SPRING_BIN"/customizer.groovy -d "${TARGETDIR}" "${compilables[@]}"
fi
config_classes=("org.springframework.bootstrap.grapes.BootstrapAutoConfiguration.class")
for config in "${configs[@]}"; do
convert_config_to_class
config_classes[${#config_classes[@]}]="${config}"
done
if [ "${#config_classes[@]}" == "0" ]; then
echo "No files to run."
exit 2
fi
exec groovy -cp "${CLASSPATH}" -Dspring.home="${SPRING_HOME}" --configscript "$SPRING_BIN"/customizer.groovy "$SPRING_BIN"/spring-"${SPRING_HANDLER}".groovy "${config_classes[@]}" "${args[@]}"

@ -0,0 +1,24 @@
// Get the args and turn them into classes
def configs = []
def parameters = []
args.each { arg ->
if (arg.endsWith(".class")) {
configs << arg.replaceAll(".class", "")
} else {
parameters << arg
}
}
// Dynamically grab some dependencies
def dependencySource = "org.springframework.bootstrap.grapes.Dependencies" as Class // TODO: maybe strategise this
def dependencies = [*dependencySource.defaults(), *dependencySource.dependencies(configs)]
configs = dependencies + configs
// Do this before any Spring auto stuff is used in case it enhances the classpath
configs = configs as Class[]
parameters = parameters as String[]
// Now run the application
def applicationClass = "org.springframework.bootstrap.SpringApplication" as Class
applicationClass.run(configs, parameters)

@ -0,0 +1,27 @@
// Spring stuff needs to be on the system classloader apparently (when using @Configuration)
@GrabResolver(name='spring-milestone', root='http://maven.springframework.org/milestone')
@GrabConfig(systemClassLoader=true)
@Grab("org.springframework:spring-context:4.0.0.BOOTSTRAP-SNAPSHOT")
@GrabExclude("commons-logging:commons-logging")
@Grab("org.slf4j:jcl-over-slf4j:1.6.1")
@Grab("org.slf4j:slf4j-jdk14:1.6.1")
import org.springframework.context.annotation.AnnotationConfigApplicationContext
// Now create a Spring context
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()
// Get the args and turn them into classes
def configs = []
def parameters = []
boolean endconfigs = false
args.each { arg ->
if (arg.endsWith(".class")) {
configs += arg.replaceAll(".class", "")
} else {
parameters += arg
}
}
configs = configs as Class[]
parameters = parameters as String[]
// Register the config classes, can be @Configuration or @Component etc.
ctx.register(configs)
ctx.refresh()

@ -0,0 +1,8 @@
@Controller
class App {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!"
}
}

@ -0,0 +1,4 @@
@Configuration
class Empty {
}

@ -0,0 +1,30 @@
@Grab("org.hsqldb:hsqldb-j5:2.0.0")
@EnableBatchProcessing
class JobConfig {
@Autowired
private JobBuilderFactory jobs
@Autowired
private StepBuilderFactory steps
@Bean
protected Tasklet tasklet() {
return new Tasklet() {
RepeatStatus execute(StepContribution contribution, ChunkContext context) {
return RepeatStatus.FINISHED
}
}
}
@Bean
Job job() throws Exception {
return jobs.get("job").start(step1()).build()
}
@Bean
protected Step step1() throws Exception {
return steps.get("step1").tasklet(tasklet()).build()
}
}

@ -0,0 +1,18 @@
import java.io.File;
import org.springframework.bootstrap.CommandLineRunner;
@Component
class Signal implements CommandLineRunner {
private File messages = new File("target/messages")
boolean ready = false
@Override
void run(String... args) {
messages.mkdirs()
new File(messages, "ready").write("Ready!")
ready = true
}
}

@ -0,0 +1,174 @@
/*
* Cloud Foundry 2012.02.03 Beta
* Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*/
package org.springframework.bootstrap.groovy
/**
* <p>As part of the gvm test suite we need to launch a bash shell and execute
* multiple commands in it. This is tricky to do using Java's support for
* working with external processes as the API can't tell you when a command
* has finished executing.</p>
* <p>This class provides some hacks that allow you to serially execute commands
* in an external bash process in a fairly reliable manner and to retrieve the
* output of those commands.</p>
*/
class BashEnv {
static final PROMPT = ""
static final EXIT_CODE_CMD = 'echo "Exit code is: $?"'
static final EXIT_CODE_PATTERN = ~/Exit code is: (\d+)\s*${PROMPT}?$/
private final Object outputLock = new Object()
def exitCode
def process
def processOutput = new StringBuilder()
def commandOutput
// Command timeout in milliseconds
def timeout = 5000
def workDir
def env
BashEnv(workDir, Map env) {
this.workDir = workDir as File
env = env + [PS1: PROMPT]
this.env = env.collect { k, v -> k + '=' + v }
}
/**
* Starts the external bash process.
*/
void start() {
process = ["bash", "--noprofile", "--norc", "-i"].execute(env, workDir)
consumeProcessStream(process.inputStream)
consumeProcessStream(process.errorStream)
}
/**
* Stops the external bash process and waits for it to finish.
*/
void stop() {
execute("exit")
process.waitFor()
}
/**
* Sends a command line to the external bash process and returns once the
* command has finished executing. If the command is interactive and requires
* input during it's execution (for example a y/n answer to a question) you
* can provide that input as a list of strings.
*/
void execute(String cmdline, List inputs = []) {
resetOutput()
if (cmdline != "exit") {
exitCode = null
}
process.outputStream << cmdline << "\n"
process.outputStream.flush()
if (cmdline != "exit") {
for (input in inputs) {
process.outputStream << input << "\n"
}
process.outputStream << EXIT_CODE_CMD << "\n"
process.outputStream.flush()
}
def start = System.currentTimeMillis()
while (cmdline != "exit") {
Thread.sleep 100
synchronized (outputLock) {
// Remove all the extraneous text that's not related to the
// command's output. This includes the command string itself,
// the 'echo' command to display the command's exit code, and
// the exit code line.
removeFromOutput(cmdline + "\n")
removeFromOutput(PROMPT + EXIT_CODE_CMD + "\n")
def str = processOutput.toString()
def m = EXIT_CODE_PATTERN.matcher(str)
commandOutput = str
if (m) {
exitCode = m[0][1]
// Remove this exit code line from the output.
commandOutput = m.replaceAll('')
break
}
// If the command times out, we should break out of the loop and
// display whatever output has already been produced.
if (System.currentTimeMillis() - start > timeout) {
commandOutput = "ALERT! Command timed out. Last output was:\n\n${processOutput}"
break
}
}
}
}
/**
* Returns the exit code of the last command that was executed.
*/
int getStatus() {
if (!exitCode) throw new IllegalStateException("Did you run execute() before getting the status?")
return exitCode.toInteger()
}
/**
* Returns the text output (both stdout and stderr) of the last command
* that was executed.
*/
String getOutput() {
return commandOutput
}
/**
* Clears the saved command output.
*/
void resetOutput() {
synchronized (outputLock) {
processOutput = new StringBuilder()
}
}
private void consumeProcessStream(final InputStream stream) {
char[] buffer = new char[256]
Thread.start {
def reader = new InputStreamReader(stream)
def charsRead = 0
while (charsRead != -1) {
charsRead = reader.read(buffer, 0, 256)
if (charsRead > 0) {
synchronized (outputLock) {
processOutput.append(buffer, 0, charsRead)
}
}
}
}
}
private void removeFromOutput(String line) {
synchronized (outputLock) {
def pos = processOutput.indexOf(line)
if (pos != -1) {
processOutput.delete(pos, pos + line.size() - 1)
}
}
}
}

@ -0,0 +1,70 @@
/*
* Cloud Foundry 2012.02.03 Beta
* Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*/
package org.springframework.bootstrap.groovy;
import static org.junit.Assert.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.junit.After
import org.junit.Before
import org.junit.Test
/**
* @author Dave Syer
*
*/
class ScriptTests {
private BashEnv bash
private ExecutorService executor = Executors.newFixedThreadPool(2)
@Before
void init() {
bash = new BashEnv(".", [SPRING_HOME: "target"])
bash.start()
bash.execute("export GVM_DIR=~/.gvm")
bash.execute("source ~/.gvm/bin/gvm-init.sh")
assertEquals("You need to install gvm to run these tests", 0, bash.status)
bash.execute("gvm use groovy 2.1.0")
assertEquals("You need to do this before running the tests: > gvm install groovy 2.1.0", 0, bash.status)
}
@After
void clean() {
bash?.stop()
}
@Test
void testVanillaApplicationContext() {
execute(bash, "src/main/scripts/spring src/test/apps/Empty.groovy")
assertEquals(0, bash.status)
}
@Test
void testBatchApplicationContext() {
execute(bash, "src/main/scripts/spring src/test/apps/JobConfig.groovy foo=bar")
assertEquals(0, bash.status)
assertTrue(bash.output.contains("[SimpleJob: [name=job]] completed with the following parameters: [{foo=bar}]"))
}
private void execute(BashEnv bash, String cmdline) {
bash.execute(cmdline)
if (bash.exitCode && bash.status!=0) {
println "Unsuccessful execution (${cmdline}). Output: \n${bash.output}"
}
}
}

@ -0,0 +1,94 @@
/*
* Cloud Foundry 2012.02.03 Beta
* Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*/
package org.springframework.bootstrap.groovy;
import static org.junit.Assert.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.springframework.web.client.RestTemplate
/**
* @author Dave Syer
*
*/
class WebScriptTests {
private BashEnv bash
private ExecutorService executor = Executors.newFixedThreadPool(2)
private File messages = new File("target/messages")
@Before
void init() {
assertTrue("Couldn't delete messages directory", messages.deleteDir())
bash = new BashEnv(".", [SPRING_HOME: "target"])
bash.start()
bash.execute("export GVM_DIR=~/.gvm")
bash.execute("source ~/.gvm/bin/gvm-init.sh")
assertEquals("You need to install gvm to run these tests", 0, bash.status)
bash.execute("gvm use groovy 2.1.0")
assertEquals("You need to do this before running the tests: > gvm install groovy 2.1.0", 0, bash.status)
}
@After
void clean() {
killit()
bash?.stop()
}
@Test
@Ignore
void testWebApplicationContext() {
executor.submit {
execute(bash, "src/main/scripts/spring src/test/apps/App.groovy src/test/apps/Signal.groovy")
}
File ready = new File(messages, "ready")
long timeout = 10000
long t0 = System.currentTimeMillis()
while (!ready.exists() && System.currentTimeMillis() - t0 < timeout) {
println "Waiting for app to start"
Thread.sleep(1000)
}
if (ready.exists()) {
println ready.text
} else {
fail("Timed out waiting for app to start")
}
// assertEquals(0, bash.status)
def response = new RestTemplate().getForEntity("http://localhost:8080", String.class)
assertEquals("Hello World!", response.body)
}
private void killit() {
BashEnv reaper = new BashEnv(".", [SPRING_HOME: "target"])
reaper.start()
reaper?.execute("pkill -9 -f '\\--configscript src/main/scripts/customizer.groovy'")
reaper?.stop()
}
private void execute(BashEnv bash, String cmdline) {
bash.execute(cmdline)
if (bash.exitCode && bash.status!=0) {
println "Unsuccessful execution (${cmdline}). Output: \n${bash.output}"
}
}
}

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-launcher</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${project.basedir}/..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<type>maven-plugin</type>
</dependency>
</dependencies>
</project>

@ -0,0 +1,47 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher;
import java.util.List;
import java.util.jar.JarEntry;
import org.springframework.bootstrap.launcher.jar.RandomAccessJarFile;
/**
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
* included inside a {@code /lib} directory.
*
* @author Phillip Webb
*/
public class JarLauncher extends Launcher {
@Override
protected boolean isNestedJarFile(JarEntry jarEntry) {
return !jarEntry.isDirectory() && jarEntry.getName().startsWith("lib/");
}
@Override
protected void postProcessLib(RandomAccessJarFile jarFile,
List<RandomAccessJarFile> lib) throws Exception {
lib.add(0, jarFile);
}
public static void main(String[] args) {
new JarLauncher().launch(args);
}
}

@ -0,0 +1,198 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher;
import java.io.File;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import org.springframework.bootstrap.launcher.jar.RandomAccessJarFile;
/**
* Base class for launchers that can start an application with a fully configured
* classpath.
*
* @author Phillip Webb
*/
public abstract class Launcher {
/**
* The main runner class. This must be loaded by the created ClassLoader so cannot be
* directly referenced.
*/
private static final String RUNNER_CLASS = Launcher.class.getPackage().getName()
+ ".MainMethodRunner";
/**
* Launch the application. This method is the initial entry point that should be
* called by a subclass {@code public static void main(String[] args)} method.
* @param args the incoming arguments
*/
public void launch(String[] args) {
try {
launch(args, getClass().getProtectionDomain());
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Launch the application given the protection domain.
* @param args the incoming arguments
* @param protectionDomain the protection domain
* @throws Exception
*/
protected void launch(String[] args, ProtectionDomain protectionDomain)
throws Exception {
CodeSource codeSource = protectionDomain.getCodeSource();
URL codeSourceLocation = (codeSource == null ? null : codeSource.getLocation());
String codeSourcePath = (codeSourceLocation == null ? null : codeSourceLocation
.getPath());
if (codeSourcePath == null) {
throw new IllegalStateException("Unable to determine code source archive");
}
if (codeSourcePath.endsWith("/")) {
throw new IllegalStateException("The specified code source path '"
+ codeSourcePath + "' is not an archive");
}
launch(args, new File(codeSourcePath));
}
/**
* Launch the application given the archive file
* @param args the incoming arguments
* @param archive the underlying (zip/war/jar) archive
* @throws Exception
*/
protected void launch(String[] args, File archive) throws Exception {
RandomAccessJarFile jarFile = new RandomAccessJarFile(archive);
List<RandomAccessJarFile> lib = new ArrayList<RandomAccessJarFile>();
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
if (isNestedJarFile(jarEntry)) {
lib.add(jarFile.getNestedJarFile(jarEntry));
}
}
postProcessLib(jarFile, lib);
ClassLoader classLoader = createClassLoader(lib);
launch(args, jarFile, classLoader);
}
/**
* Determine if the specified {@link JarEntry} is a nested item that should be added
* to the classpath. The method is called once for each entry.
* @param jarEntry the jar entry
* @return {@code true} if the entry is a nested item (jar or folder)
*/
protected abstract boolean isNestedJarFile(JarEntry jarEntry);
/**
* Called to post-process lib entries before they are used. Implementations can add
* and remove entries.
* @param jarFile the jar file
* @param lib the existing lib
* @throws Exception
*/
protected void postProcessLib(RandomAccessJarFile jarFile,
List<RandomAccessJarFile> lib) throws Exception {
}
/**
* Create a classloader for the specified lib.
* @param lib the lib
* @return the classloader
* @throws Exception
*/
protected ClassLoader createClassLoader(List<RandomAccessJarFile> lib)
throws Exception {
URL[] urls = new URL[lib.size()];
for (int i = 0; i < urls.length; i++) {
urls[i] = lib.get(i).getUrl();
}
return createClassLoader(urls);
}
/**
* Create a classloader for the specified URLs
* @param urls the URLs
* @return the classloader
* @throws Exception
*/
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
return new URLClassLoader(urls, getClass().getClassLoader().getParent());
}
/**
* Launch the application given the archive file and a fully configured classloader.
* @param args the incoming arguments
* @param jarFile the jar file
* @param classLoader the classloader
* @throws Exception
*/
protected void launch(String[] args, RandomAccessJarFile jarFile,
ClassLoader classLoader) throws Exception {
String mainClass = getMainClass(jarFile);
Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
Thread runnerThread = new Thread(runner);
runnerThread.setContextClassLoader(classLoader);
runnerThread.setName(Thread.currentThread().getName());
runnerThread.start();
}
/**
* Obtain the main class that should be used to launch the application. By default
* this method uses a {@code Start-Class} manifest entry.
* @param jarFile the jar file
* @return the main class
* @throws Exception
*/
protected String getMainClass(RandomAccessJarFile jarFile) throws Exception {
String mainClass = jarFile.getManifest().getMainAttributes()
.getValue("Start-Class");
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified");
}
return mainClass;
}
/**
* Create the {@code MainMethodRunner} used to launch the application.
* @param mainClass the main class
* @param args the incoming arguments
* @param classLoader the classloader
* @return a runnable used to start the application
* @throws Exception
*/
protected Runnable createMainMethodRunner(String mainClass, String[] args,
ClassLoader classLoader) throws Exception {
Class<?> runnerClass = classLoader.loadClass(RUNNER_CLASS);
Constructor<?> constructor = runnerClass.getConstructor(String.class,
String[].class);
return (Runnable) constructor.newInstance(mainClass, args);
}
}

@ -0,0 +1,60 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher;
import java.lang.reflect.Method;
/**
* Utility class that used by {@link Launcher}s to call a main method. This class allows
* methods to be executed within a thread configured with a specific context classloader.
*
* @author Phillip Webb
*/
public class MainMethodRunner implements Runnable {
private String mainClassName;
private String[] args;
/**
* Create a new {@link MainMethodRunner} instance.
* @param mainClass the main class
* @param args incoming arguments
*/
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = args;
}
@Override
public void run() {
try {
Class<?> mainClass = Thread.currentThread().getContextClassLoader()
.loadClass(mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
if (mainMethod == null) {
throw new IllegalStateException(mainClassName
+ " does not have a main method");
}
mainMethod.invoke(null, new Object[] { args });
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}

@ -0,0 +1,76 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher;
import java.io.IOException;
import java.util.List;
import java.util.jar.JarEntry;
import org.springframework.bootstrap.launcher.jar.JarEntryFilter;
import org.springframework.bootstrap.launcher.jar.RandomAccessJarFile;
/**
* {@link Launcher} for WAR based archives. This launcher for standard WAR archives.
* Supports dependencies in {@code WEB-INF/lib} as well as {@code WEB-INF/lib-provided},
* classes are loaded from {@code WEB-INF/classes}.
*
* @author Phillip Webb
*/
public class WarLauncher extends Launcher {
@Override
protected boolean isNestedJarFile(JarEntry jarEntry) {
if (jarEntry.isDirectory()) {
return jarEntry.getName().equals("WEB-INF/classes/");
} else {
return jarEntry.getName().startsWith("WEB-INF/lib/")
|| jarEntry.getName().startsWith("WEB-INF/lib-provided/");
}
}
@Override
protected void postProcessLib(RandomAccessJarFile jarFile,
List<RandomAccessJarFile> lib) throws Exception {
lib.add(0, filterJarFile(jarFile));
}
/**
* Filter the specified WAR file to exclude elements that should not appear on the
* classpath.
* @param jarFile the source file
* @return the filtered file
* @throws IOException on error
*/
protected RandomAccessJarFile filterJarFile(RandomAccessJarFile jarFile)
throws IOException {
return jarFile.getFilteredJarFile(new JarEntryFilter() {
@Override
public String apply(String entryName, JarEntry entry) {
if (entryName.startsWith("META-INF/") || entryName.startsWith("WEB-INF/")) {
return null;
}
return entryName;
}
});
}
public static void main(String[] args) {
new WarLauncher().launch(args);
}
}

@ -0,0 +1,51 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.data;
import java.io.InputStream;
/**
* Interface that provides read-only random access to some underlying data.
* Implementations must allow concurrent reads in a thread-safe manner.
*
* @author Phillip Webb
*/
public interface RandomAccessData {
/**
* Returns an {@link InputStream} that can be used to read the underling data. The
* caller is responsible close the underlying stream.
*
* @return a new input stream that can be used to read the underlying data.
*/
InputStream getInputStream();
/**
* Returns a new {@link RandomAccessData} for a specific subsection of this data.
* @param offset the offset of the subsection
* @param length the length of the subsection
* @return the subsection data
*/
RandomAccessData getSubsection(long offset, long length);
/**
* Returns the size of the data.
* @return the size
*/
long getSize();
}

@ -0,0 +1,252 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.data;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
/**
* {@link RandomAccessData} implementation backed by a {@link RandomAccessFile}.
*
* @author Phillip Webb
*/
public class RandomAccessDataFile implements RandomAccessData {
private static final int DEFAULT_CONCURRENT_READS = 4;
private File file;
private final FilePool filePool;
private final long offset;
private final long length;
/**
* Create a new {@link RandomAccessDataFile} backed by the specified file.
* @param file the underlying file
* @throws IllegalArgumentException if the file is null or does not exist
* @see #RandomAccessDataFile(File, int)
*/
public RandomAccessDataFile(File file) {
this(file, DEFAULT_CONCURRENT_READS);
}
/**
* Create a new {@link RandomAccessDataFile} backed by the specified file.
* @param file the underlying file
* @param concurrentReads the maximum number of concurrent reads allowed on the
* underlying file before blocking
* @throws IllegalArgumentException if the file is null or does not exist
* @see #RandomAccessDataFile(File)
*/
public RandomAccessDataFile(File file, int concurrentReads) {
if (file == null) {
throw new IllegalArgumentException("File must not be null");
}
if (!file.exists()) {
throw new IllegalArgumentException("File must exist");
}
this.file = file;
this.filePool = new FilePool(concurrentReads);
this.offset = 0L;
this.length = file.length();
}
/**
* Private constructor used to create a {@link #getSubsection(long, long) subsection}.
* @param pool the underlying pool
* @param offset the offset of the section
* @param length the length of the section
*/
private RandomAccessDataFile(FilePool pool, long offset, long length) {
this.filePool = pool;
this.offset = offset;
this.length = length;
}
/**
* Returns the underling File.
* @return the underlying file
*/
public File getFile() {
return this.file;
}
@Override
public InputStream getInputStream() {
return new DataInputStream();
}
@Override
public RandomAccessData getSubsection(long offset, long length) {
if (offset < 0 || length < 0 || offset + length > this.length) {
throw new IndexOutOfBoundsException();
}
return new RandomAccessDataFile(this.filePool, this.offset + offset, length);
}
@Override
public long getSize() {
return this.length;
}
public void close() throws IOException {
this.filePool.close();
}
/**
* {@link RandomAccessDataInputStream} implementation for the
* {@link RandomAccessDataFile}.
*/
private class DataInputStream extends InputStream {
private long position;
@Override
public int read() throws IOException {
return doRead(null, 0, 1);
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b == null ? 0 : b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException("Bytes must not be null");
}
return doRead(b, off, len);
}
/**
* Perform the actual read.
* @param b the bytes to read or {@code null} when reading a single byte
* @param off the offset of the byte array
* @param len the length of data to read
* @return the number of bytes read into {@code b} or the actual read byte if
* {@code b} is {@code null}. Returns -1 when the end of the stream is reached
* @throws IOException
*/
public int doRead(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
if (cap(len) <= 0) {
return -1;
}
RandomAccessFile file = RandomAccessDataFile.this.filePool.acquire();
try {
file.seek(RandomAccessDataFile.this.offset + this.position);
if (b == null) {
int rtn = file.read();
moveOn(rtn == -1 ? 0 : 1);
return rtn;
} else {
return (int) moveOn(file.read(b, off, (int) cap(len)));
}
} finally {
RandomAccessDataFile.this.filePool.release(file);
}
}
@Override
public long skip(long n) throws IOException {
return (n <= 0 ? 0 : moveOn(cap(n)));
}
/**
* Cap the specified value such that it cannot exceed the number of bytes
* remaining.
* @param n the value to cap
* @return the capped value
*/
private long cap(long n) {
return Math.min(RandomAccessDataFile.this.length - this.position, n);
}
/**
* Move the stream position forwards the specified amount
* @param amount the amount to move
* @return the amount moved
*/
private long moveOn(long amount) {
this.position += amount;
return amount;
}
}
/**
* Manage a pool that can be used to perform concurrent reads on the underlying
* {@link RandomAccessFile}.
*/
private class FilePool {
private int size;
private final Semaphore available;
private final Queue<RandomAccessFile> files;
public FilePool(int size) {
this.size = size;
this.available = new Semaphore(size);
this.files = new ConcurrentLinkedQueue<RandomAccessFile>();
}
@SuppressWarnings("resource")
public RandomAccessFile acquire() throws IOException {
try {
this.available.acquire();
RandomAccessFile file = this.files.poll();
return (file == null ? new RandomAccessFile(
RandomAccessDataFile.this.file, "r") : file);
} catch (InterruptedException e) {
throw new IOException(e);
}
}
public void release(RandomAccessFile file) {
this.files.add(file);
this.available.release();
}
public void close() throws IOException {
try {
this.available.acquire(size);
try {
RandomAccessFile file = files.poll();
while (file != null) {
file.close();
file = files.poll();
}
} finally {
this.available.release(size);
}
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}
}

@ -0,0 +1,23 @@
/*
* Copyright 2013 the original author or authors.
*
* 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.
*/
/**
* Classes and interfaces to allows random access to a block of data.
*
* @see org.springframework.bootstrap.launcher.data.RandomAccessData
*/
package org.springframework.bootstrap.launcher.data;

@ -0,0 +1,38 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.jar;
import java.util.jar.JarEntry;
/**
* Interface that can be used to filter and optionally rename jar entries.
*
* @author Phillip Webb
*/
public interface JarEntryFilter {
/**
* Apply the jar entry filter.
* @param entryName the current entry name. This may be different that the original
* entry name if a previous filter has been applied
* @param entry the entry to filter
* @return the new name of the entry or {@code null} if the entry should not be
* included.
*/
String apply(String entryName, JarEntry entry);
}

@ -0,0 +1,49 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.jar;
import java.util.zip.ZipEntry;
import org.springframework.bootstrap.launcher.data.RandomAccessData;
/**
* A {@link ZipEntry} returned from a {@link RandomAccessDataZipInputStream}.
*
* @author Phillip Webb
*/
public class RandomAccessDataZipEntry extends ZipEntry {
private RandomAccessData data;
/**
* Create new {@link RandomAccessDataZipEntry} instance.
* @param entry the underying {@link ZipEntry}
* @param data the entry data
*/
public RandomAccessDataZipEntry(ZipEntry entry, RandomAccessData data) {
super(entry);
this.data = data;
}
/**
* Returns the {@link RandomAccessData} for this entry.
* @return the entry data
*/
public RandomAccessData getData() {
return data;
}
}

@ -0,0 +1,122 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.jar;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.springframework.bootstrap.launcher.data.RandomAccessData;
/**
* A {@link ZipInputStream} backed by {@link RandomAccessData}. Parsed entries provide
* access to the underlying data {@link RandomAccessData#getSubsection(long, long)
* subsection}.
*
* @author Phillip Webb
*/
public class RandomAccessDataZipInputStream extends ZipInputStream {
private RandomAccessData data;
private TrackingInputStream trackingInputStream;
/**
* Create a new {@link RandomAccessData} instance.
* @param data the source of the zip stream
*/
public RandomAccessDataZipInputStream(RandomAccessData data) {
this(data, new TrackingInputStream(data.getInputStream()));
}
/**
* Private constructor used so that we can call the super constructor with a
* {@link TrackingInputStream}.
* @param data the source of the zip stream
* @param trackingInputStream a tracking input stream
*/
private RandomAccessDataZipInputStream(RandomAccessData data,
TrackingInputStream trackingInputStream) {
super(trackingInputStream);
this.data = data;
this.trackingInputStream = trackingInputStream;
}
@Override
public RandomAccessDataZipEntry getNextEntry() throws IOException {
ZipEntry entry = super.getNextEntry();
if (entry == null) {
return null;
}
int start = getPosition();
closeEntry();
int end = getPosition();
RandomAccessData entryData = this.data.getSubsection(start, end - start);
return new RandomAccessDataZipEntry(entry, entryData);
}
private int getPosition() throws IOException {
int pushback = ((PushbackInputStream) this.in).available();
return this.trackingInputStream.getPosition() - pushback;
}
/**
* Internal stream that tracks reads to provide a position.
*/
private static class TrackingInputStream extends FilterInputStream {
private int position = 0;
protected TrackingInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
return moveOn(super.read(), true);
}
@Override
public int read(byte[] b) throws IOException {
return moveOn(super.read(b), false);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return moveOn(super.read(b, off, len), false);
}
private int moveOn(int amount, boolean singleByteRead) {
this.position += (amount == -1 ? 0 : (singleByteRead ? 1 : amount));
return amount;
}
@Override
public int available() throws IOException {
// Always return 0 so that we can accurately use PushbackInputStream.available
return 0;
}
public int getPosition() {
return this.position;
}
}
}

@ -0,0 +1,435 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.jar;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import org.springframework.bootstrap.launcher.data.RandomAccessData;
import org.springframework.bootstrap.launcher.data.RandomAccessDataFile;
/**
* A Jar file that can loaded from a {@link RandomAccessDataFile}. This class extends and
* behaves in the same was a the standard JDK {@link JarFile} the following additional
* functionality.
* <ul>
* <li>Jar entries can be {@link JarEntryFilter filtered} during construction and new
* filtered files can be {@link #getFilteredJarFile(JarEntryFilter...) created} from
* existing files.</li>
* <li>A nested {@link JarFile} can be
* {@link #getNestedJarFile(ZipEntry, JarEntryFilter...) obtained} based on any directory
* entry.</li>
* <li>A nested {@link JarFile} can be
* {@link #getNestedJarFile(ZipEntry, JarEntryFilter...) obtained} for embedded JAR files
* (as long as their entry is not compressed).</li>
* <li>Entry data can be {@link #getData(ZipEntry) accessed} as {@link RandomAccessData}.</li>
* </ul>
*
* @author Phillip Webb
*/
public class RandomAccessJarFile extends JarFile {
private final RandomAccessDataFile rootJarFile;
private RandomAccessData data;
private final String name;
private final long size;
private Map<String, JarEntry> entries = new LinkedHashMap<String, JarEntry>();
private Manifest manifest;
/**
* Create a new {@link RandomAccessJarFile} backed by the specified file.
* @param file the root jar file
* @param filters an optional set of jar entry filters
* @throws IOException
*/
public RandomAccessJarFile(File file, JarEntryFilter... filters) throws IOException {
this(new RandomAccessDataFile(file));
}
/**
* Create a new {@link RandomAccessJarFile} backed by the specified file.
* @param file the root jar file
* @param filters an optional set of jar entry filters
* @throws IOException
*/
public RandomAccessJarFile(RandomAccessDataFile file, JarEntryFilter... filters)
throws IOException {
this(file, file.getFile().getPath(), file);
}
/**
* Private constructor used to create a new {@link RandomAccessJarFile} either
* directly or from a nested entry.
* @param rootJarFile the root jar file
* @param name the name of this file
* @param data the underlying data
* @param filters an optional set of jar entry filters
* @throws IOException
*/
private RandomAccessJarFile(RandomAccessDataFile rootJarFile, String name,
RandomAccessData data, JarEntryFilter... filters) throws IOException {
super(rootJarFile.getFile());
this.rootJarFile = rootJarFile;
this.name = name;
this.data = data;
this.size = data.getSize();
RandomAccessDataZipInputStream inputStream = new RandomAccessDataZipInputStream(
data);
try {
RandomAccessDataZipEntry zipEntry = inputStream.getNextEntry();
while (zipEntry != null) {
addJarEntry(zipEntry, filters);
zipEntry = inputStream.getNextEntry();
}
this.manifest = findManifest();
if (this.manifest != null) {
for (JarEntry containedEntry : this.entries.values()) {
((Entry) containedEntry).configure(this.manifest);
}
}
} finally {
inputStream.close();
}
}
private void addJarEntry(RandomAccessDataZipEntry zipEntry, JarEntryFilter... filters) {
Entry jarEntry = new Entry(zipEntry);
String name = zipEntry.getName();
for (JarEntryFilter filter : filters) {
name = (filter == null || name == null ? name : filter.apply(name, jarEntry));
}
if (name != null) {
jarEntry.setName(name);
this.entries.put(name, jarEntry);
}
}
private Manifest findManifest() throws IOException {
ZipEntry manifestEntry = getEntry(MANIFEST_NAME);
if (manifestEntry != null) {
BufferedInputStream inputStream = new BufferedInputStream(
getInputStream(manifestEntry));
return new Manifest(inputStream);
}
return null;
}
protected final RandomAccessDataFile getRootJarFile() {
return rootJarFile;
}
@Override
public Manifest getManifest() throws IOException {
return this.manifest;
}
@Override
public Enumeration<JarEntry> entries() {
return Collections.enumeration(this.entries.values());
}
@Override
public JarEntry getJarEntry(String name) {
return (JarEntry) getEntry(name);
}
@Override
public ZipEntry getEntry(String name) {
return this.entries.get(name);
}
@Override
public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
InputStream inputStream = getData(ze).getInputStream();
if (ze.getMethod() == ZipEntry.DEFLATED) {
inputStream = new InflaterInputStream(inputStream, new Inflater(true), 512);
}
return inputStream;
}
/**
* Return a nested {@link RandomAccessJarFile} loaded from the specified entry.
* @param ze the zip entry
* @param filters an optional set of jar entry filters to be applied
* @return a {@link RandomAccessJarFile} for the entry
* @throws IOException
*/
public synchronized RandomAccessJarFile getNestedJarFile(final ZipEntry ze,
JarEntryFilter... filters) throws IOException {
if (ze == null) {
throw new IllegalArgumentException("ZipEntry must not be null");
}
if (ze.isDirectory()) {
final String directoryName = ze.getName();
JarEntryFilter[] filtersToUse = new JarEntryFilter[filters.length + 1];
System.arraycopy(filters, 0, filtersToUse, 1, filters.length);
filtersToUse[0] = new JarEntryFilter() {
@Override
public String apply(String entryName, JarEntry entry) {
if (entryName.startsWith(directoryName)
&& !entryName.equals(directoryName)) {
return entryName.substring(ze.getName().length());
}
return null;
}
};
return new RandomAccessJarFile(this.rootJarFile, getName() + "!/"
+ directoryName.substring(0, directoryName.length() - 1), this.data,
filtersToUse);
} else {
if (ze.getMethod() != ZipEntry.STORED) {
throw new IllegalStateException("Unable to open nested compressed entry "
+ ze.getName());
}
return new RandomAccessJarFile(this.rootJarFile, getName() + "!/"
+ ze.getName(), getData(ze), filters);
}
}
/**
* Return a new jar based on the filtered contents of this file.
* @param filters the set of jar entry filters to be applied
* @return a filtered {@link RandomAccessJarFile}
* @throws IOException
*/
public synchronized RandomAccessJarFile getFilteredJarFile(JarEntryFilter... filters)
throws IOException {
return new RandomAccessJarFile(this.rootJarFile, getName(), this.data, filters);
}
/**
* Return {@link RandomAccessData} for the specified entry.
* @param ze the zip entry
* @return the entry {@link RandomAccessData}
* @throws IOException
*/
private synchronized RandomAccessData getData(ZipEntry ze) throws IOException {
if (!this.entries.containsValue(ze)) {
throw new IllegalArgumentException("ZipEntry must be contained in this file");
}
return ((Entry) ze).getData();
}
@Override
public String getName() {
return this.name;
}
@Override
public int size() {
return (int) this.size;
}
@Override
public void close() throws IOException {
this.rootJarFile.close();
}
@Override
public String toString() {
return getName();
}
/**
* Return a URL that can be used to access this JAR file. The returned URL will
* connect using a {@link RandomAccessJarURLConnection}. NOTE: the specified URL
* cannot be serialized and or cloned.
* @return the URL
* @throws MalformedURLException
*/
public URL getUrl() throws MalformedURLException {
RandomAccessJarURLStreamHandler handler = new RandomAccessJarURLStreamHandler(
this);
return new URL("jar", "", -1, "file:" + getName() + "!/", handler);
}
/**
* A single {@link JarEntry} in this file.
*/
private static class Entry extends JarEntry {
private String name;
private RandomAccessData entryData;
private Attributes attributes;
public Entry(RandomAccessDataZipEntry entry) {
super(entry);
this.entryData = entry.getData();
}
void configure(Manifest manifest) {
this.attributes = manifest.getAttributes(getName());
}
void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return (this.name == null ? super.getName() : this.name);
}
@Override
public Attributes getAttributes() throws IOException {
return this.attributes;
}
public RandomAccessData getData() {
return this.entryData;
}
}
/**
* {@link URLStreamHandler} used to support {@link RandomAccessJarFile#getUrl()}.
*/
private static class RandomAccessJarURLStreamHandler extends URLStreamHandler {
private RandomAccessJarFile jarFile;
public RandomAccessJarURLStreamHandler(RandomAccessJarFile jarFile) {
this.jarFile = jarFile;
}
@Override
protected URLConnection openConnection(URL url) throws IOException {
return new RandomAccessJarURLConnection(url, this.jarFile);
}
}
/**
* {@link RandomAccessJarURLConnection} used to support
* {@link RandomAccessJarFile#getUrl()}.
*/
private static class RandomAccessJarURLConnection extends JarURLConnection {
private RandomAccessJarFile jarFile;
private JarEntry jarEntry;
private String jarEntryName;
private String contentType;
protected RandomAccessJarURLConnection(URL url, RandomAccessJarFile jarFile)
throws MalformedURLException {
super(new URL("jar:file:" + jarFile.getRootJarFile().getFile().getPath()
+ "!/"));
this.jarFile = jarFile;
String spec = url.getFile();
int separator = spec.lastIndexOf("!/");
if (separator == -1) {
throw new MalformedURLException("no !/ found in url spec:" + spec);
}
if (separator + 2 != spec.length()) {
this.jarEntryName = spec.substring(separator + 2);
}
}
@Override
public void connect() throws IOException {
if (this.jarEntryName != null) {
this.jarEntry = this.jarFile.getJarEntry(this.jarEntryName);
if (this.jarEntry == null) {
throw new FileNotFoundException("JAR entry " + this.jarEntryName
+ " not found in " + this.jarFile.getName());
}
}
this.connected = true;
}
@Override
public RandomAccessJarFile getJarFile() throws IOException {
connect();
return this.jarFile;
}
@Override
public JarEntry getJarEntry() throws IOException {
connect();
return this.jarEntry;
}
@Override
public InputStream getInputStream() throws IOException {
connect();
if (this.jarEntryName == null) {
throw new IOException("no entry name specified");
}
return this.jarFile.getInputStream(this.jarEntry);
}
@Override
public int getContentLength() {
try {
connect();
return (int) (this.jarEntry == null ? this.jarFile.size() : this.jarEntry
.getSize());
} catch (IOException e) {
return -1;
}
}
@Override
public Object getContent() throws IOException {
connect();
return (this.jarEntry == null ? this.jarFile : super.getContent());
}
@Override
public String getContentType() {
if (this.contentType == null) {
// Guess the content type, don't bother with steams as mark is not
// supported
this.contentType = (this.jarEntryName == null ? "x-java/jar" : null);
this.contentType = (this.contentType == null ? guessContentTypeFromName(this.jarEntryName)
: contentType);
this.contentType = (this.contentType == null ? "content/unknown"
: contentType);
}
return this.contentType;
}
}
}

@ -0,0 +1,21 @@
/*
* Copyright 2013 the original author or authors.
*
* 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.
*/
/**
* Support for loading and manipulating JAR/WAR files.
*/
package org.springframework.bootstrap.launcher.jar;

@ -0,0 +1,27 @@
/*
* Copyright 2013 the original author or authors.
*
* 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.
*/
/**
* System that allows self contained JAR/WAR archives to be launched using
* {@code java -jar}. Archives can include nested packaged dependency JARs (there is
* not need to create shade style jars) and are executed without unpacking. The only
* constraint is that nested JARs must be stored in the archive uncompressed.
*
* @see org.springframework.bootstrap.launcher.JarLauncher
* @see org.springframework.bootstrap.launcher.WarLauncher
*/
package org.springframework.bootstrap.launcher;

@ -0,0 +1,53 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
/**
* Hamcrest matcher to tests that a byte array starts with specific bytes.
*
* @author Phillip Webb
*/
public class ByteArrayStartsWith extends TypeSafeMatcher<byte[]> {
private byte[] bytes;
public ByteArrayStartsWith(byte[] bytes) {
this.bytes = bytes;
}
@Override
public void describeTo(Description description) {
description.appendText("a byte array starting with ").appendValue(bytes);
}
@Override
protected boolean matchesSafely(byte[] item) {
if (item.length < bytes.length) {
return false;
}
for (int i = 0; i < bytes.length; i++) {
if (item[i] != bytes[i]) {
return false;
}
}
return true;
}
}

@ -0,0 +1,313 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.data;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.springframework.bootstrap.launcher.ByteArrayStartsWith;
/**
* Tests for {@link RandomAccessDataFile}.
*
* @author Phillip Webb
*/
public class RandomAccessDataFileTest {
private static final byte[] BYTES;
static {
BYTES = new byte[256];
for (int i = 0; i < BYTES.length; i++) {
BYTES[i] = (byte) i;
}
}
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File tempFile;
private RandomAccessDataFile file;
private InputStream inputStream;
@Before
public void setup() throws Exception {
this.tempFile = temporaryFolder.newFile();
FileOutputStream outputStream = new FileOutputStream(tempFile);
outputStream.write(BYTES);
outputStream.close();
this.file = new RandomAccessDataFile(tempFile);
this.inputStream = file.getInputStream();
}
@After
public void cleanup() throws Exception {
inputStream.close();
file.close();
}
@Test
public void fileNotNull() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.equals("File must not be null");
new RandomAccessDataFile(null);
}
@Test
public void fileExists() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.equals("File must exist");
new RandomAccessDataFile(new File("/does/not/exist"));
}
@Test
public void fileNotNullWithConcurrentReads() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.equals("File must not be null");
new RandomAccessDataFile(null, 1);
}
@Test
public void fileExistsWithConcurrentReads() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.equals("File must exist");
new RandomAccessDataFile(new File("/does/not/exist"), 1);
}
@Test
public void inputStreamRead() throws Exception {
for (int i = 0; i <= 255; i++) {
assertThat(inputStream.read(), equalTo(i));
}
}
@Test
public void inputStreamReadNullBytes() throws Exception {
thrown.expect(NullPointerException.class);
thrown.expectMessage("Bytes must not be null");
inputStream.read(null);
}
@Test
public void intputStreamReadNullBytesWithOffset() throws Exception {
thrown.expect(NullPointerException.class);
thrown.expectMessage("Bytes must not be null");
inputStream.read(null, 0, 1);
}
@Test
public void inputStreamReadBytes() throws Exception {
byte[] b = new byte[256];
int amountRead = inputStream.read(b);
assertThat(b, equalTo(BYTES));
assertThat(amountRead, equalTo(256));
}
@Test
public void inputSteamReadOffsetBytes() throws Exception {
byte[] b = new byte[7];
inputStream.skip(1);
int amountRead = inputStream.read(b, 2, 3);
assertThat(b, equalTo(new byte[] { 0, 0, 1, 2, 3, 0, 0 }));
assertThat(amountRead, equalTo(3));
}
@Test
public void inputStreamReadMoreBytesThanAvailable() throws Exception {
byte[] b = new byte[257];
int amountRead = inputStream.read(b);
assertThat(b, startsWith(BYTES));
assertThat(amountRead, equalTo(256));
}
@Test
public void inputStreamReadPastEnd() throws Exception {
inputStream.skip(255);
assertThat(inputStream.read(), equalTo(0xFF));
assertThat(inputStream.read(), equalTo(-1));
assertThat(inputStream.read(), equalTo(-1));
}
@Test
public void inputStreamReadZeroLength() throws Exception {
byte[] b = new byte[] { 0x0F };
int amountRead = inputStream.read(b, 0, 0);
assertThat(b, equalTo(new byte[] { 0x0F }));
assertThat(amountRead, equalTo(0));
assertThat(inputStream.read(), equalTo(0));
}
@Test
public void inputStreamSkip() throws Exception {
long amountSkipped = inputStream.skip(4);
assertThat(inputStream.read(), equalTo(4));
assertThat(amountSkipped, equalTo(4L));
}
@Test
public void inputStreamSkipMoreThanAvailable() throws Exception {
long amountSkipped = inputStream.skip(257);
assertThat(inputStream.read(), equalTo(-1));
assertThat(amountSkipped, equalTo(256L));
}
@Test
public void inputStreamSkipPastEnd() throws Exception {
inputStream.skip(256);
long amountSkipped = inputStream.skip(1);
assertThat(amountSkipped, equalTo(0L));
}
@Test
public void subsectionNegativeOffset() throws Exception {
thrown.expect(IndexOutOfBoundsException.class);
file.getSubsection(-1, 1);
}
@Test
public void subsectionNegativeLength() throws Exception {
thrown.expect(IndexOutOfBoundsException.class);
file.getSubsection(0, -1);
}
@Test
public void subsectionZeroLength() throws Exception {
RandomAccessData subsection = file.getSubsection(0, 0);
assertThat(subsection.getInputStream().read(), equalTo(-1));
}
@Test
public void subsectionTooBig() throws Exception {
file.getSubsection(0, 256);
thrown.expect(IndexOutOfBoundsException.class);
file.getSubsection(0, 257);
}
@Test
public void subsectionTooBigWithOffset() throws Exception {
file.getSubsection(1, 255);
thrown.expect(IndexOutOfBoundsException.class);
file.getSubsection(1, 256);
}
@Test
public void subsection() throws Exception {
RandomAccessData subsection = file.getSubsection(1, 1);
assertThat(subsection.getInputStream().read(), equalTo(1));
}
@Test
public void inputStreamReadPastSubsection() throws Exception {
RandomAccessData subsection = file.getSubsection(1, 2);
InputStream inputStream = subsection.getInputStream();
assertThat(inputStream.read(), equalTo(1));
assertThat(inputStream.read(), equalTo(2));
assertThat(inputStream.read(), equalTo(-1));
}
@Test
public void inputStreamReadBytesPastSubsection() throws Exception {
RandomAccessData subsection = file.getSubsection(1, 2);
InputStream inputStream = subsection.getInputStream();
byte[] b = new byte[3];
int amountRead = inputStream.read(b);
assertThat(b, equalTo(new byte[] { 1, 2, 0 }));
assertThat(amountRead, equalTo(2));
}
@Test
public void inputStreamSkipPastSubsection() throws Exception {
RandomAccessData subsection = file.getSubsection(1, 2);
InputStream inputStream = subsection.getInputStream();
assertThat(inputStream.skip(3), equalTo(2L));
assertThat(inputStream.read(), equalTo(-1));
}
@Test
public void inputStreamSkipNegative() throws Exception {
assertThat(inputStream.skip(-1), equalTo(0L));
}
@Test
public void getFile() throws Exception {
assertThat(file.getFile(), equalTo(tempFile));
}
@Test
public void concurrentReads() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(20);
List<Future<Boolean>> results = new ArrayList<Future<Boolean>>();
for (int i = 0; i < 100; i++) {
results.add(executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
InputStream subsectionInputStream = file.getSubsection(0, 256)
.getInputStream();
byte[] b = new byte[256];
subsectionInputStream.read(b);
return Arrays.equals(b, BYTES);
}
}));
}
for (Future<Boolean> future : results) {
assertThat(future.get(), equalTo(true));
}
}
@Test
public void close() throws Exception {
file.getInputStream().read();
file.close();
Field filePoolField = RandomAccessDataFile.class.getDeclaredField("filePool");
filePoolField.setAccessible(true);
Object filePool = filePoolField.get(file);
Field filesField = filePool.getClass().getDeclaredField("files");
filesField.setAccessible(true);
Queue<?> queue = (Queue<?>) filesField.get(filePool);
assertThat(queue.size(), equalTo(0));
}
private static Matcher<? super byte[]> startsWith(byte[] bytes) {
return new ByteArrayStartsWith(bytes);
}
}

@ -0,0 +1,91 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.jar;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.bootstrap.launcher.data.RandomAccessDataFile;
/**
* Tests for {@link RandomAccessDataZipInputStream}.
*
* @author Phillip Webb
*/
public class RandomAccessDataZipInputStreamTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File file;
@Before
public void setup() throws Exception {
this.file = temporaryFolder.newFile();
ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(file));
try {
writeDataEntry(zipOutputStream, "a", new byte[10]);
writeDataEntry(zipOutputStream, "b", new byte[20]);
} finally {
zipOutputStream.close();
}
}
private void writeDataEntry(ZipOutputStream zipOutputStream, String name, byte[] data)
throws IOException {
ZipEntry entry = new ZipEntry(name);
entry.setMethod(ZipEntry.STORED);
entry.setSize(data.length);
entry.setCompressedSize(data.length);
CRC32 crc32 = new CRC32();
crc32.update(data);
entry.setCrc(crc32.getValue());
zipOutputStream.putNextEntry(entry);
zipOutputStream.write(data);
zipOutputStream.closeEntry();
}
@Test
public void entryData() throws Exception {
RandomAccessDataZipInputStream z = new RandomAccessDataZipInputStream(
new RandomAccessDataFile(file));
try {
RandomAccessDataZipEntry entry1 = z.getNextEntry();
RandomAccessDataZipEntry entry2 = z.getNextEntry();
assertThat(entry1.getName(), equalTo("a"));
assertThat(entry1.getData().getSize(), equalTo(10L));
assertThat(entry2.getName(), equalTo("b"));
assertThat(entry2.getData().getSize(), equalTo(20L));
assertThat(z.getNextEntry(), nullValue());
} finally {
z.close();
}
}
}

@ -0,0 +1,331 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 org.springframework.bootstrap.launcher.jar;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.springframework.bootstrap.launcher.data.RandomAccessDataFile;
/**
* Tests for {@link RandomAccessJarFile}.
*
* @author Phillip Webb
*/
public class RandomAccessJarFileTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File rootJarFile;
private RandomAccessJarFile jarFile;
@Before
public void setup() throws Exception {
this.rootJarFile = temporaryFolder.newFile();
FileOutputStream fileOutputStream = new FileOutputStream(this.rootJarFile);
JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream);
try {
writeManifest(jarOutputStream, "j1");
writeEntry(jarOutputStream, "1.dat", 1);
writeEntry(jarOutputStream, "2.dat", 2);
writeDirEntry(jarOutputStream, "d/");
writeEntry(jarOutputStream, "d/9.dat", 9);
JarEntry nestedEntry = new JarEntry("nested.jar");
byte[] nestedJarData = getNestedJarData();
nestedEntry.setSize(nestedJarData.length);
nestedEntry.setCompressedSize(nestedJarData.length);
CRC32 crc32 = new CRC32();
crc32.update(nestedJarData);
nestedEntry.setCrc(crc32.getValue());
nestedEntry.setMethod(ZipEntry.STORED);
jarOutputStream.putNextEntry(nestedEntry);
jarOutputStream.write(nestedJarData);
jarOutputStream.closeEntry();
} finally {
jarOutputStream.close();
}
this.jarFile = new RandomAccessJarFile(this.rootJarFile);
}
private byte[] getNestedJarData() throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
JarOutputStream jarOutputStream = new JarOutputStream(byteArrayOutputStream);
writeManifest(jarOutputStream, "j2");
writeEntry(jarOutputStream, "3.dat", 3);
writeEntry(jarOutputStream, "4.dat", 4);
jarOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
private void writeManifest(JarOutputStream jarOutputStream, String name)
throws Exception {
writeDirEntry(jarOutputStream, "META-INF/");
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Built-By", name);
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
jarOutputStream.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
manifest.write(jarOutputStream);
jarOutputStream.closeEntry();
}
private void writeDirEntry(JarOutputStream jarOutputStream, String name)
throws IOException {
jarOutputStream.putNextEntry(new JarEntry(name));
jarOutputStream.closeEntry();
}
private void writeEntry(JarOutputStream jarOutputStream, String name, int data)
throws IOException {
jarOutputStream.putNextEntry(new JarEntry(name));
jarOutputStream.write(new byte[] { (byte) data });
jarOutputStream.closeEntry();
}
@Test
public void createFromFile() throws Exception {
RandomAccessJarFile jarFile = new RandomAccessJarFile(this.rootJarFile);
assertThat(jarFile.getName(), notNullValue(String.class));
}
@Test
public void createFromRandomAccessDataFile() throws Exception {
RandomAccessDataFile randomAccessDataFile = new RandomAccessDataFile(
this.rootJarFile, 1);
RandomAccessJarFile jarFile = new RandomAccessJarFile(randomAccessDataFile);
assertThat(jarFile.getName(), notNullValue(String.class));
}
@Test
public void getManifest() throws Exception {
assertThat(jarFile.getManifest().getMainAttributes().getValue("Built-By"),
equalTo("j1"));
}
@Test
public void getEntries() throws Exception {
Enumeration<JarEntry> entries = jarFile.entries();
assertThat(entries.nextElement().getName(), equalTo("META-INF/"));
assertThat(entries.nextElement().getName(), equalTo("META-INF/MANIFEST.MF"));
assertThat(entries.nextElement().getName(), equalTo("1.dat"));
assertThat(entries.nextElement().getName(), equalTo("2.dat"));
assertThat(entries.nextElement().getName(), equalTo("d/"));
assertThat(entries.nextElement().getName(), equalTo("d/9.dat"));
assertThat(entries.nextElement().getName(), equalTo("nested.jar"));
assertThat(entries.hasMoreElements(), equalTo(false));
}
@Test
public void getJarEntry() throws Exception {
JarEntry entry = jarFile.getJarEntry("1.dat");
assertThat(entry, notNullValue(ZipEntry.class));
assertThat(entry.getName(), equalTo("1.dat"));
}
@Test
public void getInputStream() throws Exception {
InputStream inputStream = jarFile.getInputStream(jarFile.getEntry("1.dat"));
assertThat(inputStream.read(), equalTo(1));
assertThat(inputStream.read(), equalTo(-1));
}
@Test
public void getName() throws Exception {
assertThat(jarFile.getName(), equalTo(rootJarFile.getPath()));
}
@Test
public void getSize() throws Exception {
assertThat(jarFile.size(), equalTo((int) rootJarFile.length()));
}
@Test
public void close() throws Exception {
RandomAccessDataFile randomAccessDataFile = spy(new RandomAccessDataFile(
this.rootJarFile, 1));
RandomAccessJarFile jarFile = new RandomAccessJarFile(randomAccessDataFile);
jarFile.close();
verify(randomAccessDataFile).close();
}
@Test
public void getUrl() throws Exception {
URL url = jarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + rootJarFile.getPath() + "!/"));
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile(), sameInstance((JarFile) jarFile));
assertThat(jarURLConnection.getJarEntry(), nullValue());
assertThat(jarURLConnection.getContentLength(), greaterThan(1));
assertThat(jarURLConnection.getContent(), sameInstance((Object) jarFile));
assertThat(jarURLConnection.getContentType(), equalTo("x-java/jar"));
}
@Test
public void getEntryUrl() throws Exception {
URL url = new URL(jarFile.getUrl(), "1.dat");
assertThat(url.toString(), equalTo("jar:file:" + rootJarFile.getPath()
+ "!/1.dat"));
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile(), sameInstance((JarFile) jarFile));
assertThat(jarURLConnection.getJarEntry(),
sameInstance(jarFile.getJarEntry("1.dat")));
assertThat(jarURLConnection.getContentLength(), equalTo(1));
assertThat(jarURLConnection.getContent(), instanceOf(InputStream.class));
assertThat(jarURLConnection.getContentType(), equalTo("content/unknown"));
}
@Test
public void getMissingEntryUrl() throws Exception {
URL url = new URL(jarFile.getUrl(), "missing.dat");
assertThat(url.toString(), equalTo("jar:file:" + rootJarFile.getPath()
+ "!/missing.dat"));
thrown.expect(FileNotFoundException.class);
((JarURLConnection) url.openConnection()).getJarEntry();
}
@Test
public void getUrlStream() throws Exception {
URL url = jarFile.getUrl();
url.openConnection();
thrown.expect(IOException.class);
url.openStream();
}
@Test
public void getEntryUrlStream() throws Exception {
URL url = new URL(jarFile.getUrl(), "1.dat");
url.openConnection();
InputStream stream = url.openStream();
assertThat(stream.read(), equalTo(1));
assertThat(stream.read(), equalTo(-1));
}
@Test
public void getNestedJarFile() throws Exception {
RandomAccessJarFile nestedJarFile = jarFile.getNestedJarFile(jarFile
.getEntry("nested.jar"));
Enumeration<JarEntry> entries = nestedJarFile.entries();
assertThat(entries.nextElement().getName(), equalTo("META-INF/"));
assertThat(entries.nextElement().getName(), equalTo("META-INF/MANIFEST.MF"));
assertThat(entries.nextElement().getName(), equalTo("3.dat"));
assertThat(entries.nextElement().getName(), equalTo("4.dat"));
assertThat(entries.hasMoreElements(), equalTo(false));
InputStream inputStream = nestedJarFile.getInputStream(nestedJarFile
.getEntry("3.dat"));
assertThat(inputStream.read(), equalTo(3));
assertThat(inputStream.read(), equalTo(-1));
URL url = nestedJarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + rootJarFile.getPath()
+ "!/nested.jar!/"));
assertThat(((JarURLConnection) url.openConnection()).getJarFile(),
sameInstance((JarFile) nestedJarFile));
}
@Test
public void getNestedJarDirectory() throws Exception {
RandomAccessJarFile nestedJarFile = jarFile.getNestedJarFile(jarFile
.getEntry("d/"));
Enumeration<JarEntry> entries = nestedJarFile.entries();
assertThat(entries.nextElement().getName(), equalTo("9.dat"));
assertThat(entries.hasMoreElements(), equalTo(false));
InputStream inputStream = nestedJarFile.getInputStream(nestedJarFile
.getEntry("9.dat"));
assertThat(inputStream.read(), equalTo(9));
assertThat(inputStream.read(), equalTo(-1));
URL url = nestedJarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + rootJarFile.getPath() + "!/d!/"));
assertThat(((JarURLConnection) url.openConnection()).getJarFile(),
sameInstance((JarFile) nestedJarFile));
}
@Test
public void getDirectoryInputStream() throws Exception {
InputStream inputStream = jarFile.getInputStream(jarFile.getEntry("d/"));
assertThat(inputStream, notNullValue());
assertThat(inputStream.read(), equalTo(-1));
}
@Test
public void getFilteredJarFile() throws Exception {
RandomAccessJarFile filteredJarFile = jarFile
.getFilteredJarFile(new JarEntryFilter() {
@Override
public String apply(String entryName, JarEntry entry) {
if (entryName.equals("1.dat")) {
return "x.dat";
}
return null;
}
});
Enumeration<JarEntry> entries = filteredJarFile.entries();
assertThat(entries.nextElement().getName(), equalTo("x.dat"));
assertThat(entries.hasMoreElements(), equalTo(false));
InputStream inputStream = filteredJarFile.getInputStream(filteredJarFile
.getEntry("x.dat"));
assertThat(inputStream.read(), equalTo(1));
assertThat(inputStream.read(), equalTo(-1));
}
@Test
public void sensibleToString() throws Exception {
assertThat(jarFile.toString(), equalTo(rootJarFile.getPath()));
assertThat(jarFile.getNestedJarFile(jarFile.getEntry("nested.jar")).toString(),
equalTo(rootJarFile.getPath() + "!/nested.jar"));
}
}

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-samples</artifactId>
<packaging>pom</packaging>
<properties>
<main.basedir>${project.basedir}/..</main.basedir>
</properties>
<modules>
<module>spring-bootstrap-sample</module>
<module>spring-bootstrap-data-sample</module>
<module>spring-bootstrap-jetty-sample</module>
<module>spring-bootstrap-simple-sample</module>
<module>spring-bootstrap-tomcat-sample</module>
<module>spring-bootstrap-profile-sample</module>
<module>spring-bootstrap-service-sample</module>
<module>spring-bootstrap-trad-sample</module>
<module>spring-bootstrap-xml-sample</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>prepare-package</phase>
<inherited>true</inherited>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-launcher</artifactId>
<version>${project.version}</version>
<type>jar</type>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}/assembly</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy</id>
<phase>prepare-package</phase>
<inherited>true</inherited>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/assembly/lib</outputDirectory>
<scope>runtime</scope>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>${project.parent.basedir}/src/main/assembly/jar-with-dependencies.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>org.springframework.bootstrap.launcher.JarLauncher</mainClass>
</manifest>
<manifestEntries>
<Start-Class>${start-class}</Start-Class>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>jar-with-dependencies</id>
<phase>package</phase>
<inherited>true</inherited>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer
implementation="org.springframework.bootstrap.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${start-class}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-samples</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-data-sample</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
<start-class>org.springframework.bootstrap.sample.data.DataBootstrapApplication</start-class>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,17 @@
package org.springframework.bootstrap.sample.data;
import org.springframework.bootstrap.SpringApplication;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class DataBootstrapApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(DataBootstrapApplication.class, args);
}
}

@ -0,0 +1,60 @@
package org.springframework.bootstrap.sample.data.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class City implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String state;
@Column(nullable = false)
private String country;
@Column(nullable = false)
private String map;
protected City() {
}
public City(String name, String country) {
super();
this.name = name;
this.country = country;
}
public String getName() {
return this.name;
}
public String getState() {
return this.state;
}
public String getCountry() {
return this.country;
}
public String getMap() {
return this.map;
}
@Override
public String toString() {
return getName() + "," + getState() + "," + getCountry();
}
}

@ -0,0 +1,65 @@
package org.springframework.bootstrap.sample.data.domain;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.NaturalId;
@Entity
public class Hotel implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false)
@NaturalId
private City city;
@Column(nullable = false)
@NaturalId
private String name;
@Column(nullable = false)
private String address;
@Column(nullable = false)
private String zip;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "hotel")
private Set<Review> reviews;
protected Hotel() {
}
public Hotel(City city, String name) {
this.city = city;
this.name = name;
}
public City getCity() {
return this.city;
}
public String getName() {
return this.name;
}
public String getAddress() {
return this.address;
}
public String getZip() {
return this.zip;
}
}

@ -0,0 +1,47 @@
package org.springframework.bootstrap.sample.data.domain;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class HotelSummary implements Serializable {
private static final long serialVersionUID = 1L;
private static final MathContext MATH_CONTEXT = new MathContext(2,
RoundingMode.HALF_UP);
private City city;
private String name;
private Double averageRating;
private Integer averageRatingRounded;
public HotelSummary(City city, String name, Double averageRating) {
this.city = city;
this.name = name;
this.averageRating = averageRating == null ? null : new BigDecimal(averageRating,
MATH_CONTEXT).doubleValue();
this.averageRatingRounded = averageRating == null ? null : (int) Math
.round(averageRating);
}
public City getCity() {
return this.city;
}
public String getName() {
return this.name;
}
public Double getAverageRating() {
return this.averageRating;
}
public Integer getAverageRatingRounded() {
return this.averageRatingRounded;
}
}

@ -0,0 +1,5 @@
package org.springframework.bootstrap.sample.data.domain;
public enum Rating {
TERRIBLE, POOR, AVERAGE, GOOD, EXCELLENT,
}

@ -0,0 +1,25 @@
package org.springframework.bootstrap.sample.data.domain;
import java.io.Serializable;
public class RatingCount implements Serializable {
private static final long serialVersionUID = 1L;
private Rating rating;
private long count;
public RatingCount(Rating rating, long count) {
this.rating = rating;
this.count = count;
}
public Rating getRating() {
return this.rating;
}
public long getCount() {
return this.count;
}
}

@ -0,0 +1,113 @@
package org.springframework.bootstrap.sample.data.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.util.Assert;
@Entity
public class Review implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@ManyToOne(optional = false)
private Hotel hotel;
@Column(nullable = false)
private int index;
@Column(nullable = false)
@Enumerated(EnumType.ORDINAL)
private Rating rating;
@Column(nullable = false)
@Temporal(TemporalType.DATE)
private Date checkInDate;
@Column(nullable = false)
@Enumerated(EnumType.ORDINAL)
private TripType tripType;
@Column(nullable = false)
private String title;
@Column(nullable = false, length = 5000)
private String details;
protected Review() {
}
public Review(Hotel hotel, int index, ReviewDetails details) {
Assert.notNull(hotel, "Hotel must not be null");
Assert.notNull(details, "Details must not be null");
this.hotel = hotel;
this.index = index;
this.rating = details.getRating();
this.checkInDate = details.getCheckInDate();
this.tripType = details.getTripType();
this.title = details.getTitle();
this.details = details.getDetails();
}
public Hotel getHotel() {
return this.hotel;
}
public int getIndex() {
return this.index;
}
public Rating getRating() {
return this.rating;
}
public void setRating(Rating rating) {
this.rating = rating;
}
public Date getCheckInDate() {
return this.checkInDate;
}
public void setCheckInDate(Date checkInDate) {
this.checkInDate = checkInDate;
}
public TripType getTripType() {
return this.tripType;
}
public void setTripType(TripType tripType) {
this.tripType = tripType;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDetails() {
return this.details;
}
public void setDetails(String details) {
this.details = details;
}
}

@ -0,0 +1,70 @@
package org.springframework.bootstrap.sample.data.domain;
import java.io.Serializable;
import java.util.Date;
//FIXME Hibernate bug HHH-5792 prevents this being embedded
public class ReviewDetails implements Serializable {
private static final long serialVersionUID = 1L;
private Rating rating;
private Date checkInDate;
private TripType tripType;
private String title;
private String details;
public ReviewDetails() {
}
@Deprecated
public ReviewDetails(String title, Rating rating) {
this.title = title;
this.rating = rating;
}
public Rating getRating() {
return this.rating;
}
public void setRating(Rating rating) {
this.rating = rating;
}
public Date getCheckInDate() {
return this.checkInDate;
}
public void setCheckInDate(Date checkInDate) {
this.checkInDate = checkInDate;
}
public TripType getTripType() {
return this.tripType;
}
public void setTripType(TripType tripType) {
this.tripType = tripType;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDetails() {
return this.details;
}
public void setDetails(String details) {
this.details = details;
}
}

@ -0,0 +1,5 @@
package org.springframework.bootstrap.sample.data.domain;
public enum TripType {
BUSINESS, COUPLES, FAMILY, FRIENDS, SOLO
}

@ -0,0 +1,17 @@
package org.springframework.bootstrap.sample.data.domain.repository;
import org.springframework.bootstrap.sample.data.domain.City;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
public interface CityRepository extends Repository<City, Long> {
Page<City> findAll(Pageable pageable);
Page<City> findByNameLikeAndCountryLikeAllIgnoringCase(String name, String country,
Pageable pageable);
City findByNameAndCountryAllIgnoringCase(String name, String country);
}

@ -0,0 +1,11 @@
package org.springframework.bootstrap.sample.data.domain.repository;
import org.springframework.bootstrap.sample.data.domain.City;
import org.springframework.bootstrap.sample.data.domain.Hotel;
import org.springframework.data.repository.Repository;
public interface HotelRepository extends Repository<Hotel, Long> {
Hotel findByCityAndName(City city, String name);
}

@ -0,0 +1,90 @@
package org.springframework.bootstrap.sample.data.domain.repository;
import java.util.List;
import java.util.Locale;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.bootstrap.sample.data.domain.City;
import org.springframework.bootstrap.sample.data.domain.Hotel;
import org.springframework.bootstrap.sample.data.domain.HotelSummary;
import org.springframework.bootstrap.sample.data.domain.RatingCount;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Repository;
@Repository
public class HotelSummaryRepository {
private static final String AVERAGE_REVIEW_FUNCTION = "avg(r.rating)";
private static final String FIND_BY_CITY_QUERY = "select new "
+ HotelSummary.class.getName() + "(h.city, h.name, "
+ AVERAGE_REVIEW_FUNCTION
+ ") from Hotel h left outer join h.reviews r where h.city = ?1 group by h";
private static final String FIND_BY_CITY_COUNT_QUERY = "select count(h) from Hotel h where h.city = ?1";
private static final String FIND_RATING_COUNTS_QUERY = "select new "
+ RatingCount.class.getName() + "(r.rating, count(r)) "
+ "from Review r where r.hotel = ?1 group by r.rating order by r.rating DESC";
private EntityManager entityManager;
public Page<HotelSummary> findByCity(City city, Pageable pageable) {
StringBuilder queryString = new StringBuilder(FIND_BY_CITY_QUERY);
applySorting(queryString, pageable == null ? null : pageable.getSort());
Query query = entityManager.createQuery(queryString.toString());
query.setParameter(1, city);
query.setFirstResult(pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
Query countQuery = entityManager.createQuery(FIND_BY_CITY_COUNT_QUERY);
countQuery.setParameter(1, city);
@SuppressWarnings("unchecked")
List<HotelSummary> content = query.getResultList();
Long total = (Long) countQuery.getSingleResult();
return new PageImpl<HotelSummary>(content, pageable, total);
}
@SuppressWarnings("unchecked")
public List<RatingCount> findRatingCounts(Hotel hotel) {
Query query = entityManager.createQuery(FIND_RATING_COUNTS_QUERY);
query.setParameter(1, hotel);
return query.getResultList();
}
private void applySorting(StringBuilder query, Sort sort) {
if (sort != null) {
query.append(" order by");
for (Order order : sort) {
String aliasedProperty = getAliasedProperty(order.getProperty());
query.append(String.format(" %s %s,", aliasedProperty, order
.getDirection().name().toLowerCase(Locale.US)));
}
query.deleteCharAt(query.length() - 1);
}
}
private String getAliasedProperty(String property) {
if (property.equals("averageRating")) {
return AVERAGE_REVIEW_FUNCTION;
}
return "h." + property;
}
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}

@ -0,0 +1,15 @@
package org.springframework.bootstrap.sample.data.domain.repository;
import org.springframework.bootstrap.sample.data.domain.Hotel;
import org.springframework.bootstrap.sample.data.domain.Review;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
public interface ReviewRepository extends Repository<Review, Long> {
Page<Review> findByHotel(Hotel hotel, Pageable pageable);
Review findByHotelAndIndex(Hotel hotel, int index);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save