initial import

This commit is contained in:
Janne Valkealahti
2015-02-04 18:17:50 +00:00
parent ded18f863f
commit 48c65fd9ac
155 changed files with 12028 additions and 0 deletions

178
build.gradle Normal file
View File

@@ -0,0 +1,178 @@
buildscript {
repositories {
maven { url 'http://repo.springsource.org/libs-release'}
maven { url 'http://repo.springsource.org/plugins-release' }
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath('org.asciidoctor:asciidoctor-gradle-plugin:1.5.2')
classpath("io.spring.gradle:docbook-reference-plugin:0.3.0")
}
}
configure(allprojects) {
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
sourceCompatibility = 1.6
targetCompatibility = 1.6
group = 'org.springframework.statemachine'
[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:none']
repositories {
mavenCentral()
maven { url "http://repo.springsource.org/libs-release" }
}
task integrationTest(type: Test) {
include '**/*IntegrationTests.*'
}
test {
exclude '**/*IntegrationTests.*'
}
// servlet-api (2.5) and tomcat-servlet-api (3.0) classpath entries should not be
// exported to dependent projects in Eclipse to avoid false compilation errors due
// to changing APIs across these versions
eclipse.classpath.file.whenMerged { classpath ->
classpath.entries.findAll { entry -> entry.path.contains('servlet-api') }*.exported = false
}
}
configure(subprojects) { subproject ->
apply from: "${rootProject.projectDir}/publish-maven.gradle"
jar {
manifest.attributes['Implementation-Title'] = subproject.name
manifest.attributes['Implementation-Version'] = subproject.version
from("${rootProject.projectDir}/src/dist") {
include "license.txt"
include "notice.txt"
into "META-INF"
expand(copyright: new Date().format('yyyy'), version: project.version)
}
}
javadoc {
// /config/configuration/StateMachineConfiguration.html...
// java.lang.ClassCastException: com.sun.tools.javadoc.MethodDocImpl cannot be cast
// to com.sun.tools.javadoc.AnnotationTypeElementDocImpl
// @Bean(name = StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY)
// vs.
// @Bean
enabled = false
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
options.header = project.name
verbose = true
}
task sourcesJar(type: Jar, dependsOn:classes) {
classifier = 'sources'
from sourceSets.main.allJava
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
artifacts {
archives sourcesJar
archives javadocJar
}
}
project('spring-statemachine-core') {
description = "Spring State Machine Core"
dependencies {
compile "org.springframework:spring-messaging:$springVersion"
testCompile "org.springframework:spring-test:$springVersion"
testCompile "org.hamcrest:hamcrest-core:$hamcrestVersion"
testCompile "org.hamcrest:hamcrest-library:$hamcrestVersion"
testCompile "junit:junit:$junitVersion"
}
}
configure(rootProject) {
description = 'Spring State Machine'
apply plugin: 'org.asciidoctor.gradle.asciidoctor'
apply plugin: "docbook-reference"
// don't publish the default jar for the root project
configurations.archives.artifacts.clear()
reference {
sourceDir = new File(asciidoctor.outputDir , 'docbook5')
pdfFilename = "spring-statemachine-reference.pdf"
epubFilename = "spring-statemachine-reference.epub"
expandPlaceholders = ""
}
afterEvaluate {
tasks.findAll { it.name.startsWith("reference") }.each{ it.dependsOn.add("asciidoctor") }
}
asciidoctorj {
version = '1.5.2'
}
asciidoctor {
sourceDir = file("docs/src/reference/asciidoc")
backends = ['docbook5']
options eruby: 'erubis'
attributes docinfo: '',
copycss : '',
icons : 'font',
'source-highlighter': 'prettify',
sectanchors : '',
toc2: '',
idprefix: '',
idseparator: '-',
doctype: 'book',
numbered: '',
'spring-hadoop-version' : project.version,
'spring-version' : springVersion,
revnumber : project.version
}
dependencies { // for integration tests
}
task api(type: Javadoc) {
group = 'Documentation'
description = 'Generates aggregated Javadoc API documentation.'
title = "${rootProject.description} ${version} API"
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
options.header = rootProject.description
options.overview = 'src/api/overview.html'
options.links(
'http://docs.jboss.org/jbossas/javadoc/4.0.5/connector'
)
source subprojects.collect { project ->
project.sourceSets.main.allJava
}
destinationDir = new File(buildDir, "api")
classpath = files(subprojects.collect { project ->
project.sourceSets.main.compileClasspath
})
maxMemory = '1024m'
}
task wrapper(type: Wrapper) {
description = 'Generates gradlew[.bat] scripts'
gradleVersion = '2.2.1'
}
}

View File

@@ -0,0 +1,14 @@
<html>
<body>
This document is the API specification for the Spring Statemachine project.
<hr/>
<div id="overviewBody">
<p>
For further API reference and developer documentation, see the
<a href="http://projects.spring.io/spring-statemachine/" target="_top">Spring Statemachine Project Page</a>.
There you can find the latest news, links to documentation, books, presentations and webinars.
</p>
</div>
</body>
</html>

599
docs/src/api/stylesheet.css Normal file
View File

@@ -0,0 +1,599 @@
/* Javadoc style sheet */
/*
Overall document style
*/
@import url('resources/fonts/dejavu.css');
body {
background-color:#ffffff;
color:#353833;
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
font-size:14px;
margin:0;
}
a:link, a:visited {
text-decoration:none;
color:#4A6782;
}
a:hover, a:focus {
text-decoration:none;
color:#bb7a2a;
}
a:active {
text-decoration:none;
color:#4A6782;
}
a[name] {
color:#353833;
}
a[name]:hover {
text-decoration:none;
color:#353833;
}
pre {
font-family:'DejaVu Sans Mono', monospace;
font-size:14px;
}
h1 {
font-size:20px;
}
h2 {
font-size:18px;
}
h3 {
font-size:16px;
font-style:italic;
}
h4 {
font-size:13px;
}
h5 {
font-size:12px;
}
h6 {
font-size:11px;
}
ul {
list-style-type:disc;
}
code, tt {
font-family:'DejaVu Sans Mono', monospace;
font-size:14px;
padding-top:4px;
margin-top:8px;
line-height:1.4em;
}
dt code {
font-family:'DejaVu Sans Mono', monospace;
font-size:14px;
padding-top:4px;
}
table tr td dt code {
font-family:'DejaVu Sans Mono', monospace;
font-size:14px;
vertical-align:top;
padding-top:4px;
}
sup {
font-size:8px;
}
/*
Document title and Copyright styles
*/
.clear {
clear:both;
height:0px;
overflow:hidden;
}
.aboutLanguage {
float:right;
padding:0px 21px;
font-size:11px;
z-index:200;
margin-top:-9px;
}
.legalCopy {
margin-left:.5em;
}
.bar a, .bar a:link, .bar a:visited, .bar a:active {
color:#FFFFFF;
text-decoration:none;
}
.bar a:hover, .bar a:focus {
color:#bb7a2a;
}
.tab {
background-color:#0066FF;
color:#ffffff;
padding:8px;
width:5em;
font-weight:bold;
}
/*
Navigation bar styles
*/
.bar {
background-color:#4D7A97;
color:#FFFFFF;
padding:.8em .5em .4em .8em;
height:auto;/*height:1.8em;*/
font-size:11px;
margin:0;
}
.topNav {
background-color:#4D7A97;
color:#FFFFFF;
float:left;
padding:0;
width:100%;
clear:right;
height:2.8em;
padding-top:10px;
overflow:hidden;
font-size:12px;
}
.bottomNav {
margin-top:10px;
background-color:#4D7A97;
color:#FFFFFF;
float:left;
padding:0;
width:100%;
clear:right;
height:2.8em;
padding-top:10px;
overflow:hidden;
font-size:12px;
}
.subNav {
background-color:#dee3e9;
float:left;
width:100%;
overflow:hidden;
font-size:12px;
}
.subNav div {
clear:left;
float:left;
padding:0 0 5px 6px;
text-transform:uppercase;
}
ul.navList, ul.subNavList {
float:left;
margin:0 25px 0 0;
padding:0;
}
ul.navList li{
list-style:none;
float:left;
padding: 5px 6px;
text-transform:uppercase;
}
ul.subNavList li{
list-style:none;
float:left;
}
.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {
color:#FFFFFF;
text-decoration:none;
text-transform:uppercase;
}
.topNav a:hover, .bottomNav a:hover {
text-decoration:none;
color:#bb7a2a;
text-transform:uppercase;
}
.navBarCell1Rev {
background-color:#F8981D;
color:#253441;
margin: auto 5px;
}
.skipNav {
position:absolute;
top:auto;
left:-9999px;
overflow:hidden;
}
/*
Page header and footer styles
*/
.header, .footer {
clear:both;
margin:0 20px;
padding:5px 0 0 0;
}
.indexHeader {
margin:10px;
position:relative;
}
.indexHeader span{
margin-right:15px;
}
.indexHeader h1 {
font-size:13px;
}
.title {
color:#2c4557;
margin:10px 0;
}
.subTitle {
margin:5px 0 0 0;
}
.header ul {
margin:0 0 15px 0;
padding:0;
}
.footer ul {
margin:20px 0 5px 0;
}
.header ul li, .footer ul li {
list-style:none;
font-size:13px;
}
/*
Heading styles
*/
div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
background-color:#dee3e9;
border:1px solid #d0d9e0;
margin:0 0 6px -8px;
padding:7px 5px;
}
ul.blockList ul.blockList ul.blockList li.blockList h3 {
background-color:#dee3e9;
border:1px solid #d0d9e0;
margin:0 0 6px -8px;
padding:7px 5px;
}
ul.blockList ul.blockList li.blockList h3 {
padding:0;
margin:15px 0;
}
ul.blockList li.blockList h2 {
padding:0px 0 20px 0;
}
/*
Page layout container styles
*/
.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {
clear:both;
padding:10px 20px;
position:relative;
}
.indexContainer {
margin:10px;
position:relative;
font-size:12px;
}
.indexContainer h2 {
font-size:13px;
padding:0 0 3px 0;
}
.indexContainer ul {
margin:0;
padding:0;
}
.indexContainer ul li {
list-style:none;
padding-top:2px;
}
.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {
font-size:12px;
font-weight:bold;
margin:10px 0 0 0;
color:#4E4E4E;
}
.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {
margin:5px 0 10px 0px;
font-size:14px;
font-family:'DejaVu Sans Mono',monospace;
}
.serializedFormContainer dl.nameValue dt {
margin-left:1px;
font-size:1.1em;
display:inline;
font-weight:bold;
}
.serializedFormContainer dl.nameValue dd {
margin:0 0 0 1px;
font-size:1.1em;
display:inline;
}
/*
List styles
*/
ul.horizontal li {
display:inline;
font-size:0.9em;
}
ul.inheritance {
margin:0;
padding:0;
}
ul.inheritance li {
display:inline;
list-style:none;
}
ul.inheritance li ul.inheritance {
margin-left:15px;
padding-left:15px;
padding-top:1px;
}
ul.blockList, ul.blockListLast {
margin:10px 0 10px 0;
padding:0;
}
ul.blockList li.blockList, ul.blockListLast li.blockList {
list-style:none;
margin-bottom:15px;
line-height:1.4;
}
ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {
padding:0px 20px 5px 10px;
border:1px solid #ededed;
background-color:#f8f8f8;
}
ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {
padding:0 0 5px 8px;
background-color:#ffffff;
border:none;
}
ul.blockList ul.blockList ul.blockList ul.blockList li.blockList {
margin-left:0;
padding-left:0;
padding-bottom:15px;
border:none;
}
ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
list-style:none;
border-bottom:none;
padding-bottom:0;
}
table tr td dl, table tr td dl dt, table tr td dl dd {
margin-top:0;
margin-bottom:1px;
}
/*
Table styles
*/
.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary {
width:100%;
border-left:1px solid #EEE;
border-right:1px solid #EEE;
border-bottom:1px solid #EEE;
}
.overviewSummary, .memberSummary {
padding:0px;
}
.overviewSummary caption, .memberSummary caption, .typeSummary caption,
.useSummary caption, .constantsSummary caption, .deprecatedSummary caption {
position:relative;
text-align:left;
background-repeat:no-repeat;
color:#253441;
font-weight:bold;
clear:none;
overflow:hidden;
padding:0px;
padding-top:10px;
padding-left:1px;
margin:0px;
white-space:pre;
}
.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link,
.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link,
.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover,
.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover,
.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active,
.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active,
.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited,
.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited {
color:#FFFFFF;
}
.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span,
.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span {
white-space:nowrap;
padding-top:5px;
padding-left:12px;
padding-right:12px;
padding-bottom:7px;
display:inline-block;
float:left;
background-color:#F8981D;
border: none;
height:16px;
}
.memberSummary caption span.activeTableTab span {
white-space:nowrap;
padding-top:5px;
padding-left:12px;
padding-right:12px;
margin-right:3px;
display:inline-block;
float:left;
background-color:#F8981D;
height:16px;
}
.memberSummary caption span.tableTab span {
white-space:nowrap;
padding-top:5px;
padding-left:12px;
padding-right:12px;
margin-right:3px;
display:inline-block;
float:left;
background-color:#4D7A97;
height:16px;
}
.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab {
padding-top:0px;
padding-left:0px;
padding-right:0px;
background-image:none;
float:none;
display:inline;
}
.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd,
.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd {
display:none;
width:5px;
position:relative;
float:left;
background-color:#F8981D;
}
.memberSummary .activeTableTab .tabEnd {
display:none;
width:5px;
margin-right:3px;
position:relative;
float:left;
background-color:#F8981D;
}
.memberSummary .tableTab .tabEnd {
display:none;
width:5px;
margin-right:3px;
position:relative;
background-color:#4D7A97;
float:left;
}
.overviewSummary td, .memberSummary td, .typeSummary td,
.useSummary td, .constantsSummary td, .deprecatedSummary td {
text-align:left;
padding:0px 0px 12px 10px;
width:100%;
}
th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th,
td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{
vertical-align:top;
padding-right:0px;
padding-top:8px;
padding-bottom:3px;
}
th.colFirst, th.colLast, th.colOne, .constantsSummary th {
background:#dee3e9;
text-align:left;
padding:8px 3px 3px 7px;
}
td.colFirst, th.colFirst {
white-space:nowrap;
font-size:13px;
}
td.colLast, th.colLast {
font-size:13px;
}
td.colOne, th.colOne {
font-size:13px;
}
.overviewSummary td.colFirst, .overviewSummary th.colFirst,
.overviewSummary td.colOne, .overviewSummary th.colOne,
.memberSummary td.colFirst, .memberSummary th.colFirst,
.memberSummary td.colOne, .memberSummary th.colOne,
.typeSummary td.colFirst{
width:25%;
vertical-align:top;
}
td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {
font-weight:bold;
}
.tableSubHeadingColor {
background-color:#EEEEFF;
}
.altColor {
background-color:#FFFFFF;
}
.rowColor {
background-color:#EEEEEF;
}
/*
Content styles
*/
.description pre {
margin-top:0;
}
.deprecatedContent {
margin:0;
padding:10px 0;
}
.docSummary {
padding:0;
}
ul.blockList ul.blockList ul.blockList li.blockList h3 {
font-style:normal;
}
div.block {
font-size:14px;
font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
}
td.colLast div {
padding-top:0px;
}
td.colLast a {
padding-bottom:3px;
}
/*
Formatting effect styles
*/
.sourceLineNo {
color:green;
padding:0 30px 0 0;
}
h1.hidden {
visibility:hidden;
overflow:hidden;
font-size:10px;
}
.block {
display:block;
margin:3px 10px 2px 0px;
color:#474747;
}
.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink,
.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel,
.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink {
font-weight:bold;
}
.deprecationComment, .emphasizedPhrase, .interfaceName {
font-style:italic;
}
div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase,
div.block div.block span.interfaceName {
font-style:normal;
}
div.contentContainer ul.blockList li.blockList h2{
padding-bottom:0px;
}
/*
Spring
*/
pre.code {
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px 3px 3px 3px;
overflow: auto;
padding: 10px;
margin: 4px 20px 2px 0px;
}
pre.code code, pre.code code * {
font-size: 1em;
}
pre.code code, pre.code code * {
padding: 0 !important;
margin: 0 !important;
}

View File

@@ -0,0 +1,4 @@
SPRING STATEMACHINE CHANGELOG
==================================
http://projects.spring.io/spring-statemachine/

201
docs/src/info/license.txt Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

21
docs/src/info/notice.txt Normal file
View File

@@ -0,0 +1,21 @@
======================================================================
== NOTICE file corresponding to section 4 d of the Apache License, ==
== Version 2.0, for the Spring Framework distribution. ==
======================================================================
This product includes software developed by
the Apache Software Foundation (http://www.apache.org).
The end-user documentation included with a redistribution, if any,
must include the following acknowledgement:
"This product includes software developed by the Spring Framework
Project (http://www.springframework.org)."
Alternately, this acknowledgement may appear in the software itself,
if and wherever such third-party acknowledgements normally appear.
The names "Spring", "Spring Framework" and "Spring for Apache Hadoop"
must not be used to endorse or promote products derived from this
software without prior written permission. For written permission,
please contact enquiries@springsource.com.

24
docs/src/info/readme.txt Normal file
View File

@@ -0,0 +1,24 @@
SPRING STATEMACHINE
------------------------
http://projects.spring.io/spring-statemachine/
1. INTRODUCTION
Spring Statemachine is a framework extension introducing state machine
concepts in a spring world.
2. RELEASE NOTES
This release comes with complete reference documentation. For further
details, consult the provided javadoc for specific packages and classes.
3. DISTRIBUTION JAR FILES
The Spring Statemachine jars files can be found in the 'dist' directory.
4. GETTING STARTED
Please see the reference documentation.
Additionally the blog at http://blog.spring.io as well
as sections of interest in the reference documentation.

View File

@@ -0,0 +1,13 @@
require 'asciidoctor'
require 'erb'
guard 'shell' do
watch(/^.*\.adoc$/) {|m|
Asciidoctor.render_file(m[0], :to_dir => "build/", :safe => Asciidoctor::SafeMode::UNSAFE, :attributes=> {'idprefix' => '', 'idseparator' => '-', 'copycss' => '', 'icons' => 'font', 'source-highlighter' => 'prettify', 'sectanchors' => '', 'doctype' => 'book','toc2' => '', 'spring-hadoop-version' => '2.1.0.BUILD-SNAPSHOT','spring-version' => '4.1.3.RELEASE', 'revnumber' => '2.1.0.BUILD-SNAPSHOT', 'numbered'=>'' })
}
end
guard 'livereload' do
watch(%r{build/.+\.(css|js|html)$})
end

View File

@@ -0,0 +1,16 @@
[[appendices]]
= Appendices
:numbered!:
[appendix]
== State Machine Concepts
This appendix provides generic information about state machines.
=== Glossary
state machine::
machine of sort.
state::
a stage in a state machine.

View File

@@ -0,0 +1,22 @@
<releaseinfo>{revnumber}</releaseinfo>
<productname>Spring Statemachine</productname>
<authorgroup>
<author>
<firstname>Janne</firstname>
<surname>Valkealahti</surname>
<affiliation>Pivotal</affiliation>
</author>
</authorgroup>
<copyright>
<year>2015</year>
<holder>Pivotal Software, Inc.</holder>
</copyright>
<legalnotice>
<para>Copies of this document may be made for your own use and for
distribution to others, provided that you do not charge any fee for such
copies and further provided that each copy contains this Copyright
Notice, whether distributed in print or electronically.</para>
</legalnotice>

View File

@@ -0,0 +1,37 @@
:hadoop-Configuration: http://hadoop.apache.org/docs/r{hadoop-version}/api/org/apache/hadoop/conf/Configuration.html
:hadoop-FileSystem: http://hadoop.apache.org/docs/r{hadoop-version}/api/org/apache/hadoop/fs/FileSystem.html
:hadoop-Security: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-project-dist/hadoop-common/SecureMode.html
:hadoop-Streaming: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-mapreduce-client/hadoop-mapreduce-client-core/HadoopStreaming.html
:hadoop-DistributedCache: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-mapreduce-client/hadoop-mapreduce-client-core/DistributedCacheDeploy.html
:hadoop-WordCount: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html#Example:_WordCount_v1.0
:hadoop-FileSystemShell: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-project-dist/hadoop-common/FileSystemShell.html
:hadoop-WebHdfs: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-project-dist/hadoop-hdfs/WebHDFS.html
:hadoop-Hftp: http://hadoop.apache.org/docs/r{hadoop-version}/hadoop-project-dist/hadoop-hdfs/Hftp.html
:java-ClassLoader: http://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html
:core-ApplicationContext: http://docs.spring.io/spring/docs/{spring-version}/javadoc-api/org/springframework/context/ApplicationContext.html
:core-beans-factory-placeholderconfigurer: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/beans.html#beans-factory-placeholderconfigurer
:core-beans-environment: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/beans.html#beans-environment
:core-ResourcePatternResolver: http://docs.spring.io/spring/docs/{spring-version}/javadoc-api/org/springframework/core/io/support/ResourcePatternResolver.html
:core-ref-util: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/xsd-config.html#xsd-config-body-schemas-util-properties
:core-aop-schema-advisors: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/aop.html#aop-schema-advisors
:core-dao: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/dao.html
:core-dao-exceptions: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/dao.html#dao-exceptions
:core-jdbc: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/jdbc.html
:core-jdbc-JdbcTemplate: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/jdbc.html#jdbc-JdbcTemplate
:shdp-FsShell: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/fs/FsShell.html
:shdp-DistCp: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/fs/DistCp.html
:shdp-HdfsResourceLoader: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/fs/HdfsResourceLoader.html
:shdp-SpringDataStoreTextWriterConfigurerAdapter: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/store/config/annotation/SpringDataStoreTextWriterConfigurerAdapter.html
:shdp-EnableDataStoreTextWriter: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/store/config/annotation/EnableDataStoreTextWriter.html
:shdp-DataStoreTextWriterConfigurer: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/store/config/annotation/builders/DataStoreTextWriterConfigurer.html
:shdp-DataStoreWriter: http://docs.spring.io/spring-hadoop/docs/{spring-hadoop-version}/api/org/springframework/data/hadoop/store/DataStoreWriter.html
:spring-data-hadoop-boot-jar: spring-data-hadoop-boot-{spring-hadoop-version}.jar
= Spring Statemachine - Reference Documentation
include::preface.adoc[]
include::introduction.adoc[]
[[springandhadoop]]
include::sm.adoc[]
include::appendix.adoc[]

View File

@@ -0,0 +1,5 @@
[[introduction]]
= Introduction
== Requirements
TBD

View File

@@ -0,0 +1,4 @@
[preface]
== Preface
Spring Statemachine is a framework for application developers to use
state machine concepts with Spring.

View File

@@ -0,0 +1,5 @@
[[statemachine]]
= State Machine
TBD

4
gradle.properties Normal file
View File

@@ -0,0 +1,4 @@
version=1.0.0.BUILD-SNAPSHOT
springVersion = 4.1.4.RELEASE
hamcrestVersion = 1.3
junitVersion = 4.11

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Sat Jan 31 16:49:22 GMT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip

164
gradlew vendored Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

60
publish-maven.gradle Normal file
View File

@@ -0,0 +1,60 @@
apply plugin: 'maven'
ext.optionalDeps = []
ext.providedDeps = []
ext.optional = { optionalDeps << it }
ext.provided = { providedDeps << it }
install {
repositories.mavenInstaller {
customizePom(pom, project)
}
}
def customizePom(pom, gradleProject) {
pom.whenConfigured { generatedPom ->
// respect 'optional' and 'provided' dependencies
gradleProject.optionalDeps.each { dep ->
generatedPom.dependencies.findAll { it.artifactId == dep.name }*.optional = true
}
gradleProject.providedDeps.each { dep ->
generatedPom.dependencies.findAll { it.artifactId == dep.name }*.scope = 'provided'
}
// eliminate test-scoped dependencies (no need in maven central poms)
generatedPom.dependencies.removeAll { dep ->
dep.scope == 'test'
}
// add all items necessary for maven central publication
generatedPom.project {
name = gradleProject.description
description = gradleProject.description
url = 'https://github.com/spring-projects/spring-statemachine'
organization {
name = 'SpringSource'
url = 'http://spring.io/spring-statemachine'
}
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
scm {
url = 'https://github.com/spring-projects/spring-statemachine'
connection = 'scm:git:git://github.com/spring-projects/spring-statemachine'
developerConnection = 'scm:git:git://github.com/spring-projects/spring-statemachine'
}
developers {
developer {
id = 'jvalkeal'
name = 'Janne Valkealahti'
email = 'janne.valkealahti@gmail.com'
}
}
}
}
}

3
settings.gradle Normal file
View File

@@ -0,0 +1,3 @@
rootProject.name = 'spring-statemachine'
include 'spring-statemachine-core'

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2015 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.statemachine;
import java.util.Collection;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.AbstractStateMachine;
import org.springframework.statemachine.transition.Transition;
/**
* Specialisation of a {@link StateMachine} using enums
* as its {@link State} and event types.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class EnumStateMachine<S extends Enum<S>, E extends Enum<E>> extends AbstractStateMachine<S, E> {
/**
* Instantiates a new enum state machine.
*
* @param states the states
* @param transitions the transitions
* @param initialState the initial state
*/
public EnumStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState) {
super(states, transitions, initialState);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2015 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.statemachine;
import java.util.Map;
/**
* Extended states are used to supplement state machine with a variables. If
* extended state is used a complete condition of a state machine is a
* combination of its state an extended state variables.
*
* @author Janne Valkealahti
*
*/
public interface ExtendedState {
/**
* Gets the extended state variables.
*
* @return the extended state variables
*/
Map<String, Object> getVariables();
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2015 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.statemachine;
import org.springframework.messaging.MessageHeaders;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.transition.Transition;
/**
* {@code StateContext} is representing a current context used in
* {@link Transition}s, {@link Action}s and {@link Guard}s order to get access
* to event headers and {@link ExtendedState}.
*
* @author Janne Valkealahti
*
*/
public interface StateContext {
/**
* Gets the event message headers.
*
* @return the event message headers
*/
MessageHeaders getMessageHeaders();
/**
* Gets the state machine extended state.
*
* @return the state machine extended state
*/
ExtendedState getExtendedState();
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2015 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.statemachine;
import org.springframework.messaging.Message;
import org.springframework.statemachine.listener.StateMachineListener;
/**
* {@code StateMachine} provides an APIs for generic finite state machine needed
* for basic operations like working with states, events and a lifecycle.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface StateMachine<S, E> {
/**
* Gets the initial state {@code S}.
*
* @return initial state
*/
S getInitialState();
/**
* Gets the current state {@code S}.
*
* @return current state
*/
S getState();
/**
* Start the state machine.
*/
void start();
/**
* Send an event {@code E} wrapped with a {@link Message} to the state
* machine.
*
* @param event the wrapped event to send
*/
void sendEvent(Message<E> event);
/**
* Send an event {@code E} to the state machine.
*
* @param event the event to send
*/
void sendEvent(E event);
/**
* Adds the state listener.
*
* @param listener the listener
*/
void addStateListener(StateMachineListener<S, E> listener);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2015 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.statemachine;
/**
* Various constants used in state machine lib.
*
* @author Janne Valkealahti
*
*/
public abstract class StateMachineSystemConstants {
/** Default bean id for state machine. */
public static final String DEFAULT_ID_STATEMACHINE = "stateMachine";
/** Default bean id for state machine factory. */
public static final String DEFAULT_ID_STATEMACHINEFACTORY = "stateMachineFactory";
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 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.statemachine.action;
import org.springframework.statemachine.StateContext;
/**
* Generic strategy interface used by a state machine to respond
* events by executing an {@code Action} with a {@link StateContext}.
*
* @author Janne Valkealahti
*
*/
public interface Action {
/**
* Execute action with a {@link StateContext}.
*
* @param context the state context
*/
void execute(StateContext context);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2015 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.statemachine.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface OnTransition {
String source() default "";
String target() default "";
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 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.statemachine.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Component
public @interface WithStateMachine {
String name() default "";
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2015 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.statemachine.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.statemachine.StateMachineSystemConstants;
import org.springframework.statemachine.config.common.annotation.EnableAnnotationConfiguration;
import org.springframework.statemachine.config.common.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.statemachine.config.configuration.StateMachineConfiguration;
/**
* Example annotation which imports @{@link Configuration}s.
*
* @author Janne Valkealahti
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@EnableAnnotationConfiguration
@Import({StateMachineConfiguration.class,ObjectPostProcessorConfiguration.class})
public @interface EnableStateMachine {
/**
* The name of bean, or if plural, aliases for bean created based on this
* annotation. If left unspecified bean name will be autogenerated.
*
* @see Bean#name()
* @return the array if names or empty as default
*/
String[] name() default {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE};
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2015 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.statemachine.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.statemachine.StateMachineSystemConstants;
import org.springframework.statemachine.config.common.annotation.EnableAnnotationConfiguration;
import org.springframework.statemachine.config.common.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.statemachine.config.configuration.StateMachineFactoryConfiguration;
/**
* Example annotation which imports @{@link Configuration}s.
*
* @author Janne Valkealahti
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@EnableAnnotationConfiguration
@Import({StateMachineFactoryConfiguration.class,ObjectPostProcessorConfiguration.class})
public @interface EnableStateMachineFactory {
/**
* The name of bean, or if plural, aliases for bean created based on this
* annotation. If left unspecified bean name will be autogenerated.
*
* @see Bean#name()
* @return the array if names or empty as default
*/
String[] name() default {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY};
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2015 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.statemachine.config;
public class EnumStateMachineConfigurerAdapter<S extends Enum<S>, E extends Enum<E>> extends StateMachineConfigurerAdapter<S, E> {
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2015 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.statemachine.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.statemachine.EnumStateMachine;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
import org.springframework.statemachine.state.EnumState;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.LifecycleObjectSupport;
import org.springframework.statemachine.transition.DefaultExternalTransition;
import org.springframework.statemachine.transition.DefaultInternalTransition;
import org.springframework.statemachine.transition.Transition;
import org.springframework.statemachine.transition.TransitionKind;
/**
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> extends LifecycleObjectSupport implements
StateMachineFactory<State<S, E>, E> {
private final StateMachineTransitions<S, E> stateMachineTransitions;
private final StateMachineStates<S, E> stateMachineStates;
/**
* Instantiates a new enum state machine factory.
*
* @param stateMachineTransitions the state machine transitions
* @param stateMachineStates the state machine states
*/
public EnumStateMachineFactory(StateMachineTransitions<S, E> stateMachineTransitions,
StateMachineStates<S, E> stateMachineStates) {
this.stateMachineTransitions = stateMachineTransitions;
this.stateMachineStates = stateMachineStates;
}
@Override
public StateMachine<State<S, E>, E> getStateMachine() {
return stateMachine();
}
public StateMachine<State<S, E>, E> stateMachine() {
Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
for (StateData<S, E> stateData : stateMachineStates.getStates()) {
stateMap.put(stateData.getState(), new EnumState<S, E>(stateData.getState(), stateData.getDeferred(),
stateData.getEntryActions(), stateData.getExitActions()));
}
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
for (TransitionData<S, E> transitionData : stateMachineTransitions.getTransitions()) {
S source = transitionData.getSource();
S target = transitionData.getTarget();
E event = transitionData.getEvent();
if (transitionData.getKind() == TransitionKind.EXTERNAL) {
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard());
transitions.add(transition);
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
transitionData.getActions(), event, transitionData.getGuard());
transitions.add(transition);
}
}
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(stateMap.values(), transitions,
stateMap.get(stateMachineStates.getInitialState()));
machine.afterPropertiesSet();
if (getBeanFactory() != null) {
machine.setBeanFactory(getBeanFactory());
}
machine.setAutoStartup(isAutoStartup());
machine.start();
return machine;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2015 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.statemachine.config;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
public class StateMachineConfig<S, E> {
public final StateMachineTransitions<S, E> transitions;
public final StateMachineStates<S, E> states;
public StateMachineConfig(StateMachineTransitions<S, E> transitions, StateMachineStates<S, E> states) {
this.transitions = transitions;
this.states = states;
}
public StateMachineTransitions<S, E> getTransitions() {
return transitions;
}
public StateMachineStates<S, E> getStates() {
return states;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2015 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.statemachine.config;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
public class StateMachineConfigurerAdapter<S, E> implements StateMachineConfigurer<S, E> {
private StateMachineTransitionBuilder<S, E> transitionBuilder;
private StateMachineStateBuilder<S, E> stateBuilder;
@Override
public final void init(StateMachineConfigBuilder<S, E> config) throws Exception {
config.setSharedObject(StateMachineTransitionBuilder.class, getStateMachineTransitionBuilder());
config.setSharedObject(StateMachineStateBuilder.class, getStateMachineStateBuilder());
}
@Override
public void configure(StateMachineConfigBuilder<S, E> config) throws Exception {
}
@Override
public void configure(StateMachineStateConfigurer<S, E> states) throws Exception {
}
@Override
public void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception {
}
@Override
public boolean isAssignable(AnnotationBuilder<StateMachineConfig<S, E>> builder) {
return builder instanceof StateMachineConfigBuilder;
}
protected final StateMachineTransitionBuilder<S, E> getStateMachineTransitionBuilder() throws Exception {
if (transitionBuilder != null) {
return transitionBuilder;
}
transitionBuilder = new StateMachineTransitionBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
configure(transitionBuilder);
return transitionBuilder;
}
protected final StateMachineStateBuilder<S, E> getStateMachineStateBuilder() throws Exception {
if (stateBuilder != null) {
return stateBuilder;
}
stateBuilder = new StateMachineStateBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
configure(stateBuilder);
return stateBuilder;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2015 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.statemachine.config;
import org.springframework.statemachine.StateMachine;
public interface StateMachineFactory<S, E> {
StateMachine<S, E> getStateMachine();
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
public class StateMachineConfigBuilder<S, E>
extends
AbstractConfiguredAnnotationBuilder<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>, StateMachineConfigBuilder<S, E>> {
@SuppressWarnings("unchecked")
@Override
protected StateMachineConfig<S, E> performBuild() throws Exception {
StateMachineTransitionBuilder<?, ?> sharedObject = getSharedObject(StateMachineTransitionBuilder.class);
StateMachineStateBuilder<?, ?> sharedObject2 = getSharedObject(StateMachineStateBuilder.class);
StateMachineStates<S, E> states = (StateMachineStates<S, E>) sharedObject2.build();
StateMachineTransitions<S, E> transitions = (StateMachineTransitions<S, E>) sharedObject.build();
StateMachineConfig<S, E> bean = new StateMachineConfig<S, E>(transitions, states);
return bean;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
public interface StateMachineConfigurer<S, E> extends
AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> {
void configure(StateMachineStateConfigurer<S, E> transitions) throws Exception;
void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception;
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
import org.springframework.statemachine.config.configurers.DefaultStateConfigurer;
import org.springframework.statemachine.config.configurers.StateConfigurer;
public class StateMachineStateBuilder<S, E>
extends AbstractConfiguredAnnotationBuilder<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
implements StateMachineStateConfigurer<S, E> {
private Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
private S initialState;
public StateMachineStateBuilder() {
super();
}
public StateMachineStateBuilder(ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
super(objectPostProcessor, allowConfigurersOfSameType);
}
public StateMachineStateBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor);
}
@Override
protected StateMachineStates<S, E> performBuild() throws Exception {
StateMachineStates<S, E> bean = new StateMachineStates<S, E>(initialState, states);
return bean;
}
@Override
public StateConfigurer<S, E> withStates() throws Exception {
return apply(new DefaultStateConfigurer<S, E>());
}
public void add(Collection<StateData<S, E>> states) {
this.states.addAll(states);
}
public void setInitialState(S state) {
this.initialState = state;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import org.springframework.statemachine.config.configurers.StateConfigurer;
public interface StateMachineStateConfigurer<S, E> {
StateConfigurer<S, E> withStates() throws Exception;
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
public class StateMachineStates<S, E> {
private Collection<StateData<S, E>> states;
private final S initialState;
public StateMachineStates(S initialState, Collection<StateData<S, E>> states) {
this.states = states;
this.initialState = initialState;
}
public Collection<StateData<S, E>> getStates() {
return states;
}
public S getInitialState() {
return initialState;
}
public static class StateData<S, E> {
private S state;
private Collection<E> deferred;
private Collection<Action> entryActions;
private Collection<Action> exitActions;
public StateData(S state, Collection<E> deferred) {
this(state, deferred, null, null);
}
public StateData(S state, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
this.state = state;
this.deferred = deferred;
this.entryActions = entryActions;
this.exitActions = exitActions;
}
public StateData(S state, E[] deferred) {
this(state, deferred != null ? Arrays.asList(deferred) : new ArrayList<E>());
}
public S getState() {
return state;
}
public Collection<E> getDeferred() {
return deferred;
}
public Collection<Action> getEntryActions() {
return entryActions;
}
public Collection<Action> getExitActions() {
return exitActions;
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
import org.springframework.statemachine.config.configurers.DefaultExternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.DefaultInternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.transition.TransitionKind;
public class StateMachineTransitionBuilder<S, E>
extends
AbstractConfiguredAnnotationBuilder<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
implements StateMachineTransitionConfigurer<S, E> {
private Collection<TransitionData<S, E>> transitionData = new ArrayList<TransitionData<S, E>>();
public StateMachineTransitionBuilder() {
super();
}
public StateMachineTransitionBuilder(ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
super(objectPostProcessor, allowConfigurersOfSameType);
}
public StateMachineTransitionBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor);
}
@Override
protected StateMachineTransitions<S, E> performBuild() throws Exception {
StateMachineTransitions<S, E> bean = new StateMachineTransitions<S, E>(transitionData);
return bean;
}
@Override
public ExternalTransitionConfigurer<S, E> withExternal() throws Exception {
return apply(new DefaultExternalTransitionConfigurer<S, E>());
}
@Override
public InternalTransitionConfigurer<S, E> withInternal() throws Exception {
return apply(new DefaultInternalTransitionConfigurer<S, E>());
}
public void add(S source, S target, E event, Collection<Action> actions, Guard guard, TransitionKind kind) {
transitionData.add(new TransitionData<S, E>(source, target, event, actions, guard, kind));
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
public interface StateMachineTransitionConfigurer<S, E> {
ExternalTransitionConfigurer<S, E> withExternal() throws Exception;
InternalTransitionConfigurer<S, E> withInternal() throws Exception;
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2015 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.statemachine.config.builders;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.transition.TransitionKind;
public class StateMachineTransitions<S, E> {
private Collection<TransitionData<S, E>> transitions;
public StateMachineTransitions(Collection<TransitionData<S, E>> transitions) {
this.transitions = transitions;
}
public Collection<TransitionData<S, E>> getTransitions() {
return transitions;
}
public static class TransitionData<S, E> {
S source;
S target;
E event;
Collection<Action> actions;
Guard guard;
TransitionKind kind;
public TransitionData(S source, S target, E event, Collection<Action> actions, Guard guard, TransitionKind kind) {
this.source = source;
this.target = target;
this.event = event;
this.actions = actions;
this.guard = guard;
this.kind = kind;
}
public S getSource() {
return source;
}
public S getTarget() {
return target;
}
public E getEvent() {
return event;
}
public Collection<Action> getActions() {
return actions;
}
public Guard getGuard() {
return guard;
}
public TransitionKind getKind() {
return kind;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A base {@link AnnotationBuilder} that ensures the object being built is only
* built one time.
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @param <O> the type of Object that is being built
*/
public abstract class AbstractAnnotationBuilder<O> implements AnnotationBuilder<O> {
/** Flag tracking build */
private AtomicBoolean building = new AtomicBoolean();
/** Built object is stored here */
private O object;
@Override
public final O build() throws Exception {
if(building.compareAndSet(false, true)) {
object = doBuild();
return object;
}
throw new IllegalStateException("This object has already been built");
}
/**
* Gets the object that was built. If it has not been built yet an Exception
* is thrown.
*
* @return the Object that was built
*/
public final O getObject() {
if(!building.get()) {
throw new IllegalStateException("This object has not been built");
}
return object;
}
/**
* Subclasses should implement this to perform the build.
*
* @return the object that should be returned by {@link #build()}.
* @throws Exception if an error occurs
*/
protected abstract O doBuild() throws Exception;
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
/**
* Base implementation of @{@link Configuration} class.
*
* @author Janne Valkealahti
*
* @param <O> The object that used builder returns
* @param <B> The type of the builder
*/
public abstract class AbstractAnnotationConfiguration<B extends AnnotationBuilder<O>, O>
implements ImportAware, BeanClassLoaderAware {
private List<AnnotationConfigurer<O,B>> configurers;
private ClassLoader beanClassLoader;
private AnnotationAttributes annotationAttributes;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
beanClassLoader = classLoader;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableConfigurationAttrMap =
importMetadata.getAnnotationAttributes(EnableAnnotationConfiguration.class.getName());
AnnotationAttributes enableConfigurationAttrs = AnnotationAttributes.fromMap(enableConfigurationAttrMap);
if(enableConfigurationAttrs == null) {
// search parent classes
Class<?> currentClass = ClassUtils.resolveClassName(importMetadata.getClassName(), beanClassLoader);
for(Class<?> classToInspect = currentClass ;classToInspect != null; classToInspect = classToInspect.getSuperclass()) {
EnableAnnotationConfiguration enableConfigurationAnnotation =
AnnotationUtils.findAnnotation(classToInspect, EnableAnnotationConfiguration.class);
if(enableConfigurationAnnotation == null) {
continue;
}
enableConfigurationAttrMap = AnnotationUtils
.getAnnotationAttributes(enableConfigurationAnnotation);
enableConfigurationAttrs = AnnotationAttributes.fromMap(enableConfigurationAttrMap);
}
}
annotationAttributes = enableConfigurationAttrs;
}
/**
* Sets the configurers.
*
* @param configurers the configurers
* @throws Exception the exception
*/
@Autowired(required=false)
public void setConfigurers(List<AnnotationConfigurer<O, B>> configurers) throws Exception {
this.configurers = configurers;
onConfigurers(configurers);
}
public AnnotationAttributes getAnnotationAttributes() {
return annotationAttributes;
}
public List<AnnotationConfigurer<O, B>> getConfigurers() {
return configurers;
}
protected abstract void onConfigurers(List<AnnotationConfigurer<O, B>> configurers) throws Exception;
}

View File

@@ -0,0 +1,531 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
/**
* A base {@link AnnotationBuilder} that allows {@link AnnotationConfigurer}s to be
* applied to it. This makes modifying the {@link AnnotationBuilder} a strategy
* that can be customised and broken up into a number of
* {@link AnnotationConfigurer} objects that have more specific goals than that
* of the {@link AnnotationBuilder}.
* <p>
*
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @param <O> The object that this builder returns
* @param <I> The interface of type B
* @param <B> The type of this builder (that is returned by the base class)
*/
public abstract class AbstractConfiguredAnnotationBuilder<O,I,B extends AnnotationBuilder<O>>
extends AbstractAnnotationBuilder<O> {
private final static Log log = LogFactory.getLog(AbstractConfiguredAnnotationBuilder.class);
/** Configurers which are added to this builder before the configure step */
private final LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>> mainConfigurers =
new LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>>();
/** Configurers which are added to this builder during the configuration phase */
private final LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>> postConfigurers =
new LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>>();
private final Map<Class<Object>, Object> sharedObjects = new HashMap<Class<Object>, Object>();
private final boolean allowConfigurersOfSameType;
/** Current state of this builder */
private BuildState buildState = BuildState.UNBUILT;
private ObjectPostProcessor<Object> objectPostProcessor;
/**
* Instantiates a new annotation builder.
*/
protected AbstractConfiguredAnnotationBuilder() {
this(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR);
}
/**
* Instantiates a new annotation builder.
*
* @param objectPostProcessor the object post processor
*/
protected AbstractConfiguredAnnotationBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
this(objectPostProcessor,false);
}
/**
* Instantiates a new annotation builder.
*
* @param objectPostProcessor the object post processor
* @param allowConfigurersOfSameType the allow configurers of same type
*/
protected AbstractConfiguredAnnotationBuilder(ObjectPostProcessor<Object> objectPostProcessor, boolean allowConfigurersOfSameType) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}
@Override
protected final O doBuild() throws Exception {
synchronized (mainConfigurers) {
buildState = BuildState.INITIALIZING_MAINS;
beforeInit();
initMainConfigurers();
buildState = BuildState.CONFIGURING_MAINS;
beforeConfigureMains();
configureMainConfigurers();
buildState = BuildState.CONFIGURING_POSTS;
beforeConfigurePosts();
configurePostConfigurers();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
/**
* Similar to {@link #build()} and {@link #getObject()} but checks the state
* to determine if {@link #build()} needs to be called first.
*
* @return the result of {@link #build()} or {@link #getObject()}. If an
* error occurs while building, returns null.
*/
public O getOrBuild() {
if (isUnbuilt()) {
try {
return build();
} catch (Exception e) {
log.error("Failed to perform build. Returning null", e);
return null;
}
} else {
return getObject();
}
}
/**
* Applies a {@link AnnotationConfigurerAdapter} to this
* {@link AnnotationBuilder} and invokes
* {@link AnnotationConfigurerAdapter#setBuilder(AnnotationBuilder)}.
*
* @param configurer the configurer
* @param <C> type of AnnotationConfigurer
* @return Configurer passed as parameter
* @throws Exception if error occurred
*/
@SuppressWarnings("unchecked")
public <C extends AnnotationConfigurerAdapter<O,I,B>> C apply(C configurer) throws Exception {
add(configurer);
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
return configurer;
}
/**
* Similar to {@link #apply(AnnotationConfigurer)} but checks the state
* to determine if {@link #apply(AnnotationConfigurer)} needs to be called first.
*
* @param configurer the configurer
* @param <C> type of AnnotationConfigurer
* @return Configurer passed as parameter
* @throws Exception if error occurred
*/
@SuppressWarnings("unchecked")
public <C extends AnnotationConfigurerAdapter<O,I,B>> C getOrApply(C configurer) throws Exception {
C existing = (C) getConfigurer(configurer.getClass());
if (existing != null) {
return existing;
}
return apply(configurer);
}
/**
* Applies a {@link AnnotationConfigurer} to this {@link AnnotationBuilder}
* overriding any {@link AnnotationConfigurer} of the exact same class. Note
* that object hierarchies are not considered.
*
* @param configurer the configurer
* @param <C> type of AnnotationConfigurer
* @return Configurer passed as parameter
* @throws Exception if error occurred
*/
public <C extends AnnotationConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
/**
* Sets an object that is shared by multiple {@link AnnotationConfigurer}.
*
* @param sharedType the Class to key the shared object by.
* @param object the Object to store
* @param <C> type of share object
*/
@SuppressWarnings("unchecked")
public <C> void setSharedObject(Class<C> sharedType, C object) {
this.sharedObjects.put((Class<Object>) sharedType, object);
}
/**
* Gets a shared Object. Note that object hierarchies are not considered.
*
* @param sharedType the type of the shared Object
* @param <C> type of share object
* @return the shared Object or null if it is not found
*/
@SuppressWarnings("unchecked")
public <C> C getSharedObject(Class<C> sharedType) {
return (C) this.sharedObjects.get(sharedType);
}
/**
* Gets the shared objects.
*
* @return Shared objects
*/
public Map<Class<Object>, Object> getSharedObjects() {
return Collections.unmodifiableMap(this.sharedObjects);
}
/**
* Adds {@link AnnotationConfigurer} ensuring that it is allowed and
* invoking {@link AnnotationConfigurer#init(AnnotationBuilder)} immediately
* if necessary.
*
* @param configurer the {@link AnnotationConfigurer} to add
* @param <C> type of AnnotationConfigurer
* @throws Exception if an error occurs
*/
@SuppressWarnings("unchecked")
private <C extends AnnotationConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends AnnotationConfigurer<O, B>> clazz =
(Class<? extends AnnotationConfigurer<O, B>>) configurer.getClass();
if (!buildState.isConfigured()) {
synchronized (mainConfigurers) {
List<AnnotationConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.mainConfigurers.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<AnnotationConfigurer<O, B>>(1);
}
configs.add(configurer);
this.mainConfigurers.put(clazz, configs);
if (buildState.isInitializing()) {
configurer.init((B) this);
}
}
} else {
synchronized (postConfigurers) {
List<AnnotationConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.postConfigurers.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<AnnotationConfigurer<O, B>>(1);
}
configs.add(configurer);
this.postConfigurers.put(clazz, configs);
configurer.init((B) this);
}
}
}
/**
* Gets all the {@link AnnotationConfigurer} instances by its class name or an
* empty List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link AnnotationConfigurer} class to look for
* @param <C> type of AnnotationConfigurer
* @return All configurers
*/
@SuppressWarnings("unchecked")
public <C extends AnnotationConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.mainConfigurers.get(clazz);
if (configs == null) {
return new ArrayList<C>();
}
return new ArrayList<C>(configs);
}
/**
* Removes all the {@link AnnotationConfigurer} instances by its class name or an
* empty List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link AnnotationConfigurer} class to look for
* @param <C> type of AnnotationConfigurer
* @return Empty list of configurers
*/
@SuppressWarnings("unchecked")
public <C extends AnnotationConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.mainConfigurers.remove(clazz);
if (configs == null) {
return new ArrayList<C>();
}
return new ArrayList<C>(configs);
}
/**
* Gets the {@link AnnotationConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not
* considered.
*
* @param clazz the configurer class type
* @param <C> type of AnnotationConfigurer
* @return Matched configurers
*/
@SuppressWarnings("unchecked")
public <C extends AnnotationConfigurer<O, B>> C getConfigurer(Class<C> clazz) {
List<AnnotationConfigurer<O, B>> configs = this.mainConfigurers.get(clazz);
if (configs == null) {
return null;
}
if (configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
}
return (C) configs.get(0);
}
/**
* Removes and returns the {@link AnnotationConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not
* considered.
*
* @param clazz the configurer class type
* @param <C> type of AnnotationConfigurer
* @return Matched configurers
*/
@SuppressWarnings("unchecked")
public <C extends AnnotationConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
List<AnnotationConfigurer<O, B>> configs = this.mainConfigurers.remove(clazz);
if (configs == null) {
return null;
}
if (configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
}
return (C) configs.get(0);
}
/**
* Specifies the {@link ObjectPostProcessor} to use.
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
* @return the {@link AnnotationBuilder} for further customizations
*/
@SuppressWarnings("unchecked")
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
Assert.notNull(objectPostProcessor,"objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
return (O) this;
}
/**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
*
* @param object the Object to post process
* @param <P> type of processed object
* @return the possibly modified Object to use
*/
protected <P> P postProcess(P object) {
return (P) this.objectPostProcessor.postProcess(object);
}
/**
* Invoked prior to invoking each
* {@link AnnotationConfigurer#init(AnnotationBuilder)} method. Subclasses may
* override this method to hook into the lifecycle without using a
* {@link AnnotationConfigurer}.
*
* @throws Exception if error occurred
*/
protected void beforeInit() throws Exception {
}
/**
* Invoked prior to invoking each main
* {@link AnnotationConfigurer#configure(AnnotationBuilder)} method.
* Subclasses may override this method to hook into the lifecycle without
* using a {@link AnnotationConfigurer}.
*
* @throws Exception if error occurred
*/
protected void beforeConfigureMains() throws Exception {
}
/**
* Invoked prior to invoking each post
* {@link AnnotationConfigurer#configure(AnnotationBuilder)} method.
* Subclasses may override this method to hook into the lifecycle without
* using a {@link AnnotationConfigurer}.
*
* @throws Exception if error occurred
*/
protected void beforeConfigurePosts() throws Exception {
}
/**
* Subclasses must implement this method to build the object that is being returned.
*
* @return Object build by this builder
* @throws Exception if error occurred
*/
protected abstract O performBuild() throws Exception;
@SuppressWarnings("unchecked")
private void initMainConfigurers() throws Exception {
for (AnnotationConfigurer<O, B> configurer : getMainConfigurers()) {
configurer.init((B) this);
}
}
@SuppressWarnings("unchecked")
private void configureMainConfigurers() throws Exception {
for (AnnotationConfigurer<O, B> configurer : getMainConfigurers()) {
configurer.configure((B) this);
}
}
@SuppressWarnings("unchecked")
private void configurePostConfigurers() throws Exception {
for (AnnotationConfigurer<O, B> configurer : getPostConfigurers()) {
configurer.configure((B) this);
}
}
/**
* Gets all configurers.
*
* @return the configurers
*/
private Collection<AnnotationConfigurer<O, B>> getMainConfigurers() {
List<AnnotationConfigurer<O, B>> result = new ArrayList<AnnotationConfigurer<O, B>>();
for (List<AnnotationConfigurer<O, B>> configs : this.mainConfigurers.values()) {
result.addAll(configs);
}
return result;
}
private Collection<AnnotationConfigurer<O, B>> getPostConfigurers() {
List<AnnotationConfigurer<O, B>> result = new ArrayList<AnnotationConfigurer<O, B>>();
for (List<AnnotationConfigurer<O, B>> configs : this.postConfigurers.values()) {
result.addAll(configs);
}
return result;
}
/**
* Determines if the object is unbuilt.
*
* @return true, if unbuilt else false
*/
private boolean isUnbuilt() {
synchronized (mainConfigurers) {
return buildState == BuildState.UNBUILT;
}
}
/**
* The build state for the application
*/
private static enum BuildState {
/**
* This is the state before the {@link AnnotationBuilder#build()} is invoked
*/
UNBUILT(0),
/**
* The state from when {@link AnnotationBuilder#build()} is first invoked until
* all the {@link AnnotationConfigurer#init(AnnotationBuilder)} methods have
* been invoked.
*/
INITIALIZING_MAINS(1),
/**
* The state from after all main
* {@link AnnotationConfigurer#init(AnnotationBuilder)}
* have been invoked until after all the
* {@link AnnotationConfigurer#configure(AnnotationBuilder)}
* methods have been invoked.
*/
CONFIGURING_MAINS(2),
/**
* The state from after all post
* {@link AnnotationConfigurer#init(AnnotationBuilder)}
* have been invoked until after all the
* {@link AnnotationConfigurer#configure(AnnotationBuilder)}
* methods have been invoked.
*/
CONFIGURING_POSTS(3),
/**
* From the point after all the
* {@link AnnotationConfigurer#configure(AnnotationBuilder)}
* have completed to just after
* {@link AbstractConfiguredAnnotationBuilder#performBuild()}.
*/
BUILDING(4),
/**
* After the object has been completely built.
*/
BUILT(5);
private final int order;
BuildState(int order) {
this.order = order;
}
/**
* Checks if is initializing.
*
* @return true, if is initializing
*/
public boolean isInitializing() {
return INITIALIZING_MAINS.order == order;
}
/**
* Determines if the state is CONFIGURING or later
*
* @return true, if configured
*/
public boolean isConfigured() {
return order >= CONFIGURING_MAINS.order;
}
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import java.lang.annotation.Annotation;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Base class for {@link Configuration} which works on a bean definition level
* relying on {@link ImportBeanDefinitionRegistrar} phase to register beans.
*
* @author Janne Valkealahti
*
* @param <O> The object that used builder returns
* @param <B> The type of the builder
*/
public abstract class AbstractImportingAnnotationConfiguration<B extends AnnotationBuilder<O>, O> implements
ImportBeanDefinitionRegistrar, BeanFactoryAware, EnvironmentAware {
private BeanFactory beanFactory;
private Environment environment;
private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Class<?> annotationType = getAnnotation();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(
annotationType.getName(), false));
String[] names = attributes.getStringArray("name");
BeanDefinition beanDefinition;
try {
beanDefinition = buildBeanDefinition();
} catch (Exception e) {
throw new RuntimeException("Error with onConfigurers", e);
}
if (ObjectUtils.isEmpty(names)) {
// ok, name(s) not given, generate one
names = new String[] { beanNameGenerator.generateBeanName(beanDefinition, registry) };
}
registry.registerBeanDefinition(names[0], beanDefinition);
if (names.length > 1) {
for (int i = 1; i < names.length; i++) {
registry.registerAlias(names[0], names[i]);
}
}
}
protected abstract static class BeanDelegatingFactoryBean<B extends AnnotationBuilder<O>, O>
implements FactoryBean<O>, InitializingBean {
private final B builder;
private O object;
private List<AnnotationConfigurer<O, B>> configurers;
public BeanDelegatingFactoryBean(B builder){
this.builder = builder;
}
@Override
public abstract Class<O> getObjectType();
@Override
public O getObject() throws Exception {
return object;
}
@Override
public boolean isSingleton() {
return true;
}
// @Override
// public void afterPropertiesSet() throws Exception {
// for (AnnotationConfigurer<O, B> configurer : configurers) {
// if (configurer.isAssignable(builder)) {
// // we need builder.apply(configurer);
//// builder.
// }
// }
// // should be getOrBuild???
// object = builder.build();
// }
@Autowired(required = false)
public void setConfigurers(List<AnnotationConfigurer<O, B>> configurers) {
this.configurers = configurers;
}
public B getBuilder() {
return builder;
}
public List<AnnotationConfigurer<O, B>> getConfigurers() {
return configurers;
}
protected void setObject(O object) {
this.object = object;
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"beanFactory be of type ListableBeanFactory but was " + beanFactory);
this.beanFactory = beanFactory;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
/**
* Called to get a bean definition to register.
*
* @return the bean definition to register
* @throws Exception if error occurred
*/
protected abstract BeanDefinition buildBeanDefinition() throws Exception;
/**
* Gets the annotation specific for this configurer.
*
* @return the annotation
*/
protected abstract Class<? extends Annotation> getAnnotation();
/**
* Gets the bean factory.
*
* @return the bean factory
*/
protected BeanFactory getBeanFactory() {
return beanFactory;
}
/**
* Gets the environment.
*
* @return the environment
*/
protected Environment getEnvironment() {
return environment;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
/**
* Interface for building an {@link Object}.
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @param <O> The type of the Object being built
*/
public interface AnnotationBuilder<O> {
/**
* Builds the object and returns it or null.
*
* @return the Object to be built or null if the implementation allows it.
* @throws Exception if an error occurred when building the Object
*/
O build() throws Exception;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
/**
* Allows for configuring an {@link AnnotationBuilder}. All
* {@link AnnotationConfigurer}s first have their {@link #init(AnnotationBuilder)}
* method invoked. After all {@link #init(AnnotationBuilder)} methods have been
* invoked, each {@link #configure(AnnotationBuilder)} method is invoked.
*
* @see AbstractConfiguredAnnotationBuilder
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @param <O> The object being built by the {@link AnnotationBuilder} B
* @param <B> The {@link AnnotationBuilder} that builds objects of type O. This is
* also the {@link AnnotationBuilder} that is being configured.
*/
public interface AnnotationConfigurer<O, B extends AnnotationBuilder<O>> {
/**
* Initialise the {@link AnnotationBuilder}. Here only shared state should be
* created and modified, but not properties on the {@link AnnotationBuilder}
* used for building the object. This ensures that the
* {@link #configure(AnnotationBuilder)} method uses the correct shared
* objects when building.
*
* @param builder the builder
* @throws Exception if error occurred
*/
void init(B builder) throws Exception;
/**
* Configure the {@link AnnotationBuilder} by setting the necessary properties
* on the {@link AnnotationBuilder}.
*
* @param builder the builder
* @throws Exception if error occurred
*/
void configure(B builder) throws Exception;
boolean isAssignable(AnnotationBuilder<O> builder);
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.GenericTypeResolver;
/**
* A base class for {@link AnnotationConfigurer} that allows subclasses to only
* implement the methods they are interested in. It also provides a mechanism
* for using the {@link AnnotationConfigurer} and when done gaining access to the
* {@link AnnotationBuilder} that is being configured.
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @param <O> The Object being built by B
* @param <I> The interface of type B
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
*/
public abstract class AnnotationConfigurerAdapter<O,I,B extends AnnotationBuilder<O>>
implements AnnotationConfigurer<O,B> {
private B builder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
@Override
public void init(B builder) throws Exception {}
@Override
public void configure(B builder) throws Exception {}
/**
* Return the {@link AnnotationBuilder} when done using the
* {@link AnnotationConfigurer}. This is useful for method chaining.
*
* @return the {@link AnnotationBuilder}
*/
@SuppressWarnings("unchecked")
public I and() {
// we're either casting to itself or its interface
return (I) getBuilder();
}
/**
* Gets the {@link AnnotationBuilder}. Cannot be null.
*
* @return the {@link AnnotationBuilder}
* @throws IllegalStateException if AnnotationBuilder is null
*/
protected final B getBuilder() {
if(builder == null) {
throw new IllegalStateException("annotationBuilder cannot be null");
}
return builder;
}
/**
* Adds an {@link ObjectPostProcessor} to be used for this adapter. The
* default implementation does nothing to the object.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
/**
* Sets the {@link AnnotationBuilder} to be used. This is automatically set
* when using
* {@link AbstractConfiguredAnnotationBuilder#apply(AnnotationConfigurerAdapter)}
*
* @param builder the {@link AnnotationBuilder} to set
*/
public void setBuilder(B builder) {
this.builder = builder;
}
/**
* An {@link ObjectPostProcessor} that delegates work to numerous
* {@link ObjectPostProcessor} implementations.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<? extends Object>> postProcessors = new ArrayList<ObjectPostProcessor<?>>();
@Override
public Object postProcess(Object object) {
for(ObjectPostProcessor opp : postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,ObjectPostProcessor.class);
if(oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
}
return object;
}
/**
* Adds an {@link ObjectPostProcessor} to use
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
* @return true if the {@link ObjectPostProcessor} was added, else false
*/
private boolean addObjectPostProcessor(ObjectPostProcessor<? extends Object> objectPostProcessor) {
return this.postProcessors.add(objectPostProcessor);
}
}
@Override
public boolean isAssignable(AnnotationBuilder<O> builder) {
return true;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
/**
* Interface for wrapping a return type from {@link AnnotationConfigurer}
* into {@link AnnotationBuilder}.
*
* @author Janne Valkealahti
*
* @param <I> The parent return type of the configurer.
*/
public interface AnnotationConfigurerBuilder<I> {
/**
* Get a parent {@link AnnotationBuilder} working
* with a {@link AnnotationConfigurer}.
* @return The parent {@link AnnotationBuilder}
*/
I and();
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Base annotation used in JavaConfig order to enable
* some base functionality.
*
* @author Janne Valkealahti
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface EnableAnnotationConfiguration {
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Allows initialization of Objects. Typically this is used to call the
* {@link Aware} methods, {@link InitializingBean#afterPropertiesSet()}, and
* ensure that {@link DisposableBean#destroy()} has been invoked.
*
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
*
* @author Rob Winch
*/
public interface ObjectPostProcessor<T> {
/**
* Initialize the object possibly returning a modified instance that should
* be used instead.
*
* @param object the object to initialize
* @param <O> the type of a processed object
* @return the initialized version of the object
*/
<O extends T> O postProcess(O object);
/**
* A do nothing implementation of the {@link ObjectPostProcessor}
*/
ObjectPostProcessor<Object> QUIESCENT_POSTPROCESSOR = new ObjectPostProcessor<Object>() {
@Override
public <T> T postProcess(T object) {
return object;
}
};
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configuration;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
import org.springframework.util.Assert;
/**
* Post processor handling lifecycle methods of
* POJOs created from builder/configurer.
*
* @author Janne Valkealahti
*
*/
final class AutowireBeanFactoryObjectPostProcessor implements ObjectPostProcessor<Object>, DisposableBean, SmartLifecycle {
private final static Log log = LogFactory.getLog(AutowireBeanFactoryObjectPostProcessor.class);
private final AutowireCapableBeanFactory autowireBeanFactory;
private final List<DisposableBean> disposableBeans = new ArrayList<DisposableBean>();
private final List<Lifecycle> lifecycleBeans = new ArrayList<Lifecycle>();
private boolean running;
/**
* Instantiates a new autowire bean factory object post processor.
*
* @param autowireBeanFactory the autowire bean factory
*/
public AutowireBeanFactoryObjectPostProcessor(AutowireCapableBeanFactory autowireBeanFactory) {
Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
this.autowireBeanFactory = autowireBeanFactory;
}
@SuppressWarnings("unchecked")
@Override
public <T> T postProcess(T object) {
T result = (T) autowireBeanFactory.initializeBean(object, null);
if(result instanceof DisposableBean) {
disposableBeans.add((DisposableBean) result);
}
if(result instanceof Lifecycle) {
lifecycleBeans.add((Lifecycle) result);
}
return result;
}
@Override
public void destroy() throws Exception {
for(DisposableBean disposable : disposableBeans) {
try {
disposable.destroy();
} catch(Exception error) {
log.error(error);
}
}
}
@Override
public void start() {
running = true;
for (Lifecycle bean : lifecycleBeans) {
bean.start();
}
}
@Override
public void stop() {
for (Lifecycle bean : lifecycleBeans) {
bean.stop();
}
running = false;
}
@Override
public boolean isRunning() {
return running;
}
@Override
public int getPhase() {
return 0;
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configuration;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.common.annotation.EnableAnnotationConfiguration;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
/**
* Spring {@link Configuration} that exports the default
* {@link ObjectPostProcessor}. This class is not intended to be imported
* manually rather it is imported automatically when using {@link EnableAnnotationConfiguration}
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @see EnableAnnotationConfiguration
*/
@Configuration
public class ObjectPostProcessorConfiguration {
@Bean
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configurers;
import java.util.Map;
import java.util.Properties;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
/**
* {@link org.springframework.statemachine.config.common.annotation.AnnotationConfigurer AnnotationConfigurer}
* which knows how to handle configuring a {@link Properties}.
*
* @author Janne Valkealahti
*
* @param <O> The Object being built by B
* @param <I> The type of interface or builder itself returned by the configurer
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
*/
public class DefaultPropertiesConfigurer<O,I,B extends AnnotationBuilder<O>>
extends AnnotationConfigurerAdapter<O,I,B> implements PropertiesConfigurer<I> {
private Properties properties = new Properties();
/**
* Adds a {@link Properties} to this builder.
*
* @param properties the properties
* @return the {@link PropertiesConfigurer} for chaining
*/
@Override
public PropertiesConfigurer<I> properties(Properties properties) {
if (properties != null) {
this.properties.putAll(properties);
}
return this;
}
@Override
public PropertiesConfigurer<I> properties(Map<String, String> properties) {
Properties props = new Properties();
if (properties != null) {
props.putAll(properties);
}
return properties(props);
}
/**
* Adds a property to this builder.
*
* @param key the key
* @param value the value
* @return the {@link PropertiesConfigurer} for chaining
*/
@Override
public PropertiesConfigurer<I> property(String key, String value) {
properties.put(key, value);
return this;
}
/**
* Gets the {@link Properties} configured for this builder.
*
* @return the properties
*/
public Properties getProperties() {
return properties;
}
@Override
public void configure(B builder) throws Exception {
if (!configureProperties(builder, properties)) {
if (builder instanceof PropertiesConfigurerAware) {
((PropertiesConfigurerAware)builder).configureProperties(properties);
}
}
}
/**
* Configure properties. If this implementation is extended,
* custom configure handling can be handled here.
*
* @param builder the builder
* @param properties the properties
* @return true, if properties configure is handled
*/
protected boolean configureProperties(B builder, Properties properties){
return false;
};
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configurers;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
/**
* {@link AnnotationConfigurer} which knows how to handle
* configuring a {@link Resource}s.
*
* @author Janne Valkealahti
*
* @param <O> The Object being built by B
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
* @param <I> The type of an interface of B
*/
public class DefaultResourceConfigurer<O,I,B extends AnnotationBuilder<O>>
extends AnnotationConfigurerAdapter<O,I,B> implements ResourceConfigurer<I> {
private Set<Resource> resources = new HashSet<Resource>();
private final DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
@Override
public void configure(B builder) throws Exception {
if (!configureResources(builder, resources)) {
if (builder instanceof ResourceConfigurerAware) {
((ResourceConfigurerAware)builder).configureResources(resources);
}
}
}
/**
* Adds a {@link Set} of {@link Resource}s to this builder.
*
* @param resources the resources
* @return the {@link ResourceConfigurer} for chaining
*/
@Override
public ResourceConfigurer<I> resources(Set<Resource> resources) {
this.resources.addAll(resources);
return this;
}
/**
* Adds a {@link Resource} to this builder.
*
* @param resource the resource
* @return the {@link ResourceConfigurer} for chaining
*/
@Override
public ResourceConfigurer<I> resource(Resource resource) {
resources.add(resource);
return this;
}
/**
* Adds a {@link Resource} to this builder.
*
* @param resource the resource
* @return the {@link ResourceConfigurer} for chaining
*/
@Override
public ResourceConfigurer<I> resource(String resource) {
resources.add(resourceLoader.getResource(resource));
return this;
}
/**
* Adds a {@link Resource}s to this builder.
*
* @param resources the resources
* @return the {@link ResourceConfigurer} for chaining
*/
@Override
public ResourceConfigurer<I> resources(List<String> resources) {
if (resources != null) {
for (String resource : resources) {
resource(resource);
}
}
return this;
}
/**
* Gets the {@link Resource}s configured for this builder.
*
* @return the resources
*/
public Set<Resource> getResources() {
return resources;
}
/**
* Configure resources. If this implementation is extended,
* custom configure handling can be handled here.
*
* @param builder the builder
* @param resources the resources
* @return true, if resources configure is handled
*/
protected boolean configureResources(B builder, Set<Resource> resources){
return false;
};
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configurers;
import java.util.Map;
import java.util.Properties;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
/**
* Interface for {@link DefaultPropertiesConfigurer} which act
* as intermediate gatekeeper between a user and
* an {@link org.springframework.statemachine.config.common.annotation.AnnotationConfigurer}.
*
* @author Janne Valkealahti
*
* @param <I> The parent return type of the configurer.
*/
public interface PropertiesConfigurer<I> extends AnnotationConfigurerBuilder<I> {
PropertiesConfigurer<I> properties(Properties properties);
PropertiesConfigurer<I> properties(Map<String, String> properties);
PropertiesConfigurer<I> property(String key, String value);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configurers;
import java.util.Properties;
/**
* Interface for {@link org.springframework.statemachine.config.common.annotation.AnnotationBuilder AnnotationBuilder}
* which wants to be aware of {@link Properties} configured by {@link DefaultPropertiesConfigurer}.
*
* @author Janne Valkealahti
*
*/
public interface PropertiesConfigurerAware {
/**
* Configure {@link Properties}.
*
* @param properties the properties
*/
void configureProperties(Properties properties);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configurers;
import java.util.List;
import java.util.Set;
import org.springframework.core.io.Resource;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
/**
* Interface for {@link DefaultResourceConfigurer} which act
* as intermediate gatekeeper between a user and
* an {@link org.springframework.statemachine.config.common.annotation.AnnotationConfigurer}.
*
* @author Janne Valkealahti
*
* @param <I> The parent return type of the configurer.
*/
public interface ResourceConfigurer<I> extends AnnotationConfigurerBuilder<I> {
ResourceConfigurer<I> resources(Set<Resource> resources);
ResourceConfigurer<I> resources(List<String> resources);
ResourceConfigurer<I> resource(Resource resource);
ResourceConfigurer<I> resource(String resource);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2015 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.statemachine.config.common.annotation.configurers;
import java.util.Set;
import org.springframework.core.io.Resource;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
/**
* Interface for {@link AnnotationBuilder} which wants to be
* aware of {@link Resource}s configured by {@link DefaultResourceConfigurer}.
*
* @author Janne Valkealahti
*
*/
public interface ResourceConfigurerAware {
/**
* Configure {@link Resource}s.
*
* @param resources the resources
*/
void configureResources(Set<Resource> resources);
}

View File

@@ -0,0 +1,103 @@
package org.springframework.statemachine.config.configuration;
import java.lang.annotation.Annotation;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
import org.springframework.statemachine.state.State;
@Configuration
public class StateMachineConfiguration<S extends Enum<S>, E extends Enum<E>> extends
AbstractImportingAnnotationConfiguration<StateMachineConfigBuilder<S, E>, StateMachineConfig<S, E>> {
private final StateMachineConfigBuilder<S, E> builder = new StateMachineConfigBuilder<S, E>();
@Override
protected BeanDefinition buildBeanDefinition() throws Exception {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(StateMachineDelegatingFactoryBean.class);
beanDefinitionBuilder.addConstructorArgValue(builder);
return beanDefinitionBuilder.getBeanDefinition();
}
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableStateMachine.class;
}
private static class StateMachineDelegatingFactoryBean<S extends Enum<S>, E extends Enum<E>> implements
FactoryBean<StateMachine<State<S, E>, E>>, BeanFactoryAware, InitializingBean {
private final StateMachineConfigBuilder<S, E> builder;
private List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers;
private BeanFactory beanFactory;
private StateMachine<State<S, E>, E> stateMachine;
@SuppressWarnings("unused")
public StateMachineDelegatingFactoryBean(StateMachineConfigBuilder<S, E> builder) {
this.builder = builder;
}
@Override
public StateMachine<State<S, E>, E> getObject() throws Exception {
return stateMachine;
}
@Override
public Class<?> getObjectType() {
return StateMachine.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void afterPropertiesSet() throws Exception {
for (AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> configurer : configurers) {
builder.apply(configurer);
}
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
EnumStateMachineFactory<S,E> stateMachineFactory = new EnumStateMachineFactory<S, E>(stateMachineTransitions, stateMachineStates);
stateMachineFactory.setBeanFactory(beanFactory);
stateMachine = stateMachineFactory.getStateMachine();
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Autowired(required=false)
protected void onConfigurers(
List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers)
throws Exception {
this.configurers = configurers;
}
}
}

View File

@@ -0,0 +1,103 @@
package org.springframework.statemachine.config.configuration;
import java.lang.annotation.Annotation;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfig;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
import org.springframework.statemachine.state.State;
@Configuration
public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<E>> extends
AbstractImportingAnnotationConfiguration<StateMachineConfigBuilder<S, E>, StateMachineConfig<S, E>> {
private final StateMachineConfigBuilder<S, E> builder = new StateMachineConfigBuilder<S, E>();
@Override
protected BeanDefinition buildBeanDefinition() throws Exception {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(StateMachineFactoryDelegatingFactoryBean.class);
beanDefinitionBuilder.addConstructorArgValue(builder);
return beanDefinitionBuilder.getBeanDefinition();
}
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableStateMachineFactory.class;
}
private static class StateMachineFactoryDelegatingFactoryBean<S extends Enum<S>, E extends Enum<E>> implements
FactoryBean<StateMachineFactory<State<S, E>, E>>, BeanFactoryAware, InitializingBean {
private final StateMachineConfigBuilder<S, E> builder;
private List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers;
private BeanFactory beanFactory;
private StateMachineFactory<State<S, E>, E> stateMachineFactory;
@SuppressWarnings("unused")
public StateMachineFactoryDelegatingFactoryBean(StateMachineConfigBuilder<S, E> builder) {
this.builder = builder;
}
@Override
public StateMachineFactory<State<S, E>, E> getObject() throws Exception {
return stateMachineFactory;
}
@Override
public Class<?> getObjectType() {
return StateMachineFactory.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void afterPropertiesSet() throws Exception {
for (AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> configurer : configurers) {
builder.apply(configurer);
}
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
EnumStateMachineFactory<S,E> enumStateMachineFactory = new EnumStateMachineFactory<S, E>(stateMachineTransitions, stateMachineStates);
enumStateMachineFactory.setBeanFactory(beanFactory);
this.stateMachineFactory = enumStateMachineFactory;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Autowired(required=false)
protected void onConfigurers(
List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers)
throws Exception {
this.configurers = configurers;
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.guard.SpelExpressionGuard;
import org.springframework.statemachine.transition.TransitionKind;
/**
* Default implementation of a {@link ExternalTransitionConfigurer}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class DefaultExternalTransitionConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
implements ExternalTransitionConfigurer<S, E> {
private S source;
private S target;
private E event;
private Collection<Action> actions = new ArrayList<Action>();
private Guard guard;
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
builder.add(source, target, event, actions, guard, TransitionKind.EXTERNAL);
}
@Override
public ExternalTransitionConfigurer<S, E> source(S source) {
this.source = source;
return this;
}
@Override
public ExternalTransitionConfigurer<S, E> target(S target) {
this.target = target;
return this;
}
@Override
public ExternalTransitionConfigurer<S, E> event(E event) {
this.event = event;
return this;
}
@Override
public ExternalTransitionConfigurer<S, E> action(Action action) {
actions.add(action);
return this;
}
@Override
public ExternalTransitionConfigurer<S, E> guard(Guard guard) {
this.guard = guard;
return this;
}
@Override
public ExternalTransitionConfigurer<S, E> guardExpression(String expression) {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
this.guard = new SpelExpressionGuard(parser.parseExpression(expression));
return this;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.guard.SpelExpressionGuard;
import org.springframework.statemachine.transition.TransitionKind;
/**
* Default implementation of a {@link InternalTransitionConfigurer}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class DefaultInternalTransitionConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
implements InternalTransitionConfigurer<S, E> {
private S source;
private S target;
private E event;
private Collection<Action> actions = new ArrayList<Action>();
private Guard guard;
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
builder.add(source, target, event, actions, guard, TransitionKind.INTERNAL);
}
@Override
public InternalTransitionConfigurer<S, E> source(S source) {
this.source = source;
return this;
}
@Override
public InternalTransitionConfigurer<S, E> event(E event) {
this.event = event;
return this;
}
@Override
public InternalTransitionConfigurer<S, E> action(Action action) {
actions.add(action);
return this;
}
@Override
public InternalTransitionConfigurer<S, E> guard(Guard guard) {
this.guard = guard;
return this;
}
@Override
public InternalTransitionConfigurer<S, E> guardExpression(String expression) {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
this.guard = new SpelExpressionGuard(parser.parseExpression(expression));
return this;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
public class DefaultStateConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
implements StateConfigurer<S, E> {
private final Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
private S initial;
@Override
public void configure(StateMachineStateBuilder<S, E> builder) throws Exception {
builder.add(states);
builder.setInitialState(initial);
}
@Override
public StateConfigurer<S, E> initial(S initial) {
this.initial = initial;
return this;
}
@Override
public StateConfigurer<S, E> state(S state) {
return state(state, (E[])null);
}
@Override
public StateConfigurer<S, E> state(S state, Collection<Action> entryActions, Collection<Action> exitActions) {
states.add(new StateData<S, E>(state, null, entryActions, exitActions));
return this;
}
@Override
public StateConfigurer<S, E> state(S state, E... deferred) {
states.add(new StateData<S, E>(state, deferred));
return this;
}
@Override
public StateConfigurer<S, E> states(Set<S> states) {
for (S s : states) {
state(s);
}
return this;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import org.springframework.statemachine.transition.Transition;
/**
* {@code TransitionConfigurer} interface for configuring external {@link Transition}s.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface ExternalTransitionConfigurer<S, E> extends
TransitionConfigurer<ExternalTransitionConfigurer<S, E>, S, E> {
/**
* Specify a target state {@code S} for this {@link Transition}.
*
* @param target the target state {@code S}
* @return configurer for chaining
*/
ExternalTransitionConfigurer<S, E> target(S target);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import org.springframework.statemachine.transition.Transition;
/**
* {@code TransitionConfigurer} interface for configuring internal {@link Transition}s.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface InternalTransitionConfigurer<S, E> extends
TransitionConfigurer<InternalTransitionConfigurer<S, E>, S, E> {
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import java.util.Collection;
import java.util.Set;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
public interface StateConfigurer<S, E> extends
AnnotationConfigurerBuilder<StateMachineStateConfigurer<S, E>> {
StateConfigurer<S, E> initial(S initial);
StateConfigurer<S, E> state(S state);
StateConfigurer<S, E> state(S state, Collection<Action> entryActions, Collection<Action> exitActions);
StateConfigurer<S, E> state(S state, E... deferred);
StateConfigurer<S, E> states(Set<S> states);
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2015 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.statemachine.config.configurers;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.transition.Transition;
/**
* Base {@code TransitionConfigurer} interface for configuring {@link Transition}s.
*
* @author Janne Valkealahti
*
* @param <T> the type of a transition configurer
* @param <S> the type of state
* @param <E> the type of event
*/
public interface TransitionConfigurer<T, S, E> extends
AnnotationConfigurerBuilder<StateMachineTransitionConfigurer<S, E>> {
/**
* Specify a source state {@code S} for this {@link Transition}.
*
* @param source the source state {@code S}
* @return configurer for chaining
*/
T source(S source);
/**
* Specify event {@code E} for this {@link Transition}.
*
* @param event the event for transition
* @return configurer for chaining
*/
T event(E event);
/**
* Specify {@link Action} for this {@link Transition}.
*
* @param action the action
* @return configurer for chaining
*/
T action(Action action);
/**
* Specify a {@link Guard} for this {@link Transition}.
*
* @param guard the guard
* @return configurer for chaining
*/
T guard(Guard guard);
/**
* Specify a {@link Guard} backed by a SpEL expression for this {@link Transition}.
*
* @param expression the SpEL expression
* @return configurer for chaining
*/
T guardExpression(String expression);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2015 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.statemachine.guard;
import org.springframework.statemachine.StateContext;
/**
* {@code Guard}s are typically considered as guard conditions which affect the
* behaviour of a state machine by enabling actions or transitions only when they
* evaluate to {@code TRUE} and disabling them when they evaluate to
* {@code FALSE}.
*
* @author Janne Valkealahti
*
*/
public interface Guard {
/**
* Evaluate a guard condition.
*
* @param context the state context
* @return true, if guard evaluation is successful, false otherwise.
*/
boolean evaluate(StateContext context);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2015 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.statemachine.guard;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.statemachine.StateContext;
import org.springframework.util.Assert;
/**
* {@link Guard} which uses Spring SpEL expression for condition evaluation.
*
* @author Janne Valkealahti
*
*/
public class SpelExpressionGuard implements Guard {
private final Expression expression;
/**
* Instantiates a new spel expression guard.
*
* @param expression the expression
*/
public SpelExpressionGuard(Expression expression) {
Assert.notNull(expression, "Expression cannot be null");
this.expression = expression;
}
@Override
public boolean evaluate(StateContext context) {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(context);
return expression.getValue(evaluationContext, Boolean.class);
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2015 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.statemachine.listener;
import java.util.List;
/**
* Base implementation for all composite listeners.
*
* @author Janne Valkealahti
*
* @param <T> the type of the listener
*/
public class AbstractCompositeListener<T> {
/** List of ordered composite listeners */
private OrderedComposite<T> listeners;
/**
* Constructs instance with an empty listener list.
*/
public AbstractCompositeListener() {
listeners = new OrderedComposite<T>();
}
/**
* Sets the list of listeners. This clears
* all existing listeners.
*
* @param listeners the new listeners
*/
public void setListeners(List<? extends T> listeners) {
this.listeners.setItems(listeners);
}
/**
* Register a new listener.
*
* @param listener the listener
*/
public void register(T listener) {
listeners.add(listener);
}
/**
* Gets the listeners.
*
* @return the listeners
*/
public OrderedComposite<T> getListeners() {
return listeners;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2015 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.statemachine.listener;
import java.util.Iterator;
import org.springframework.statemachine.state.State;
public class CompositeStateMachineListener<S,E> extends AbstractCompositeListener<StateMachineListener<State<S,E>, E>> implements
StateMachineListener<State<S,E>, E> {
@Override
public void stateChanged(State<S, E> from, State<S, E> to) {
for (Iterator<StateMachineListener<State<S,E>, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
StateMachineListener<State<S,E>, E> listener = iterator.next();
listener.stateChanged(from, to);
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2015 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.statemachine.listener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
/**
* Composite item which can be used in other components which
* may want to allow automatic and annotation based ordering.
* Good use case is a list of listeners where user may want
* to place some of them to be processed before the others.
*
* @author Janne Valkealahti
*
* @param <S> the type of the item
*/
public class OrderedComposite<S> {
private List<S> unordered = new ArrayList<S>();
private List<S> ordered = new ArrayList<S>();
private Comparator<? super S> comparator = new AnnotationAwareOrderComparator();
private List<S> list = new ArrayList<S>();
/**
* Public setter for the listeners.
*
* @param items items
*/
public void setItems(List<? extends S> items) {
unordered.clear();
ordered.clear();
for (S s : items) {
add(s);
}
}
/**
* Register additional item.
*
* @param item item
*/
public void add(S item) {
if (item instanceof Ordered) {
if (!ordered.contains(item)) {
ordered.add(item);
}
} else if (AnnotationUtils.isAnnotationDeclaredLocally(Order.class, item.getClass())) {
if (!ordered.contains(item)) {
ordered.add(item);
}
} else if (!unordered.contains(item)) {
unordered.add(item);
}
Collections.sort(ordered, comparator);
list.clear();
list.addAll(ordered);
list.addAll(unordered);
}
/**
* Public getter for the list of items. The {@link Ordered} items come
* first, followed by any unordered ones.
*
* @return an iterator over the list of items
*/
public Iterator<S> iterator() {
return new ArrayList<S>(list).iterator();
}
/**
* Public getter for the list of items in reverse. The {@link Ordered} items
* come last, after any unordered ones.
*
* @return an iterator over the list of items
*/
public Iterator<S> reverse() {
ArrayList<S> result = new ArrayList<S>(list);
Collections.reverse(result);
return result.iterator();
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2015 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.statemachine.listener;
public interface StateMachineListener<S,E> {
void stateChanged(S from, S to);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* Strategy interface for post-processing annotated methods.
*
* @author Mark Fisher
* @author Janne Valkealahti
*
* @param <T> the type of an annotation
*/
public interface MethodAnnotationPostProcessor<T extends Annotation> {
/**
* Post process a bean. As a result of a given bean, its name, method and
* annotation in a method, this method can return a new bean or
* <code>null</code>. Caller of this method is then responsible to handle
* newly created object.
*
* @param bean the bean
* @param beanName the bean name
* @param method the method
* @param annotation the annotation
* @return the object
*/
Object postProcess(Object bean, String beanName, Method method, T annotation);
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* A simple {@link StateMachineRuntimeProcessor} implementation using
* methods from a state machine protected bean.
*
* @author Janne Valkealahti
*
* @param <T> the return type
*/
public class MethodInvokingStateMachineRuntimeProcessor<T> implements StateMachineRuntimeProcessor<T> {
private final StateMachineMethodInvokerHelper<T> delegate;
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Method method) {
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, method);
}
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, String methodName) {
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, methodName);
}
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Class<? extends Annotation> annotationType) {
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, annotationType);
}
@Override
public T process(StateMachineRuntime stateMachineRuntime) {
try {
return delegate.process(stateMachineRuntime);
} catch (Exception e) {
throw new RuntimeException("Error processing bean", e);
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.reflect.Method;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.annotation.OrderUtils;
import org.springframework.statemachine.annotation.OnTransition;
/**
* Post-processor for Methods annotated with {@link OnTransition}.
*
* @author Janne Valkealahti
*
*/
public class StateMachineActivatorAnnotationPostProcessor implements MethodAnnotationPostProcessor<OnTransition>{
protected final BeanFactory beanFactory;
public StateMachineActivatorAnnotationPostProcessor(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcess(Object bean, String beanName, Method method, OnTransition annotation) {
StateMachineHandler handler = new StateMachineOnTransitionHandler(bean, method, annotation);
Integer order = findOrder(bean, method);
if (order != null) {
handler.setOrder(order);
}
return handler;
}
/**
* Find {@link Order} order either from a class or
* method level. Method level always takes presence
* over class level.
*
* @param bean the bean to inspect
* @param method the method to inspect
* @return the order or NULL if not found
*/
private static Integer findOrder(Object bean, Method method) {
Integer order = OrderUtils.getOrder(bean.getClass());
Order ann = AnnotationUtils.findAnnotation(method, Order.class);
if (ann != null) {
order = ann.value();
}
return order;
}
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* A {@link BeanPostProcessor} implementation that processes method-level
* annotations such as {@link OnTransition}.
*
* @author Mark Fisher
* @author Marius Bogoevici
* @author Janne Valkealahti
*
*/
public class StateMachineAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean,
Lifecycle, ApplicationListener<ApplicationEvent> {
private final static Log log = LogFactory.getLog(StateMachineAnnotationPostProcessor.class);
/** Factory from BeanFactoryAware */
private volatile ConfigurableListableBeanFactory beanFactory;
/** Post processors map - annotation -> method post processor */
private final Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> postProcessors =
new HashMap<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>>();
/**
* Application events for post processed beans (if bean instance of ApplicationListener) will
* be dispatched from here via callback in this class.
*/
private final Set<ApplicationListener<ApplicationEvent>> listeners = new HashSet<ApplicationListener<ApplicationEvent>>();
/**
* Lifecycle callbacks for post processed bean (if bean instance of Lifecycle) will
* be dispatched from here via callback in this class.
*/
private final Set<Lifecycle> lifecycles = new HashSet<Lifecycle>();
/** Flag for Lifecycle in this class */
private volatile boolean running = true;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isAssignable(ConfigurableListableBeanFactory.class, beanFactory.getClass(),
"a ConfigurableListableBeanFactory is required");
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
@Override
public void afterPropertiesSet() {
Assert.notNull(beanFactory, "BeanFactory must not be null");
postProcessors.put(OnTransition.class, new StateMachineActivatorAnnotationPostProcessor(beanFactory));
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
Assert.notNull(beanFactory, "BeanFactory must not be null");
final Class<?> beanClass = getBeanClass(bean);
if (!isStereotype(beanClass)) {
// we only post-process stereotype components
return bean;
}
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@SuppressWarnings({ "unchecked", "rawtypes" })
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
for (Annotation annotation : annotations) {
MethodAnnotationPostProcessor postProcessor = postProcessors.get(annotation.annotationType());
if (postProcessor != null && shouldCreateHandler(annotation)) {
Object result = postProcessor.postProcess(bean, beanName, method, annotation);
if (result != null && result instanceof StateMachineHandler) {
String endpointBeanName = generateBeanName(beanName, method, annotation.annotationType());
if (result instanceof BeanNameAware) {
((BeanNameAware) result).setBeanName(endpointBeanName);
}
beanFactory.registerSingleton(endpointBeanName, result);
if (result instanceof BeanFactoryAware) {
((BeanFactoryAware) result).setBeanFactory(beanFactory);
}
if (result instanceof InitializingBean) {
try {
((InitializingBean) result).afterPropertiesSet();
} catch (Exception e) {
throw new BeanInitializationException("failed to initialize annotated component", e);
}
}
if (result instanceof Lifecycle) {
lifecycles.add((Lifecycle) result);
if (result instanceof SmartLifecycle && ((SmartLifecycle) result).isAutoStartup()) {
((SmartLifecycle) result).start();
}
}
if (result instanceof ApplicationListener) {
listeners.add((ApplicationListener) result);
}
}
}
}
}
});
return bean;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
for (ApplicationListener<ApplicationEvent> listener : listeners) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException e) {
if (log.isWarnEnabled() && event != null) {
log.warn("ApplicationEvent of type [" + event.getClass()
+ "] not accepted by ApplicationListener [" + listener + "]");
}
}
}
}
@Override
public boolean isRunning() {
return this.running;
}
@Override
public void start() {
for (Lifecycle lifecycle : this.lifecycles) {
if (!lifecycle.isRunning()) {
lifecycle.start();
}
}
this.running = true;
}
@Override
public void stop() {
for (Lifecycle lifecycle : this.lifecycles) {
if (lifecycle.isRunning()) {
lifecycle.stop();
}
}
this.running = false;
}
private boolean shouldCreateHandler(Annotation annotation) {
return true;
}
/**
* Gets the bean class. Will check if bean is a proxy and
* find a class from there as target class, otherwise
* we just get bean class.
*
* @param bean the bean
* @return the bean class
*/
private Class<?> getBeanClass(Object bean) {
Class<?> targetClass = AopUtils.getTargetClass(bean);
return (targetClass != null) ? targetClass : bean.getClass();
}
/**
* Checks if class is a stereotype meaning if there is
* a Component annotation present.
*
* @param beanClass the bean class
* @return true, if is stereotype
*/
private boolean isStereotype(Class<?> beanClass) {
List<Annotation> annotations = new ArrayList<Annotation>(Arrays.asList(beanClass.getAnnotations()));
Class<?>[] interfaces = beanClass.getInterfaces();
for (Class<?> iface : interfaces) {
annotations.addAll(Arrays.asList(iface.getAnnotations()));
}
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.equals(Component.class) || annotationType.isAnnotationPresent(Component.class)) {
return true;
}
}
return false;
}
private String generateBeanName(String originalBeanName, Method method, Class<? extends Annotation> annotationType) {
String baseName = originalBeanName + "." + method.getName() + "." + ClassUtils.getShortNameAsProperty(annotationType);
String name = baseName;
int count = 1;
while (beanFactory.containsBean(name)) {
name = baseName + "#" + (++count);
}
return name;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.reflect.Method;
import org.springframework.core.Ordered;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
/**
* Handler for a common object representing something to be run.
* This is usually used when a plain pojo is configured with {@link WithStateMachine}
* and {@link OnTransition} annotations.
*
* @author Janne Valkealahti
*
*/
public class StateMachineHandler implements Ordered {
private final StateMachineRuntimeProcessor<?> processor;
private int order = Ordered.LOWEST_PRECEDENCE;
/**
* Instantiates a new container handler.
*
* @param target the target bean
*/
// public StateMachineHandler(Object target) {
// this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, OnTransition.class));
// }
/**
* Instantiates a new container handler.
*
* @param target the target bean
* @param method the method
*/
public StateMachineHandler(Object target, Method method) {
this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, method));
}
/**
* Instantiates a new container handler.
*
* @param target the target bean
* @param methodName the method name
*/
public StateMachineHandler(Object target, String methodName) {
this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, methodName));
}
/**
* Instantiates a new container handler.
*
* @param <T> the generic type
* @param processor the processor
*/
public <T> StateMachineHandler(MethodInvokingStateMachineRuntimeProcessor<T> processor) {
this.processor = processor;
}
@Override
public int getOrder() {
return order;
}
/**
* Sets the order used get value from {@link #getOrder()}.
* Default value is {@link Ordered#LOWEST_PRECEDENCE}.
*
* @param order the new order
*/
public void setOrder(int order) {
this.order = order;
}
/**
* Handle container using a {@link StateMachineRuntimeProcessor}.
*
* @param stateMachineRuntime the state machine runtime
* @return the result value
*/
public Object handle(StateMachineRuntime stateMachineRuntime) {
return processor.process(stateMachineRuntime);
}
}

View File

@@ -0,0 +1,553 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.support.AbstractExpressionEvaluator;
import org.springframework.statemachine.support.AnnotatedMethodFilter;
import org.springframework.statemachine.support.FixedMethodFilter;
import org.springframework.statemachine.support.UniqueMethodFilter;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* A helper class using spel to execute target methods.
*
* @author Janne Valkealahti
*
* @param <T> the return type
*/
public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvaluator {
private static final String CANDIDATE_METHODS = "CANDIDATE_METHODS";
private static final String CANDIDATE_MESSAGE_METHODS = "CANDIDATE_MESSAGE_METHODS";
private final Log logger = LogFactory.getLog(this.getClass());
private final Object targetObject;
private volatile String displayString;
private volatile boolean requiresReply;
private final Map<Class<?>, HandlerMethod> handlerMethods;
private final Map<Class<?>, HandlerMethod> handlerMessageMethods;
private final LinkedList<Map<Class<?>, HandlerMethod>> handlerMethodsList;
private final HandlerMethod handlerMethod;
private final Class<?> expectedType;
public StateMachineMethodInvokerHelper(Object targetObject, Method method) {
this(targetObject, method, null);
}
public StateMachineMethodInvokerHelper(Object targetObject, Method method, Class<?> expectedType) {
this(targetObject, null, method, expectedType);
}
public StateMachineMethodInvokerHelper(Object targetObject, String methodName) {
this(targetObject, methodName, null);
}
public StateMachineMethodInvokerHelper(Object targetObject, String methodName, Class<?> expectedType) {
this(targetObject, null, methodName, expectedType);
}
public StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType) {
this(targetObject, annotationType, null);
}
public StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType,
Class<?> expectedType) {
this(targetObject, annotationType, (String) null, expectedType);
}
public T process(StateMachineRuntime stateMachineRuntime) throws Exception {
ParametersWrapper wrapper = new ParametersWrapper(stateMachineRuntime.getStateContext());
return processInternal(wrapper);
}
@Override
public String toString() {
return this.displayString;
}
private StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType,
Method method, Class<?> expectedType) {
Assert.notNull(method, "method must not be null");
this.expectedType = expectedType;
this.requiresReply = expectedType != null;
if (expectedType != null) {
Assert.isTrue(method.getReturnType() != Void.class && method.getReturnType() != Void.TYPE,
"method must have a return type");
}
Assert.notNull(targetObject, "targetObject must not be null");
this.targetObject = targetObject;
this.handlerMethod = new HandlerMethod(method);
this.handlerMethods = null;
this.handlerMessageMethods = null;
this.handlerMethodsList = null;
this.prepareEvaluationContext(this.getEvaluationContext(false), method, annotationType);
this.setDisplayString(targetObject, method);
}
private StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType,
String methodName, Class<?> expectedType) {
Assert.notNull(targetObject, "targetObject must not be null");
this.expectedType = expectedType;
this.targetObject = targetObject;
this.requiresReply = expectedType != null;
Map<String, Map<Class<?>, HandlerMethod>> handlerMethodsForTarget = this.findHandlerMethodsForTarget(
targetObject, annotationType, methodName, requiresReply);
Map<Class<?>, HandlerMethod> handlerMethods = handlerMethodsForTarget.get(CANDIDATE_METHODS);
Map<Class<?>, HandlerMethod> handlerMessageMethods = handlerMethodsForTarget.get(CANDIDATE_MESSAGE_METHODS);
if ((handlerMethods.size() == 1 && handlerMessageMethods.isEmpty())
|| (handlerMessageMethods.size() == 1 && handlerMethods.isEmpty())) {
if (handlerMethods.size() == 1) {
this.handlerMethod = handlerMethods.values().iterator().next();
} else {
this.handlerMethod = handlerMessageMethods.values().iterator().next();
}
this.handlerMethods = null;
this.handlerMessageMethods = null;
this.handlerMethodsList = null;
} else {
this.handlerMethod = null;
this.handlerMethods = handlerMethods;
this.handlerMessageMethods = handlerMessageMethods;
this.handlerMethodsList = new LinkedList<Map<Class<?>, HandlerMethod>>();
// TODO Consider to use global option to determine a precedence of
// methods
this.handlerMethodsList.add(this.handlerMethods);
this.handlerMethodsList.add(this.handlerMessageMethods);
}
this.prepareEvaluationContext(this.getEvaluationContext(false), methodName, annotationType);
this.setDisplayString(targetObject, methodName);
}
private void setDisplayString(Object targetObject, Object targetMethod) {
StringBuilder sb = new StringBuilder(targetObject.getClass().getName());
if (targetMethod instanceof Method) {
sb.append("." + ((Method) targetMethod).getName());
} else if (targetMethod instanceof String) {
sb.append("." + targetMethod);
}
this.displayString = sb.toString() + "]";
}
private void prepareEvaluationContext(StandardEvaluationContext context, Object method,
Class<? extends Annotation> annotationType) {
Class<?> targetType = AopUtils.getTargetClass(this.targetObject);
if (method instanceof Method) {
context.registerMethodFilter(targetType, new FixedMethodFilter((Method) method));
if (expectedType != null) {
Assert.state(
context.getTypeConverter().canConvert(
TypeDescriptor.valueOf(((Method) method).getReturnType()),
TypeDescriptor.valueOf(expectedType)), "Cannot convert to expected type ("
+ expectedType + ") from " + method);
}
} else if (method == null || method instanceof String) {
AnnotatedMethodFilter filter = new AnnotatedMethodFilter(annotationType, (String) method,
this.requiresReply);
Assert.state(canReturnExpectedType(filter, targetType, context.getTypeConverter()),
"Cannot convert to expected type (" + expectedType + ") from " + method);
context.registerMethodFilter(targetType, filter);
}
context.setVariable("target", targetObject);
}
private boolean canReturnExpectedType(AnnotatedMethodFilter filter, Class<?> targetType, TypeConverter typeConverter) {
if (expectedType == null) {
return true;
}
List<Method> methods = filter.filter(Arrays.asList(ReflectionUtils.getAllDeclaredMethods(targetType)));
for (Method method : methods) {
if (typeConverter.canConvert(TypeDescriptor.valueOf(method.getReturnType()), TypeDescriptor.valueOf(expectedType))) {
return true;
}
}
return false;
}
private T processInternal(ParametersWrapper parameters) throws Exception {
HandlerMethod candidate = this.findHandlerMethodForParameters(parameters);
Assert.notNull(candidate, "No candidate methods found for messages.");
Expression expression = candidate.getExpression();
Class<?> expectedType = this.expectedType != null ? this.expectedType : candidate.method.getReturnType();
try {
@SuppressWarnings("unchecked")
T result = (T) this.evaluateExpression(expression, parameters, expectedType);
if (this.requiresReply) {
Assert.notNull(result, "Expression evaluation result was null, but this processor requires a reply.");
}
return result;
} catch (Exception e) {
Throwable evaluationException = e;
if (e instanceof EvaluationException && e.getCause() != null) {
evaluationException = e.getCause();
}
if (evaluationException instanceof Exception) {
throw (Exception) evaluationException;
} else {
throw new IllegalStateException("Cannot process message", evaluationException);
}
}
}
private Map<String, Map<Class<?>, HandlerMethod>> findHandlerMethodsForTarget(final Object targetObject,
final Class<? extends Annotation> annotationType, final String methodName, final boolean requiresReply) {
Map<String, Map<Class<?>, HandlerMethod>> handlerMethods = new HashMap<String, Map<Class<?>, HandlerMethod>>();
final Map<Class<?>, HandlerMethod> candidateMethods = new HashMap<Class<?>, HandlerMethod>();
final Map<Class<?>, HandlerMethod> candidateMessageMethods = new HashMap<Class<?>, HandlerMethod>();
final Class<?> targetClass = this.getTargetClass(targetObject);
MethodFilter methodFilter = new UniqueMethodFilter(targetClass);
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
boolean matchesAnnotation = false;
if (method.isBridge()) {
return;
}
if (isMethodDefinedOnObjectClass(method)) {
return;
}
if (method.getDeclaringClass().equals(Proxy.class)) {
return;
}
if (!Modifier.isPublic(method.getModifiers())) {
return;
}
if (requiresReply && void.class.equals(method.getReturnType())) {
return;
}
if (methodName != null && !methodName.equals(method.getName())) {
return;
}
if (annotationType != null && AnnotationUtils.findAnnotation(method, annotationType) != null) {
matchesAnnotation = true;
}
HandlerMethod handlerMethod = null;
try {
handlerMethod = new HandlerMethod(method);
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Method [" + method + "] is not eligible for container handling.", e);
}
return;
}
Class<?> targetParameterType = handlerMethod.getTargetParameterType();
if (matchesAnnotation || annotationType == null) {
if (handlerMethod.isMessageMethod()) {
if (candidateMessageMethods.containsKey(targetParameterType)) {
throw new IllegalArgumentException("Found more than one method match for type " +
"[Message<" + targetParameterType + ">]");
}
candidateMessageMethods.put(targetParameterType, handlerMethod);
} else {
if (candidateMethods.containsKey(targetParameterType)) {
String exceptionMessage = "Found more than one method match for ";
if (Void.class.equals(targetParameterType)) {
exceptionMessage += "empty parameter for 'payload'";
} else {
exceptionMessage += "type [" + targetParameterType + "]";
}
throw new IllegalArgumentException(exceptionMessage);
}
candidateMethods.put(targetParameterType, handlerMethod);
}
}
}
}, methodFilter);
if (!candidateMethods.isEmpty() || !candidateMessageMethods.isEmpty()) {
handlerMethods.put(CANDIDATE_METHODS, candidateMethods);
handlerMethods.put(CANDIDATE_MESSAGE_METHODS, candidateMessageMethods);
return handlerMethods;
}
Assert.state(!handlerMethods.isEmpty(), "Target object of type [" + this.targetObject.getClass()
+ "] has no eligible methods for handling Container.");
return handlerMethods;
}
private Class<?> getTargetClass(Object targetObject) {
Class<?> targetClass = targetObject.getClass();
if (AopUtils.isAopProxy(targetObject)) {
targetClass = AopUtils.getTargetClass(targetObject);
if (targetClass == targetObject.getClass()) {
try {
// Maybe a proxy with no target - e.g. gateway
Class<?>[] interfaces = ((Advised) targetObject).getProxiedInterfaces();
if (interfaces != null && interfaces.length == 1) {
targetClass = interfaces[0];
}
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Exception trying to extract interface", e);
}
}
}
}
else if (org.springframework.util.ClassUtils.isCglibProxyClass(targetClass)) {
Class<?> superClass = targetObject.getClass().getSuperclass();
if (!Object.class.equals(superClass)) {
targetClass = superClass;
}
}
return targetClass;
}
private HandlerMethod findHandlerMethodForParameters(ParametersWrapper parameters) {
if (this.handlerMethod != null) {
return this.handlerMethod;
} else {
return this.handlerMethods.get(Void.class);
}
}
private static boolean isMethodDefinedOnObjectClass(Method method) {
if (method == null) {
return false;
}
if (method.getDeclaringClass().equals(Object.class)) {
return true;
}
if (ReflectionUtils.isEqualsMethod(method) || ReflectionUtils.isHashCodeMethod(method)
|| ReflectionUtils.isToStringMethod(method) || AopUtils.isFinalizeMethod(method)) {
return true;
}
return (method.getName().equals("clone") && method.getParameterTypes().length == 0);
}
/**
* Helper class for generating and exposing metadata for a candidate handler method. The metadata includes the SpEL
* expression and the expected payload type.
*/
private static class HandlerMethod {
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
private final Method method;
private final Expression expression;
private volatile TypeDescriptor targetParameterTypeDescriptor;
private volatile Class<?> targetParameterType = Void.class;
private volatile boolean messageMethod;
HandlerMethod(Method method) {
this.method = method;
this.expression = this.generateExpression(method);
}
Expression getExpression() {
return this.expression;
}
Class<?> getTargetParameterType() {
return this.targetParameterType;
}
private boolean isMessageMethod() {
return messageMethod;
}
@Override
public String toString() {
return this.method.toString();
}
private Expression generateExpression(Method method) {
StringBuilder sb = new StringBuilder("#target." + method.getName() + "(");
Class<?>[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
boolean hasUnqualifiedMapParameter = false;
for (int i = 0; i < parameterTypes.length; i++) {
if (i != 0) {
sb.append(", ");
}
MethodParameter methodParameter = new MethodParameter(method, i);
TypeDescriptor parameterTypeDescriptor = new TypeDescriptor(methodParameter);
Class<?> parameterType = parameterTypeDescriptor.getObjectType();
Annotation mappingAnnotation = findMappingAnnotation(parameterAnnotations[i]);
if (mappingAnnotation != null) {
Class<? extends Annotation> annotationType = mappingAnnotation.annotationType();
// if (annotationType.equals(YarnEnvironments.class)) {
// sb.append("environment");
// } else if (annotationType.equals(YarnEnvironment.class)) {
// YarnEnvironment headerAnnotation = (YarnEnvironment) mappingAnnotation;
// sb.append(this.determineEnvironmentExpression(headerAnnotation, methodParameter));
// } else if (annotationType.equals(YarnParameters.class)) {
// Assert.isTrue(Map.class.isAssignableFrom(parameterType),
// "The @YarnParameters annotation can only be applied to a Map-typed parameter.");
// sb.append("parameters");
// } else if (annotationType.equals(YarnParameter.class)) {
// YarnParameter headerAnnotation = (YarnParameter) mappingAnnotation;
// sb.append(this.determineParameterExpression(headerAnnotation, methodParameter));
// }
}
}
if (hasUnqualifiedMapParameter) {
if (targetParameterType != null && Map.class.isAssignableFrom(this.targetParameterType)) {
throw new IllegalArgumentException(
"Unable to determine payload matching parameter due to ambiguous Map typed parameters. "
+ "Consider adding the @Payload and or @Headers annotations as appropriate.");
}
}
sb.append(")");
if (this.targetParameterTypeDescriptor == null) {
this.targetParameterTypeDescriptor = TypeDescriptor.valueOf(Void.class);
}
return EXPRESSION_PARSER.parseExpression(sb.toString());
}
private Annotation findMappingAnnotation(Annotation[] annotations) {
if (annotations == null || annotations.length == 0) {
return null;
}
Annotation match = null;
for (Annotation annotation : annotations) {
Class<? extends Annotation> type = annotation.annotationType();
// if (type.equals(YarnParameters.class) || type.equals(YarnParameter.class)
// || type.equals(YarnEnvironments.class) || type.equals(YarnEnvironment.class)) {
// if (match != null) {
// throw new IllegalArgumentException(
// "At most one parameter annotation can be provided for message mapping, "
// + "but found two: [" + match.annotationType().getName() + "] and ["
// + annotation.annotationType().getName() + "]");
// }
// match = annotation;
// }
}
return match;
}
// private String determineParameterExpression(YarnParameter parameterAnnotation, MethodParameter methodParameter) {
// methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
// String headerName = null;
// String relativeExpression = "";
// String valueAttribute = parameterAnnotation.value();
// if (!StringUtils.hasText(valueAttribute)) {
// headerName = methodParameter.getParameterName();
// } else if (valueAttribute.indexOf('.') != -1) {
// String tokens[] = valueAttribute.split("\\.", 2);
// headerName = tokens[0];
// if (StringUtils.hasText(tokens[1])) {
// relativeExpression = "." + tokens[1];
// }
// } else {
// headerName = valueAttribute;
// }
// Assert.notNull(headerName, "Cannot determine parameter name. Possible reasons: -debug is "
// + "disabled or header name is not explicitly provided via @YarnParameter annotation.");
// String headerRetrievalExpression = "parameters['" + headerName + "']";
// String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
// String fallbackExpression = (parameterAnnotation.required()) ? "T(org.springframework.util.Assert).isTrue(false, 'required parameter not available: "
// + headerName + "')"
// : "null";
// return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
// }
// private String determineEnvironmentExpression(YarnEnvironment environmentAnnotation, MethodParameter methodParameter) {
// methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
// String headerName = null;
// String relativeExpression = "";
// String valueAttribute = environmentAnnotation.value();
// if (!StringUtils.hasText(valueAttribute)) {
// headerName = methodParameter.getParameterName();
// } else if (valueAttribute.indexOf('.') != -1) {
// String tokens[] = valueAttribute.split("\\.", 2);
// headerName = tokens[0];
// if (StringUtils.hasText(tokens[1])) {
// relativeExpression = "." + tokens[1];
// }
// } else {
// headerName = valueAttribute;
// }
// Assert.notNull(headerName, "Cannot determine parameter name. Possible reasons: -debug is "
// + "disabled or header name is not explicitly provided via @YarnEnvironment annotation.");
// String headerRetrievalExpression = "environment['" + headerName + "']";
// String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
// String fallbackExpression = (environmentAnnotation.required()) ? "T(org.springframework.util.Assert).isTrue(false, 'required parameter not available: "
// + headerName + "')"
// : "null";
// return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
// }
}
/**
* Wrapping everything we need to work with spel.
*/
public class ParametersWrapper {
private final StateContext stateContext;
public ParametersWrapper(StateContext stateContext) {
this.stateContext = stateContext;
}
public StateContext getStateContext() {
return stateContext;
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2015 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.statemachine.processor;
import java.lang.reflect.Method;
import org.springframework.statemachine.annotation.OnTransition;
public class StateMachineOnTransitionHandler extends StateMachineHandler {
private OnTransition annotation;
public StateMachineOnTransitionHandler(Object target, Method method, OnTransition annotation) {
super(target, method);
this.annotation = annotation;
}
public OnTransition getAnnotation() {
return annotation;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2015 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.statemachine.processor;
import org.springframework.statemachine.StateContext;
/**
* A generic runtime representation of a state machine.
*
* @author Janne Valkealahti
*
*/
public interface StateMachineRuntime {
StateContext getStateContext();
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2015 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.statemachine.processor;
/**
* Defines a strategy of processing a state machine and returning
* some Object (or null).
*
* @author Janne Valkealahti
*
* @param <T> type
*/
public interface StateMachineRuntimeProcessor<T> {
/**
* Process the container based on information available
* from {@link StateMachineRuntime}.
*
* @param stateMachineRuntime the yarn container runtime
* @return the result
*/
T process(StateMachineRuntime stateMachineRuntime);
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2015 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.statemachine.state;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
public abstract class AbstractState<S, E> implements State<S, E> {
private S id;
private Collection<E> deferred;
private Collection<Action> entryActions;
private Collection<Action> exitActions;
public AbstractState(S id) {
this(id, null);
}
public AbstractState(S id, Collection<E> deferred) {
this(id, deferred, null, null);
}
public AbstractState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
this.id = id;
this.deferred = deferred;
this.entryActions = entryActions;
this.exitActions = exitActions;
}
@Override
public S getId() {
return id;
}
@Override
public Collection<E> getDeferredEvents() {
return deferred;
}
@Override
public Collection<Action> getEntryActions() {
return entryActions;
}
@Override
public Collection<Action> getExitActions() {
return exitActions;
}
@Override
public String toString() {
return "AbstractState [id=" + id + ", deferred=" + deferred + ", entryActions=" + entryActions
+ ", exitActions=" + exitActions + "]";
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2015 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.statemachine.state;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractState<S, E> {
public EnumState(S id) {
super(id);
}
public EnumState(S id, Collection<E> deferred) {
super(id, deferred);
}
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
super(id, deferred, entryActions, exitActions);
}
@Override
public String toString() {
return "EnumState [getId()=" + getId() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
+ ", toString()=" + super.toString() + "]";
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2015 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.statemachine.state;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
/**
* {@code State} is an interface representing possible state in a state machine.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface State<S, E> {
/**
* Gets the state identifier.
*
* @return the identifier
*/
S getId();
/**
* Gets the deferred events for this state.
*
* @return the state deferred events
*/
Collection<E> getDeferredEvents();
/**
* Gets {@link Action}s executed entering in this state.
*
* @return the state entry actions
*/
Collection<Action> getEntryActions();
/**
* Gets {@link Action}s executed exiting from this state.
*
* @return the state exit actions
*/
Collection<Action> getExitActions();
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2015 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.statemachine.support;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Base class providing common functionality for using Spring expression language.
*
* @author Mark Fisher
* @author Dave Syer
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @author Janne Valkealahti
*
*/
public abstract class AbstractExpressionEvaluator implements BeanFactoryAware, InitializingBean {
private volatile StandardEvaluationContext evaluationContext;
private final ExpressionParser expressionParser = new SpelExpressionParser();
private final BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter();
private volatile BeanFactory beanFactory;
@Override
public void setBeanFactory(final BeanFactory beanFactory) {
if (beanFactory != null) {
this.beanFactory = beanFactory;
this.typeConverter.setBeanFactory(beanFactory);
if (this.evaluationContext != null && this.evaluationContext.getBeanResolver() == null) {
this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
}
}
@Override
public void afterPropertiesSet() throws Exception {
getEvaluationContext();
}
/**
* Sets the conversion service.
*
* @param conversionService the new conversion service
*/
public void setConversionService(ConversionService conversionService) {
if (conversionService != null) {
this.typeConverter.setConversionService(conversionService);
}
}
/**
* Gets the evaluation context.
*
* @return the evaluation context
*/
protected StandardEvaluationContext getEvaluationContext() {
return this.getEvaluationContext(true);
}
/**
* Emits a WARN log if the beanFactory field is null, unless the argument is false.
* @param beanFactoryRequired set to false to suppress the warning.
* @return The evaluation context.
*/
protected final StandardEvaluationContext getEvaluationContext(boolean beanFactoryRequired) {
if (this.evaluationContext == null) {
if (this.beanFactory == null && !beanFactoryRequired) {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext();
}
else {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
}
if (this.typeConverter != null) {
this.evaluationContext.setTypeConverter(this.typeConverter);
}
}
return this.evaluationContext;
}
protected Object evaluateExpression(String expression, Object input) {
return this.evaluateExpression(expression, input, (Class<?>) null);
}
protected <T> T evaluateExpression(String expression, Object input, Class<T> expectedType) {
return this.expressionParser.parseExpression(expression).getValue(this.getEvaluationContext(), input, expectedType);
}
protected Object evaluateExpression(Expression expression, Object input) {
return this.evaluateExpression(expression, input, (Class<?>) null);
}
protected <T> T evaluateExpression(Expression expression, Class<T> expectedType) {
return expression.getValue(this.getEvaluationContext(), expectedType);
}
protected Object evaluateExpression(Expression expression) {
return expression.getValue(this.getEvaluationContext());
}
protected <T> T evaluateExpression(Expression expression, Object input, Class<T> expectedType) {
return expression.getValue(this.getEvaluationContext(), input, expectedType);
}
}

View File

@@ -0,0 +1,326 @@
/*
* Copyright 2015 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.statemachine.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.OrderComparator;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.listener.CompositeStateMachineListener;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.processor.StateMachineHandler;
import org.springframework.statemachine.processor.StateMachineOnTransitionHandler;
import org.springframework.statemachine.processor.StateMachineRuntime;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
import org.springframework.statemachine.transition.TransitionKind;
import org.springframework.statemachine.trigger.Trigger;
import org.springframework.util.Assert;
/**
* Base implementation of a {@link StateMachine} loosely modelled from UML state
* machine.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport implements StateMachine<State<S,E>, E> {
private static final Log log = LogFactory.getLog(AbstractStateMachine.class);
private final Collection<State<S,E>> states;
private final Collection<Transition<S,E>> transitions;
private final State<S,E> initialState;
private final ExtendedState extendedState;
private final Queue<Message<E>> eventQueue = new ConcurrentLinkedQueue<Message<E>>();
private final LinkedList<Message<E>> deferList = new LinkedList<Message<E>>();
private final CompositeStateMachineListener<S, E> stateListener = new CompositeStateMachineListener<S, E>();
private volatile State<S,E> currentState;
private volatile Runnable task;
/**
* Instantiates a new abstract state machine.
*
* @param states the states of this machine
* @param transitions the transitions of this machine
* @param initialState the initial state of this machine
*/
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState) {
this(states, transitions, initialState, new DefaultExtendedState());
}
/**
* Instantiates a new abstract state machine.
*
* @param states the states of this machine
* @param transitions the transitions of this machine
* @param initialState the initial state of this machine
* @param extendedState the extended state of this machine
*/
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState, ExtendedState extendedState) {
super();
this.states = states;
this.transitions = transitions;
this.initialState = initialState;
this.extendedState = extendedState;
}
@Override
public State<S,E> getState() {
return currentState;
}
@Override
public State<S,E> getInitialState() {
return initialState;
}
@Override
public void sendEvent(Message<E> event) {
// TODO: machine header looks weird!
event = MessageBuilder.fromMessage(event).setHeader("machine", this).build();
if (log.isDebugEnabled()) {
log.debug("Queue event " + event);
}
eventQueue.add(event);
scheduleEventQueueProcessing();
}
@Override
public void sendEvent(E event) {
sendEvent(MessageBuilder.withPayload(event).build());
}
@Override
protected void doStart() {
super.doStart();
switchToState(initialState, null);
}
@Override
public void addStateListener(StateMachineListener<State<S, E>, E> listener) {
stateListener.register(listener);
}
/**
* Gets the {@link State}s defined in this machine. Returned collection is
* an unmodifiable copy because states in a state machine are immutable.
*
* @return immutable copy of existing states
*/
public Collection<State<S,E>> getStates() {
return Collections.unmodifiableCollection(states);
}
private void switchToState(State<S,E> state, Message<E> event) {
log.info("Moving into state=" + state + " from " + currentState);
exitFromState(currentState, event);
stateListener.stateChanged(currentState, state);
callHandlers(currentState, state, event);
currentState = state;
entryToState(state, event);
for (Transition<S,E> transition : transitions) {
State<S,E> source = transition.getSource();
State<S,E> target = transition.getTarget();
if (transition.getTrigger() == null && source.equals(currentState)) {
switchToState(target, event);
}
}
}
private void exitFromState(State<S, E> state, Message<E> event) {
if (state != null) {
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
Collection<Action> actions = state.getExitActions();
if (actions != null) {
for (Action action : actions) {
action.execute(new DefaultStateContext(messageHeaders, extendedState));
}
}
}
}
private void entryToState(State<S,E> state, Message<E> event) {
if (state != null) {
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
Collection<Action> actions = state.getEntryActions();
if (actions != null) {
for (Action action : actions) {
action.execute(new DefaultStateContext(messageHeaders, extendedState));
}
}
}
}
private void processEventQueue() {
log.debug("Process event queue");
Message<E> queuedEvent = null;
while ((queuedEvent = eventQueue.poll()) != null) {
Message<E> defer = null;
for (Transition<S,E> transition : transitions) {
State<S,E> source = transition.getSource();
State<S,E> target = transition.getTarget();
Trigger<S, E> trigger = transition.getTrigger();
if (source.equals(currentState)) {
if (trigger != null && trigger.evaluate(queuedEvent.getPayload())) {
boolean transit = transition.transit(new DefaultStateContext(queuedEvent.getHeaders(), extendedState));
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
switchToState(target, queuedEvent);
}
break;
} else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(queuedEvent.getPayload())) {
defer = queuedEvent;
}
}
}
if (defer != null) {
log.info("Deferring event " + defer);
deferList.addLast(defer);
}
}
}
private void processDeferList() {
log.debug("Process defer list");
ListIterator<Message<E>> iterator = deferList.listIterator();
while (iterator.hasNext()) {
Message<E> event = iterator.next();
for (Transition<S,E> transition : transitions) {
State<S,E> source = transition.getSource();
State<S,E> target = transition.getTarget();
Trigger<S, E> trigger = transition.getTrigger();
if (source.equals(currentState)) {
if (trigger != null && trigger.evaluate(event.getPayload())) {
boolean transit = transition.transit(new DefaultStateContext(event.getHeaders(), extendedState));
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
switchToState(target, event);
}
iterator.remove();
}
}
}
}
}
private void scheduleEventQueueProcessing() {
if (task == null) {
task = new Runnable() {
@Override
public void run() {
processEventQueue();
processDeferList();
task = null;
}
};
getTaskExecutor().execute(task);
}
}
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> event) {
if (sourceState != null && targetState != null) {
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState);
getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext);
}
}
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler> stateMachineHandlers, final StateContext stateContext) {
StateMachineRuntime runtime = new StateMachineRuntime() {
@Override
public StateContext getStateContext() {
return stateContext;
}
};
List<Object> results = new ArrayList<Object>();
for (StateMachineHandler handler : stateMachineHandlers) {
results.add(handler.handle(runtime));
}
return results;
}
private List<StateMachineHandler> getStateMachineHandlers(State<S,E> sourceState, State<S,E> targetState) {
BeanFactory beanFactory = getBeanFactory();
// TODO think how to handle null bf
if (beanFactory == null) {
return Collections.emptyList();
}
Assert.state(beanFactory instanceof ListableBeanFactory, "Bean factory must be instance of ListableBeanFactory");
Map<String, StateMachineOnTransitionHandler> handlers = ((ListableBeanFactory) beanFactory)
.getBeansOfType(StateMachineOnTransitionHandler.class);
List<StateMachineHandler> handlersList = new ArrayList<StateMachineHandler>();
for (Entry<String, StateMachineOnTransitionHandler> entry : handlers.entrySet()) {
OnTransition annotation = entry.getValue().getAnnotation();
String source = annotation.source();
String target = annotation.target();
String s = sourceState.getId().toString();
String t = targetState.getId().toString();
if (s.equals(source) && t.equals(target)) {
handlersList.add(entry.getValue());
}
}
OrderComparator comparator = new OrderComparator();
Collections.sort(handlersList, comparator);
return handlersList;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2015 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.statemachine.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.MethodFilter;
import org.springframework.util.StringUtils;
/**
* A MethodFilter implementation that enables the following:
* <ol>
* <li>matching on method name, if available</li>
* <li>exclusion of void-returning methods if 'requiresReply' is true</li>
* <li>limiting to annotated methods if at least one is present</li>
* </ol>
* <p>
*
* @author Mark Fisher
* @author Janne Valkealahti
*/
public class AnnotatedMethodFilter implements MethodFilter {
private final Class<? extends Annotation> annotationType;
private final String methodName;
private final boolean requiresReply;
public AnnotatedMethodFilter(Class<? extends Annotation> annotationType, String methodName, boolean requiresReply) {
this.annotationType = annotationType;
this.methodName = methodName;
this.requiresReply = requiresReply;
}
public List<Method> filter(List<Method> methods) {
List<Method> annotatedCandidates = new ArrayList<Method>();
List<Method> fallbackCandidates = new ArrayList<Method>();
for (Method method : methods) {
if (method.isBridge()) {
continue;
}
if (this.requiresReply && method.getReturnType().equals(void.class)) {
continue;
}
if (StringUtils.hasText(this.methodName) && !this.methodName.equals(method.getName())) {
continue;
}
if (this.annotationType != null && AnnotationUtils.findAnnotation(method, this.annotationType) != null) {
annotatedCandidates.add(method);
} else {
fallbackCandidates.add(method);
}
}
return (!annotatedCandidates.isEmpty()) ? annotatedCandidates : fallbackCandidates;
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2015 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.statemachine.support;
import java.beans.PropertyEditor;
import org.springframework.beans.BeansException;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.TypeConverter;
import org.springframework.util.ClassUtils;
/**
* @author Dave Syer
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Soby Chacko
* @author Janne Valkealahti
*
*/
public class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
private static ConversionService defaultConversionService;
private volatile SimpleTypeConverter delegate = new SimpleTypeConverter();
private volatile boolean haveCalledDelegateGetDefaultEditor;
private volatile ConversionService conversionService;
/**
* Instantiates a new bean factory type converter.
*/
public BeanFactoryTypeConverter() {
synchronized (BeanFactoryTypeConverter.class) {
if (defaultConversionService == null) {
defaultConversionService = new DefaultConversionService();
}
}
this.conversionService = defaultConversionService;
}
public BeanFactoryTypeConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableBeanFactory) {
Object typeConverter = ((ConfigurableBeanFactory) beanFactory).getTypeConverter();
if (typeConverter instanceof SimpleTypeConverter) {
delegate = (SimpleTypeConverter) typeConverter;
}
}
}
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
if (conversionService.canConvert(sourceType, targetType)) {
return true;
}
if (!String.class.isAssignableFrom(sourceType) && !String.class.isAssignableFrom(targetType)) {
// PropertyEditor cannot convert non-Strings
return false;
}
if (!String.class.isAssignableFrom(sourceType)) {
return delegate.findCustomEditor(sourceType, null) != null || this.getDefaultEditor(sourceType) != null;
}
return delegate.findCustomEditor(targetType, null) != null || this.getDefaultEditor(targetType) != null;
}
public boolean canConvert(TypeDescriptor sourceTypeDescriptor, TypeDescriptor targetTypeDescriptor) {
if (conversionService.canConvert(sourceTypeDescriptor, targetTypeDescriptor)) {
return true;
}
// TODO: what does this mean? This method is not used in SpEL so
// probably ignorable?
Class<?> sourceType = sourceTypeDescriptor.getObjectType();
Class<?> targetType = targetTypeDescriptor.getObjectType();
return canConvert(sourceType, targetType);
}
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
// Echoes
// org.springframework.expression.common.ExpressionUtils.convertTypedValue()
if ((targetType.getType() == Void.class || targetType.getType() == Void.TYPE) && value == null) {
return null;
}
if (sourceType != null) {
Class<?> sourceClass = sourceType.getType();
//Class<?> targetClass = targetType.getType();
if (sourceType.isAssignableTo(targetType) && ClassUtils.isPrimitiveArray(sourceClass)) {
return value;
}
}
if (conversionService.canConvert(sourceType, targetType)) {
return conversionService.convert(value, sourceType, targetType);
}
if (!String.class.isAssignableFrom(sourceType.getType())) {
PropertyEditor editor = delegate.findCustomEditor(sourceType.getType(), null);
if (editor == null) {
editor = this.getDefaultEditor(sourceType.getType());
}
if (editor != null) { // INT-1441
String text = null;
synchronized (editor) {
editor.setValue(value);
text = editor.getAsText();
}
if (String.class.isAssignableFrom(targetType.getType())) {
return text;
}
return convertValue(text, TypeDescriptor.valueOf(String.class), targetType);
}
}
return delegate.convertIfNecessary(value, targetType.getType());
}
private PropertyEditor getDefaultEditor(Class<?> sourceType) {
PropertyEditor defaultEditor;
if (this.haveCalledDelegateGetDefaultEditor) {
defaultEditor = delegate.getDefaultEditor(sourceType);
} else {
synchronized (this) {
// not thread-safe - it builds the defaultEditors field in-place
// (SPR-10191)
defaultEditor = delegate.getDefaultEditor(sourceType);
}
this.haveCalledDelegateGetDefaultEditor = true;
}
return defaultEditor;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2015 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.statemachine.support;
import java.util.HashMap;
import java.util.Map;
import org.springframework.statemachine.ExtendedState;
/**
* Default implementation of a {@link ExtendedState}.
*
* @author Janne Valkealahti
*
*/
public class DefaultExtendedState implements ExtendedState {
private final Map<String, Object> variables;
/**
* Instantiates a new default extended state.
*/
public DefaultExtendedState() {
this.variables = new HashMap<String, Object>();
}
@Override
public Map<String, Object> getVariables() {
return variables;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2015 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.statemachine.support;
import org.springframework.messaging.MessageHeaders;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateContext;
public class DefaultStateContext implements StateContext {
private final MessageHeaders messageHeaders;
private final ExtendedState extendedState;
public DefaultStateContext(MessageHeaders messageHeaders, ExtendedState extendedState) {
this.messageHeaders = messageHeaders;
this.extendedState = extendedState;
}
@Override
public MessageHeaders getMessageHeaders() {
return messageHeaders;
}
@Override
public ExtendedState getExtendedState() {
return extendedState;
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2015 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.statemachine.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
/**
* Utility class with static methods for helping with establishing environments for
* SpEL expressions.
*
* @author Gary Russell
* @author Oleg Zhurakousky
* @author Artem Bilan
*/
public abstract class ExpressionUtils {
private static final Log logger = LogFactory.getLog(ExpressionUtils.class);
/**
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
* property accessor property and the supplied {@link ConversionService} in its
* conversionService property.
*
* @param conversionService the conversion service.
* @return the evaluation context.
*/
private static StandardEvaluationContext createStandardEvaluationContext(ConversionService conversionService,
BeanFactory beanFactory) {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.addPropertyAccessor(new MapAccessor());
if (conversionService != null) {
evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService));
}
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
/**
* Used to create a context with no BeanFactory, usually in tests.
* @return The evaluation context.
*/
public static StandardEvaluationContext createStandardEvaluationContext() {
return doCreateContext(null);
}
/**
* Obtains the context from the beanFactory if not null; emits a warning if the beanFactory
* is null.
* @param beanFactory The bean factory.
* @return The evaluation context.
*/
public static StandardEvaluationContext createStandardEvaluationContext(BeanFactory beanFactory) {
if (beanFactory == null) {
logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanfactory"));
}
return doCreateContext(beanFactory);
}
private static StandardEvaluationContext doCreateContext(BeanFactory beanFactory) {
ConversionService conversionService = null;
StandardEvaluationContext evaluationContext = null;
if (beanFactory != null) {
evaluationContext = StateMachineContextUtils.getEvaluationContext(beanFactory);
}
if (evaluationContext == null) {
if (beanFactory != null) {
conversionService = StateMachineContextUtils.getConversionService(beanFactory);
}
evaluationContext = createStandardEvaluationContext(conversionService, beanFactory);
}
return evaluationContext;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2015 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.statemachine.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.expression.MethodFilter;
import org.springframework.util.Assert;
/**
* A {@link MethodFilter} implementation that will always return the same Method
* instance within a single-element list if it is present in the candidate list.
* If the Method is not present in the candidate list, it will return an empty
* list.
*
* @author Mark Fisher
* @author Gary Russell
* @author Janne Valkealahti
*/
public class FixedMethodFilter implements MethodFilter {
private final Method method;
public FixedMethodFilter(Method method) {
Assert.notNull(method, "method must not be null");
this.method = method;
}
public List<Method> filter(List<Method> methods) {
if (methods != null && methods.contains(this.method)) {
List<Method> filteredList = new ArrayList<Method>(1);
filteredList.add(this.method);
return filteredList;
}
return Collections.<Method> emptyList();
}
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright 2015 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.statemachine.support;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
/**
* Convenient base class for object which needs spring task scheduler, task
* executor and life cycle handling.
*
* @author Janne Valkealahti
*
*/
public abstract class LifecycleObjectSupport implements InitializingBean, SmartLifecycle, BeanFactoryAware {
private static final Log log = LogFactory.getLog(LifecycleObjectSupport.class);
// fields for lifecycle
private volatile boolean autoStartup = true;
private volatile int phase = 0;
private volatile boolean running;
// lock to protect lifycycle methods
private final ReentrantLock lifecycleLock = new ReentrantLock();
// common task handling
private TaskScheduler taskScheduler;
private TaskExecutor taskExecutor;
// to access bean factory
private volatile BeanFactory beanFactory;
@Override
public final void afterPropertiesSet() {
try {
this.onInit();
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new BeanInitializationException("failed to initialize", e);
}
}
@Override
public final void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.notNull(beanFactory, "beanFactory must not be null");
if(log.isDebugEnabled()) {
log.debug("Setting bean factory: " + beanFactory + " for " + this);
}
this.beanFactory = beanFactory;
}
@Override
public final boolean isAutoStartup() {
return this.autoStartup;
}
@Override
public final int getPhase() {
return this.phase;
}
@Override
public final boolean isRunning() {
this.lifecycleLock.lock();
try {
return this.running;
} finally {
this.lifecycleLock.unlock();
}
}
@Override
public final void start() {
this.lifecycleLock.lock();
try {
if (!this.running) {
this.doStart();
this.running = true;
if (log.isInfoEnabled()) {
log.info("started " + this);
} else {
if(log.isDebugEnabled()) {
log.debug("already started " + this);
}
}
}
} finally {
this.lifecycleLock.unlock();
}
}
@Override
public final void stop() {
this.lifecycleLock.lock();
try {
if (this.running) {
this.doStop();
this.running = false;
if (log.isInfoEnabled()) {
log.info("stopped " + this);
}
} else {
if (log.isDebugEnabled()) {
log.debug("already stopped " + this);
}
}
} finally {
this.lifecycleLock.unlock();
}
}
@Override
public final void stop(Runnable callback) {
this.lifecycleLock.lock();
try {
this.stop();
callback.run();
} finally {
this.lifecycleLock.unlock();
}
}
/**
* Sets the auto startup.
*
* @param autoStartup the new auto startup
* @see SmartLifecycle
*/
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
/**
* Sets the phase.
*
* @param phase the new phase
* @see SmartLifecycle
*/
public void setPhase(int phase) {
this.phase = phase;
}
/**
* Gets the {@link BeanFactory} for this instance.
*
* @return the bean factory.
*/
protected final BeanFactory getBeanFactory() {
return beanFactory;
}
/**
* Sets the used {@link TaskScheduler}.
*
* @param taskScheduler the task scheduler
*/
public void setTaskScheduler(TaskScheduler taskScheduler) {
Assert.notNull(taskScheduler, "taskScheduler must not be null");
this.taskScheduler = taskScheduler;
}
/**
* Gets the defined {@link TaskScheduler}.
*
* @return the defined task scheduler
*/
protected TaskScheduler getTaskScheduler() {
if(taskScheduler == null && getBeanFactory() != null) {
if(log.isDebugEnabled()) {
log.debug("getting taskScheduler service from bean factory " + getBeanFactory());
}
taskScheduler = StateMachineContextUtils.getTaskScheduler(getBeanFactory());
}
return taskScheduler;
}
/**
* Sets the used {@link TaskExecutor}.
*
* @param taskExecutor the task executor
*/
public void setTaskExecutor(TaskExecutor taskExecutor) {
Assert.notNull(taskExecutor, "taskExecutor must not be null");
this.taskExecutor = taskExecutor;
}
/**
* Gets the defined {@link TaskExecutor}.
*
* @return the defined task executor
*/
protected TaskExecutor getTaskExecutor() {
if(taskExecutor == null && getBeanFactory() != null) {
if(log.isDebugEnabled()) {
log.debug("getting taskExecutor service from bean factory " + getBeanFactory());
}
taskExecutor = StateMachineContextUtils.getTaskExecutor(getBeanFactory());
}
return taskExecutor;
}
/**
* Subclasses may implement this for initialization logic. Called
* during the {@link InitializingBean} phase. Implementor should
* always call super method not to break initialization chain.
*
* @throws Exception exception
*/
protected void onInit() throws Exception {}
/**
* Subclasses may implement this method with the start behavior. This
* method will be invoked while holding the {@link #lifecycleLock}.
*/
protected void doStart() {};
/**
* Subclasses may implement this method with the stop behavior. This method
* will be invoked while holding the {@link #lifecycleLock}.
*/
protected void doStop() {};
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2015 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.statemachine.support;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.task.TaskExecutor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
/**
* Utility methods for accessing common components from the BeanFactory.
*
* @author Janne Valkealahti
*
*/
public class StateMachineContextUtils {
/* Default task scheduler bean name */
public static final String TASK_SCHEDULER_BEAN_NAME = "taskScheduler";
/* Default task executor bean name */
public static final String TASK_EXECUTOR_BEAN_NAME = "taskExecutor";
/* Default conversion service bean name */
public static final String CONVERSION_SERVICE_BEAN_NAME = "cloudClusterConversionService";
/* Default evaluation context bean name */
public static final String EVALUATION_CONTEXT_BEAN_NAME = "cloudClusterEvaluationContext";
/**
* Return the {@link TaskScheduler} bean whose name is "taskScheduler" if
* available.
*
* @param beanFactory BeanFactory for lookup, must not be null.
* @return task scheduler
*/
public static TaskScheduler getTaskScheduler(BeanFactory beanFactory) {
return getBeanOfType(beanFactory, TASK_SCHEDULER_BEAN_NAME, TaskScheduler.class);
}
/**
* Return the {@link TaskScheduler} bean whose name is "taskExecutor" if
* available.
*
* @param beanFactory BeanFactory for lookup, must not be null.
* @return task executor
*/
public static TaskExecutor getTaskExecutor(BeanFactory beanFactory) {
return getBeanOfType(beanFactory, TASK_EXECUTOR_BEAN_NAME, TaskExecutor.class);
}
/**
* Return the {@link ConversionService} bean whose name is
* "yarnConversionService" if available.
*
* @param beanFactory
* BeanFactory for lookup, must not be null.
*
* @return The {@link ConversionService} bean whose name is
* "yarnConversionService" if available.
*/
public static ConversionService getConversionService(BeanFactory beanFactory) {
return getBeanOfType(beanFactory, CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
}
/**
* Return the {@link StandardEvaluationContext} bean whose name is
* "yarnEvaluationContext" if available.
*
* @param beanFactory BeanFactory for lookup, must not be null.
*
* @return the instance of {@link StandardEvaluationContext} bean whose name
* is "yarnEvaluationContext" .
*/
public static StandardEvaluationContext getEvaluationContext(BeanFactory beanFactory) {
return getBeanOfType(beanFactory, EVALUATION_CONTEXT_BEAN_NAME, StandardEvaluationContext.class);
}
/**
* Gets a bean from a factory with a given name and type.
*
* @param beanFactory the bean factory
* @param beanName the bean name
* @param type the type as of a class
* @return Bean known to a bean factory, null if not found.
*/
private static <T> T getBeanOfType(BeanFactory beanFactory, String beanName, Class<T> type) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
if (!beanFactory.containsBean(beanName)) {
return null;
}
return beanFactory.getBean(beanName, type);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2015 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.statemachine.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* A {@link MethodFilter} implementation that will match unique methods.
*
* @author Oleg Zhurakousky
* @author Janne Valkealahti
*/
public class UniqueMethodFilter implements MethodFilter {
private final List<Method> uniqueMethods = new ArrayList<Method>();
public UniqueMethodFilter(Class<?> targetClass) {
ArrayList<Method> allMethods = new ArrayList<Method>(Arrays.asList(targetClass.getMethods()));
for (Method method : allMethods) {
this.uniqueMethods.add(org.springframework.util.ClassUtils.getMostSpecificMethod(method, targetClass));
}
}
public boolean matches(Method method) {
return this.uniqueMethods.contains(method);
}
}

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