Compare commits

..

76 Commits

Author SHA1 Message Date
Sam Brannen ed3d91b929 Update antora-extensions to 1.14.12
See gh-36851
2026-05-28 11:01:56 +02:00
Sam Brannen 731ceb8aca Upgrade to fast-xml-parser 5.7.0 2026-05-27 12:19:12 +02:00
Sam Brannen 6f213fb6a0 Upgrade Antora dependencies 2026-05-27 12:13:00 +02:00
Sam Brannen 131ae59724 Pin Node.js version to 24.15.0
Prior to this commit, the `antora` Gradle task silently failed to build
the reference documentation, since Antora uses the latest LTS release
for Node.js by default, and the latest LTS apparently does not work for
us.
2026-05-27 12:09:02 +02:00
Brian Clozel dd3b3a7c69 Remove 6.0.x and 6.1.x reference docs
Branches are now in commercial support mode.
2026-03-23 09:32:20 +01:00
github-actions[bot] cbe79a9241 Update Antora Spring UI to v0.4.26 2026-03-14 10:10:40 +00:00
Sébastien Deleuze ef4a5fb54c Upgrade Antora dependencies
See gh-36105
See gh-36106
2026-01-07 10:20:21 +01:00
Sam Brannen 8ffaec5d72 Update rsync-antora-reference to v0.0.22
Closes gh-35951
2025-12-02 15:40:24 +01:00
Sam Brannen 578cdb5f7e Update antora-extensions to 1.14.7
Closes gh-35949
2025-12-02 15:38:11 +01:00
Sam Brannen 305a512a55 Stop linking to 6.1.22-SNAPSHOT reference docs
With this commit, we now include snapshots for main (which currently
correlates to 7.0.x), 6.2.x, and 7.0.x to 9.*.x.

Closes gh-35923
2025-11-28 12:59:04 +01:00
github-actions[bot] cf036eeaa3 Update Antora UI Spring to v0.4.25
Closes gh-35877

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-24 14:43:29 +01:00
github-actions[bot] fa8a30f753 Update Antora Spring UI to v0.4.22
Closes gh-35858
2025-11-24 10:17:15 +01:00
github-actions[bot] f63ae425bf Update Antora Spring UI to v0.4.20
Closes gh-35812
2025-11-17 14:21:09 +01:00
github-actions[bot] fa20dd820d Update Antora Spring UI to v0.4.19
Closes gh-35796
2025-11-12 12:33:31 +01:00
Sébastien Deleuze 01cebc85b4 Flip the Java 17/25 order
To use Java 17 as the default.
2025-09-23 18:46:47 +02:00
Sébastien Deleuze 6cf89a1e58 Upgrade to Gradle 9.1.0 and Java 25 2025-09-23 18:26:58 +02:00
Brian Clozel e5e8574466 Install both Java 17 and Java 24 for building docs
Docs branches have different Java requirements we should ensure that all
required Java versions are available locally.
2025-06-13 00:52:48 +02:00
Brian Clozel a2cabcbb90 Enable automatic toolchain provisioning 2025-06-12 17:27:03 +02:00
Brian Clozel 9b3625b39e Upgrade to Gradle 8.14.2 2025-06-12 09:33:44 +02:00
Brian Clozel 7232e83807 Fix failing build for docs
As of https://github.com/spring-projects/spring-framework/issues/35007,
the auto-provisioning is disabled for Java runtimes in Gradle.
This makes the build fail because Java 24 is required for building the
docs whereas this action is setting up Java 17.

This commit also upgrades other GitHub actions.
2025-06-12 09:22:16 +02:00
Sébastien Deleuze 91afab7723 Fix broken antora task
See https://github.com/spring-io/antora-extensions/pull/43
2025-02-19 16:49:24 +01:00
github-actions[bot] e19a8e58fc Update Antora UI Spring to v0.4.18 (#33899)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-16 17:51:07 +01:00
Sébastien Deleuze 5f2707a7e3 Upgrade Antora extensions to 1.14.2
It adds support to external link CSS style via
override-navigation-builder-extension.
2024-10-22 08:46:36 +02:00
github-actions[bot] 92ecef74ec Update Antora Spring UI to v0.4.17 2024-10-16 14:12:11 +02:00
Stéphane Nicoll 11c9373439 Polish 2024-08-22 11:09:18 +02:00
Stéphane Nicoll 6f8db2cfd3 Upgrade to Java 17.0.12 2024-08-22 10:33:30 +02:00
Stéphane Nicoll bff8cff03b Upgrade to Gradle 8.9 2024-08-22 10:32:50 +02:00
Sébastien Deleuze c55979b8a9 Build documentation branches only for 6.1 and later
To avoid a duplication error caused by a conflict between the
6.0.x branch and the v6.0.23 tag.
2024-08-16 17:01:00 +02:00
github-actions[bot] 097c773b91 Update Antora Spring UI to v0.4.16 2024-06-17 10:06:44 +00:00
Sébastien Deleuze 431cce0b1e Upgrade to Gradle 8.7 2024-05-27 16:57:12 +02:00
Sébastien Deleuze 0ed87a350c Revert "Add update-antora-ui-spring.yml workflow"
This reverts commit 1a43d698d1.
2024-05-27 16:53:11 +02:00
Marcus Hert Da Coregio 1a43d698d1 Add update-antora-ui-spring.yml workflow 2024-05-24 16:55:10 +02:00
Sébastien Deleuze 94c5481e9e Remove per-branch-antora-playbook.yml
Not needed anymore due to gh-32864.
2024-05-22 16:47:44 +02:00
Rob Winch 41675587ea Modernize Antora Build
- Use package.json so dependabot can automatically update JS dependencies
- Use @springio/antora-extensions (automatically apply default extensions
  in proper order)
- Leverage set-algolia-env-extension to manage algolia env variables
- Update to latest ui

Closes gh-32863
2024-05-22 16:45:17 +02:00
Sébastien Deleuze b14a20161f Update spring-doc-actions to 0.0.16 2024-05-16 18:00:25 +02:00
Sébastien Deleuze 10e5029e88 Upgrade to antora-ui-spring 0.4.13
See https://github.com/spring-io/antora-ui-spring/pull/234
2024-05-15 18:44:01 +02:00
Sébastien Deleuze 4b2552f498 Upgrade to antora-ui-spring 0.4.12 in per-branch-antora-playbook.yml 2024-04-05 10:31:54 +02:00
Sébastien Deleuze 554f15262d Upgrade to antora-ui-spring 0.4.12 2024-04-05 10:00:45 +02:00
Sébastien Deleuze 9fc8046728 Upgrade asciidoctor-extensions to 1.0.0-alpha.10
See gh-22171
2024-03-07 18:03:52 +01:00
Rob Winch 9b60075085 Fixes atlas-extension order
The atlas-extension must be registered before the latest-version-extension
so that the latest version logic is applied to versions imported from the
atlas-extension.

Closes gh-32067
2024-01-22 20:40:47 +01:00
Sébastien Deleuze aa2ea2055d Enable .htaccess generation on per branch builds
See gh-32044
2024-01-19 18:34:54 +01:00
Rob Winch f9c1e6053c Update to spring-io/spring-doc-actions v0.0.14 2024-01-19 17:16:05 +01:00
Rob Winch 2362a142c1 Update to antora-extensions 1.8.2 2024-01-19 09:16:27 +01:00
Stéphane Nicoll b656bb4855 Merge pull request #31701 from rwinch/docs-build-version-updates
Docs build version updates
2023-11-28 10:33:31 +01:00
Rob Winch c6eb4c0eae antora-extensions 1.7.0 2023-11-27 23:36:33 -06:00
Rob Winch 57c6cc83da antora-ui-spring v0.4.9 2023-11-27 23:36:33 -06:00
Rob Winch 1943aad216 Remove @opendevise/antora-release-line-extension
This causes conflicts with latest-version-extension and needs removed.

Closes gh-31480
2023-10-25 19:40:40 +02:00
Rob Winch 57b9a57452 Add Search in all Spring Docs
Closes gh-31290
2023-09-22 10:25:36 +02:00
Rob Winch 35f3e5d664 Update to antora-ui-spring 0.3.6 2023-09-08 08:36:24 +01:00
Rob Winch d88eccc837 Update to antora-ui-spring 0.3.5 2023-08-28 14:04:55 -05:00
Stephane Nicoll 3f8db79e1c Docs build updates
Closes gh-31022
2023-08-10 15:51:16 +02:00
Rob Winch e30e453bb4 Use bust-cloudflare-antora-cache@v0.0.11
Various bug fixes and features
2023-08-09 17:11:59 -05:00
Rob Winch edc01f4957 Add caching for the collector
Cache the collector output for tags which speeds up builds with tags.

- Add inject-collector-cache-config-extension
- Update deployment to include the cache in the site
- Update rsync-antora-referece to handle the cache properly
2023-08-09 17:11:17 -05:00
Rob Winch a53a17c01a Add the partial build extension
Allows partial builds to occur from other branches.
2023-08-09 17:08:29 -05:00
Rob Winch 6c95ee2334 latest-version-extension before collector-extension
This ensures that the versions that do not need to be built will be removed
prior to running the collector on them and thus saving time.
2023-08-09 17:07:57 -05:00
Rob Winch 6236ed56e4 Update to antora-extensions 1.4.2 2023-08-09 17:07:06 -05:00
Rob Winch 6455960cf1 Fix space to tabs 2023-08-09 17:06:53 -05:00
Rob Winch 3771743082 Use a map for antora.options 2023-08-09 17:06:36 -05:00
Rob Winch 1e3b4cdc34 Fix site.url
This was causing the .htaccess file to be generated incorrectly which
prevented redirects from happening properly.

Closes gh-31018
2023-08-09 17:04:47 -05:00
Rob Winch 889c9c7de4 Update to antora-ui-spring 0.3.3 2023-07-18 11:14:26 -05:00
Rob Winch da97ab1dea Update to spring-antora-ui v0.3.2 2023-07-12 17:31:20 +01:00
Rob Winch 9ac9cd15fb Update to antora-ui-spring v0.3.1
- Adds GitHub Project Link
- Adds Stackoverflow Link
- Adds Related Documentation links
2023-07-12 17:07:52 +01:00
Stéphane Nicoll 621f0a6568 Merge pull request #30673 from rwinch/spring-doc-actions-v0.0.8
Upgrade to spring-doc-actions 0.0.8
2023-06-19 15:48:41 +02:00
Rob Winch 6fb9e4a9ea Update to spring-doc-actions 0.0.8 2023-06-14 15:47:23 -05:00
rstoyanchev 3881954125 Roll back rsync-antora-reference to 0.0.5 for now 2023-06-14 20:04:24 +01:00
Rob Winch 42a4409c73 Update to antora-ui-spring v0.3.0 2023-06-14 19:52:49 +01:00
Rob Winch 6c5ef6db1e Add bust-cloudflare-antora-cache
This is necessary so that the css and JavaScript cache will be busted
when updates are deployed
2023-06-14 19:52:49 +01:00
rstoyanchev f4ccf68458 Revert "Use cached playbook in build dir"
This reverts commit 4aa824d456.

See gh-30481
2023-05-23 15:10:24 +01:00
Rob Winch 4aa824d456 Use cached playbook in build dir
Closes gh-30466
2023-05-10 17:38:01 +01:00
rstoyanchev 6a68b4d299 Polishing
Closes gh-30453
2023-05-10 17:36:32 +01:00
Rob Winch 28cc8b7a08 Include future Spring branches and Releases
See gh-30453
2023-05-10 17:36:32 +01:00
Rob Winch 317eee83ac Enable tabs-sync-option
This ensures that the tabs are synced across pages. See
https://github.com/asciidoctor/asciidoctor-tabs#tabs-sync
2023-05-05 17:17:21 +01:00
Rob Winch 81e9a8a26d rwinch to spring-projects 2023-05-04 17:48:59 +01:00
rstoyanchev a2337b73e0 Add main branch to antora-playbook
Closes gh-30415
2023-05-04 15:54:49 +01:00
Rob Winch 8422f0f5b7 Initial 2023-05-03 00:20:21 -05:00
rstoyanchev dc3c016712 Add .gitignore 2023-04-24 15:38:47 +01:00
6076 changed files with 551 additions and 891666 deletions
+57
View File
@@ -0,0 +1,57 @@
name: Build and Deploy Documentation
run-name: ${{ format('{0} ({1})', github.workflow, github.event.inputs.build-refname || 'all') }}
on:
workflow_dispatch:
inputs:
build-refname:
description: Enter git refname to build (e.g., 5.7.x).
required: false
push:
branches: docs-build
env:
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
permissions: read-all
jobs:
build-and-deploy-docs:
name: Build and Deploy Documentation
if: github.repository_owner == 'spring-projects'
runs-on: ubuntu-latest
steps:
- name: Check Out
uses: actions/checkout@v4
with:
fetch-depth: 5
- name: Set Up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: |
25
17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
with:
cache-read-only: false
- name: Set up refname build
if: github.event.inputs.build-refname
run: |
git fetch --depth 1 https://github.com/$GITHUB_REPOSITORY ${{ github.event.inputs.build-refname }}
echo BUILD_REFNAME=${{ github.event.inputs.build-refname }} >> $GITHUB_ENV
echo BUILD_VERSION=$(git cat-file --textconv FETCH_HEAD:gradle.properties | sed -n '/^version=/ { s/^version=//;p }') >> $GITHUB_ENV
- name: Run Antora
run: ./gradlew antora
- name: Copy the cache to be included in the site
run: cp -rf build/antora/inject-collector-cache-config-extension/.cache build/site/
- name: Publish Docs
uses: spring-io/spring-doc-actions/rsync-antora-reference@v0.0.22
with:
docs-username: ${{ secrets.DOCS_USERNAME }}
docs-host: ${{ secrets.DOCS_HOST }}
docs-ssh-key: ${{ secrets.DOCS_SSH_KEY }}
docs-ssh-host-key: ${{ secrets.DOCS_SSH_HOST_KEY }}
- name: Bust Cloudflare Cache
uses: spring-io/spring-doc-actions/bust-cloudflare-antora-cache@v0.0.22
with:
context-root: spring-framework
cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
cloudflare-cache-token: ${{ secrets.CLOUDFLARE_CACHE_TOKEN }}
+29 -8
View File
@@ -1,35 +1,56 @@
# Miscellaneous
*.java.hsp
*.sonarj
*.sw*
.DS_Store
.settings
.springBeans
bin
build.sh
integration-repo
ivy-cache
argfile*
activemq-data/
classes/
# Log files
jxl.log
jmx.log
derby.log
spring-test/test-output/
.gradle
argfile*
pom.xml
# Gradle artifacts
.gradle
.gradletasknamecache
/build
buildSrc/build
/spring-*/build
target/
/framework-bom/build
/framework-docs/build
/integration-tests/build
/src/asciidoc/build
spring-test/test-output/
# Maven artifacts
pom.xml
/target/
# Eclipse artifacts, including WTP generated manifests
bin
.classpath
.project
.settings
.springBeans
spring-*/src/main/java/META-INF/MANIFEST.MF
# IDEA artifacts and output dirs
*.iml
*.ipr
*.iws
.idea
out
test-output
atlassian-ide-plugin.xml
# VS Code
.vscode/
cached-antora-playbook.yml
node_modules
+3
View File
@@ -0,0 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.12-librca
@@ -1,9 +0,0 @@
#com.springsource.sts.gradle.core.preferences.GradleImportPreferences
#Thu Aug 9 11:34:43 CEST 2012
enableAfterTasks=true
afterTasks=afterEclipseImport;
enableDependendencyManagement=false
enableBeforeTasks=true
projects=;spring-aop;spring-aspects;spring-beans;spring-context;spring-context-support;spring-core;spring-expression;spring-instrument;spring-instrument-tomcat;spring-jdbc;spring-jms;spring-orm;spring-oxm;spring-struts;spring-test;spring-tx;spring-web;spring-webmvc;spring-webmvc-portlet;
enableDSLD=false
beforeTasks=cleanEclipse;eclipse;\:spring-oxm\:compileTestJava;
@@ -1,5 +0,0 @@
#com.springsource.sts.gradle.core.preferences.GradleProjectPreferences
#Tue Feb 21 14:38:31 CET 2012
com.springsource.sts.gradle.classpath.enableSorting=false
com.springsource.sts.gradle.rootprojectloc=
com.springsource.sts.gradle.linkedresources=
@@ -1,9 +0,0 @@
#com.springsource.sts.gradle.core.actions.GradleRefreshPreferences
#Thu Aug 9 11:34:43 CEST 2012
enableAfterTasks=true
afterTasks=afterEclipseImport;
useHierarchicalNames=false
enableBeforeTasks=true
addResourceFilters=false
enableDSLD=false
beforeTasks=cleanEclipse;eclipse;\:spring-oxm\:compileTestJava;
-303
View File
@@ -1,303 +0,0 @@
_Have something you'd like to contribute to the framework? We welcome pull
requests, but ask that you carefully read this document first to understand how
best to submit them; what kind of changes are likely to be accepted; and what
to expect from the Spring team when evaluating your submission._
_Please refer back to this document as a checklist before issuing any pull
request; this will save time for everyone!_
## Understand the basics
Not sure what a pull request is, or how to submit one? Take a look at GitHub's
excellent [help documentation][] first.
## Search JIRA first; create an issue if necessary
Is there already an issue that addresses your concern? Do a bit of searching
in our [JIRA issue tracker][] to see if you can find something similar. If not,
please create a new issue before submitting a pull request unless the change is
truly trivial, e.g. typo fixes, removing compiler warnings, etc.
## Discuss non-trivial contribution ideas with committers
If you're considering anything more than correcting a typo or fixing a minor
bug, please discuss it on the [spring-framework-contrib][] mailing list before
submitting a pull request. We're happy to provide guidance, but please spend an
hour or two researching the subject on your own including searching the mailing
list for prior discussions.
## Sign the Contributor License Agreement
If you have not previously done so, please fill out and submit the
[SpringSource CLA form][]. You'll receive a token when this process is complete.
Keep track of this; you may be asked for it later!
Note that emailing/postal mailing a signed copy is _not_ necessary. Submission
of the web form is all that is required.
Once you've completed the web form, simply add the following in a comment on
your pull request:
I have signed and agree to the terms of the SpringSource Individual
Contributor License Agreement.
You do not need to include your token/id. Please add the statement above to all
future pull requests as well, simply so that the Spring Framework team knows
immediately that this process is complete.
## Create your branch from `3.2.x`
If your pull request addresses a bug or improvement, please create your branch
from Spring Framework's `3.2.x` branch. `master` is reserved for work on new features
for the next major version of the framework. Rest assured that if your pull
request is accepted and merged into `3.2.x`, these changes will also eventually
be merged into `master`.
## Use short branch names
Branches used when submitting pull requests should preferably be named
according to JIRA issues, e.g. 'SPR-1234'. Otherwise, use succinct, lower-case,
dash (-) delimited names, such as 'fix-warnings', 'fix-typo', etc. In
[fork-and-edit][] cases, the GitHub default 'patch-1' is fine as well. This is
important, because branch names show up in the merge commits that result from
accepting pull requests, and should be as expressive and concise as possible.
## Mind the whitespace
Please carefully follow the whitespace and formatting conventions already
present in the framework.
1. Tabs, not spaces
1. Unix (LF), not DOS (CRLF) line endings
1. Eliminate all trailing whitespace
1. Wrap Javadoc at 90 characters
1. Aim to wrap code at 90 characters, but favor readability over wrapping
1. Preserve existing formatting; i.e. do not reformat code for its own sake
1. Search the codebase using `git grep` and other tools to discover common
naming conventions, etc.
1. Latin-1 (ISO-8859-1) encoding for Java sources; use `native2ascii` to convert
if necessary
## Add Apache license header to all new classes
```java
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ...;
```
## Update Apache license header to modified files as necessary
Always check the date range in the license header. For example, if you've
modified a file in 2013 whose header still reads
```java
* Copyright 2002-2011 the original author or authors.
```
then be sure to update it to 2013 appropriately
```java
* Copyright 2002-2013 the original author or authors.
```
## Use @since tags for newly-added public API types and methods
e.g.
```java
/**
* ...
*
* @author First Last
* @since 3.2
* @see ...
*/
```
## Submit JUnit test cases for all behavior changes
Search the codebase to find related unit tests and add additional @Test methods
within. It is also acceptable to submit test cases on a per JIRA issue basis,
e.g.
```java
package org.springframework.beans.factory.support;
/**
* Unit tests for SPR-8954, in which a custom {@link InstantiationAwareBeanPostProcessor}
* forces the predicted type of a FactoryBean, effectively preventing retrieval of the
* bean from calls to #getBeansOfType(FactoryBean.class). The implementation of
* {@link AbstractBeanFactory#isFactoryBean(String, RootBeanDefinition)} now ensures
* that not only the predicted bean type is considered, but also the original bean
* definition's beanClass.
*
* @author Chris Beams
*/
public class Spr8954Tests {
@Test
public void cornerSpr8954() {
// ...
}
}
```
## Squash commits
Use `git rebase --interactive`, `git add --patch` and other tools to "squash"
multiple commits into atomic changes. In addition to the man pages for git,
there are many resources online to help you understand how these tools work.
Here is one: http://book.git-scm.com/4_interactive_rebasing.html.
## Use real name in git commits
Please configure git to use your real first and last name for any commits you
intend to submit as pull requests. For example, this is not acceptable:
Author: Nickname <user@mail.com>
Rather, please include your first and last name, properly capitalized, as
submitted against the SpringSource contributor license agreement:
Author: First Last <user@mail.com>
This helps ensure traceability against the CLA, and also goes a long way to
ensuring useful output from tools like `git shortlog` and others.
You can configure this globally via the account admin area GitHub (useful for
fork-and-edit cases); globally with
git config --global user.name "First Last"
git config --global user.email user@mail.com
or locally for the spring-framework repository only by omitting the '--global'
flag:
cd spring-framework
git config user.name "First Last"
git config user.email user@mail.com
## Format commit messages
Please read and follow the [commit guidelines section of Pro Git][].
Most importantly, please format your commit messages in the following way
(adapted from the commit template in the link above):
Short (50 chars or less) summary of changes
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded by a
single space, with blank lines in between, but conventions vary here
Issue: SPR-1234, SPR-1235
1. Use imperative statements in the subject line, e.g. "Fix broken Javadoc link"
1. Begin the subject line sentence with a capitalized verb, e.g. "Add, Prune,
Fix, Introduce, Avoid, etc."
1. Do not end the subject line with a period
1. Keep the subject line to 50 characters or less if possible
1. Wrap lines in the body at 72 characters or less
1. Mention associated JIRA issue(s) at the end of the commit comment, prefixed
with "Issue: " as above
1. In the body of the commit message, explain how things worked before this
commit, what has changed, and how things work now
For examples of this style, issue a `git log --author=cbeams` in the
spring-framework git repository. For convenience, here are several such commits:
https://github.com/SpringSource/spring-framework/commit/08e2669b84ec0faa2f7904441fe39ac70b65b078
https://github.com/SpringSource/spring-framework/commit/1d9d3e6ff79ce9f0eca03b02cd1df705925575da
https://github.com/SpringSource/spring-framework/commit/8e0b1c3a5f957af3049cfa0438317177e16d6de6
https://github.com/SpringSource/spring-framework/commit/b787a68f2050df179f7036b209aa741230a02477
## Run all tests prior to submission
See the [building from source][] section of the README for instructions. Make
sure that all tests pass prior to submitting your pull request.
## Submit your pull request
Subject line:
Follow the same conventions for pull request subject lines as mentioned above
for commit message subject lines.
In the body:
1. Explain your use case. What led you to submit this change? Why were existing
mechanisms in the framework insufficient? Make a case that this is a
general-purpose problem and that yours is a general-purpose solution, etc.
1. Add any additional information and ask questions; start a conversation, or
continue one from JIRA
1. Mention the JIRA issue ID
1. Also mention that you have submitted the CLA as described above
Note that for pull requests containing a single commit, GitHub will default the
subject line and body of the pull request to match the subject line and body of
the commit message. This is fine, but please also include the items above in the
body of the request.
## Mention your pull request on the associated JIRA issue
Add a comment to the associated JIRA issue(s) linking to your new pull request.
## Expect discussion and rework
The Spring team takes a very conservative approach to accepting contributions to
the framework. This is to keep code quality and stability as high as possible,
and to keep complexity at a minimum. Your changes, if accepted, may be heavily
modified prior to merging. You will retain "Author:" attribution for your Git
commits granted that the bulk of your changes remain intact. You may be asked to
rework the submission for style (as explained above) and/or substance. Again, we
strongly recommend discussing any serious submissions with the Spring Framework
team _prior_ to engaging in serious development work.
Note that you can always force push (`git push -f`) reworked / rebased commits
against the branch used to submit your pull request. i.e. you do not need to
issue a new pull request when asked to make changes.
[help documentation]: http://help.github.com/send-pull-requests
[JIRA issue tracker]: https://jira.springsource.org/browse/SPR
[spring-framework-contrib]: https://groups.google.com/forum/#!forum/spring-framework-contrib
[SpringSource CLA form]: https://support.springsource.com/spring_committer_signup
[fork-and-edit]: https://github.com/blog/844-forking-with-the-edit-button
[commit guidelines section of Pro Git]: http://progit.org/book/ch5-2.html#commit_guidelines
[building from source]: https://github.com/SpringSource/spring-framework#building-from-source
+140
View File
@@ -0,0 +1,140 @@
= Spring Framework Docs Build
You're currently viewing the Antora playbook branch.
The playbook branch hosts the docs build that is used to build and publish the production docs site.
The Spring Framework reference docs are built using https://antora.org[Antora].
This README covers how to build the docs in a software branch as well as how to build the production docs site locally.
== Overview
To prepare your system for building the documentation, <<prerequisites,install the prerequisites>> and then <<build-main,create your workspace and build the main branch documentation>>.
Once you've completed those steps, follow the instructions in <<build-branch,Build the 6.0.x branch documentation>> to learn how to build the documentation for a version branch you haven't previously checked out.
To build the production site documentation on your computer, follow the instructions in <<prerequisites,Prerequisites>>, <<build-main,Build the main branch documentation>>, and then <<build-production,Build the production documentation site>>.
.Branch checkout instead of worktrees
[NOTE]
====
If you prefer to set up your workspace without worktrees, complete the steps in <<prerequisites,Prerequisites>> and clone the project repository onto your computer.
Then follow the instructions in each section starting from the `sdk env || sdk env install` step once you've checked out the desired branch.
====
[#prerequisites]
== Prerequisites (everyone)
These instructions assume you already have basic tools on your system, including bash, zip, unzip, git, and curl.
In addition to these basic tools, you need https://sdkman.io/install[SDKMAN!] installed so that the correct JDK is set for each branch.
. Open your terminal and enter the following command:
+
--
$ curl -s "https://get.sdkman.io" | bash
This command downloads and installs SDKMAN!
Once installation is complete, you should see a command displayed in your terminal that will initiate SDKMAN.
--
. Copy the command displayed in your terminal and run it.
`$HOME` is the path unique to your computer (e.g., _home/my-jam/.sdkman/bin/sdkman-init.sh_).
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
You'll use SDKMAN in the next sections to install and switch to the JDK required for each branch.
Now you're ready to <<build-main,create your workspace>>.
[#build-main]
== Build the main branch documentation (writers)
Your workspace will be the folder that contains the git worktrees of the project.
. In your terminal, create a directory for the project and then change into that directory.
$ mkdir spring-framework
$ cd spring-framework
. Clone the project repository and create the primary worktree for the main branch.
Then change into the new _main_ folder.
$ git clone https://github.com/spring-projects/spring-framework main
$ cd main
. Switch to the required JDK using SDKMAN by running the following command:
+
--
$ sdk env || sdk env install
SDKMAN will switch to the required JDK or install it if it isn't present.
--
. Generate the documentation with Antora using the following command:
+
--
$ ./gradlew -PbuildSrc.skipTests=true :framework-docs:antora
This command will build the documentation, including any generated attributes, for the main branch.
--
. Navigate to _$HOME/spring-framework/main/framework-docs/build/site/index.html_ to view the generated documentation.
[#build-branch]
== Build the 6.0.x branch documentation (writers)
NOTE: The instructions in this section assume you've completed the steps in the <<build-main,previous section>>.
After creating the worktree for the main branch, you can set up a worktree for any other branches you'll work on in the future.
In this section, you'll create a worktree for the 6.0.x branch in your project workspace.
. To add a worktree, you have to be in a worktree.
In your terminal, change to the _main_ folder if you aren't already in it, e.g., _$HOME/spring-framework/main_.
Set up a worktree for the 6.0.x branch and then change into the new directory by running the following commands:
$ git worktree add ../6.0.x 6.0.x --track
$ cd ../6.0.x
. Switch to the required JDK or install it.
$ sdk env || sdk env install
. Generate the documentation with the following command:
+
--
$ ./gradlew -PbuildSrc.skipTests=true :framework-docs:antora
This command will build the documentation, including any generated attributes, for the 6.0.x branch.
--
. Navigate to _$HOME/spring-framework/6.0.x/docs/build/site/index.html_ to view the generated documentation.
[#build-production]
== Build the production documentation site (docs manager)
NOTE: The instructions in this section assume you've <<build-main,prepared your workspace and created the worktree for the main branch>>.
To build the project's production site, you'll set up a worktree for the docs-build branch of the repository.
. To add a worktree, you have to be in a worktree.
In your terminal, change to the _main_ folder if you aren't already in it, e.g., _$HOME/spring-framework/main_.
Run the following command to set up the worktree for the _docs-build_ branch.
Then change into the new _docs-build_ directory.
$ git worktree add ../docs-build docs-build --track
$ cd ../docs-build
. Switch to the required JDK or install it.
$ sdk env || sdk env install
. Generate the documentation for the project's production site using the following command:
+
--
$ ./gradlew antora
This command will build all of the documentation included in the project's production site from the repository on GitHub.
To build the documentation from the current clone, using any worktrees that are available, use the following command instead:
$ ./gradlew antora --playbook local-antora-playbook.yml
--
. Navigate to _$HOME/spring-framework/docs-site/build/site/index.html_ to view the generated documentation.
-96
View File
@@ -1,96 +0,0 @@
## Spring Framework
The Spring Framework provides a comprehensive programming and configuration
model for modern Java-based enterprise applications - on any kind of deployment
platform. A key element of Spring is infrastructural support at the application
level: Spring focuses on the "plumbing" of enterprise applications so that teams
can focus on application-level business logic, without unnecessary ties to
specific deployment environments.
The framework also serves as the foundation for [Spring Integration][], [Spring
Batch][] and the rest of the Spring [family of projects][]. Browse the
repositories under the [SpringSource organization][] on GitHub for a full list.
## Downloading artifacts
See [downloading Spring artifacts][] for Maven repository information. Unable to
use Maven or other transitive dependency management tools? See [building a
distribution with dependencies][].
## Documentation
See the current [Javadoc][] and [reference docs][].
## Getting support
Check out the [Spring forums][] and the [spring][spring tag] and
[spring-mvc][spring-mvc tag] tags on [Stack Overflow][]. [Commercial support][]
is available too.
## Issue Tracking
Report issues via the [Spring Framework JIRA]. Understand our issue management
process by reading about [the lifecycle of an issue][]. Think you've found a
bug? Please consider submitting a reproduction project via the
[spring-framework-issues][] GitHub repository. The [readme][] there provides
simple step-by-step instructions.
## Building from source
The Spring Framework uses a [Gradle][]-based build system. In the instructions
below, [`./gradlew`][] is invoked from the root of the source tree and serves as
a cross-platform, self-contained bootstrap mechanism for the build.
### prerequisites
[Git][] and [Early Access build of OpenJDK 1.8 build 88][JDK18 build 88].
### check out sources
`git clone git://github.com/SpringSource/spring-framework.git`
### compile and test, build all jars, distribution zips and docs
`./gradlew build`
### install all spring-\* jars into your local Maven cache
`./gradlew install`
### import sources into your IDE
Run `./import-into-eclipse.sh` or read `import-into-idea.md` as appropriate.
... and discover more commands with `./gradlew tasks`. See also the [Gradle
build and release FAQ][].
## Contributing
[Pull requests][] are welcome; see the [contributor guidelines][] for details.
## Staying in touch
Follow [@springframework][] and its [team members][] on Twitter. In-depth
articles can be found at the SpringSource [team blog][], and releases are
announced via our [news feed][].
## License
The Spring Framework is released under version 2.0 of the [Apache License][].
[Spring Integration]: https://github.com/SpringSource/spring-integration
[Spring Batch]: https://github.com/SpringSource/spring-batch
[family of projects]: http://springsource.org/projects
[SpringSource organization]: https://github.com/SpringSource
[downloading Spring artifacts]: https://github.com/SpringSource/spring-framework/wiki/Downloading-Spring-artifacts
[building a distribution with dependencies]: https://github.com/SpringSource/spring-framework/wiki/Building-a-distribution-with-dependencies
[Javadoc]: http://static.springsource.org/spring-framework/docs/current/javadoc-api
[reference docs]: http://static.springsource.org/spring-framework/docs/current/spring-framework-reference
[Spring forums]: http://forum.springsource.org
[spring tag]: http://stackoverflow.com/questions/tagged/spring
[spring-mvc tag]: http://stackoverflow.com/questions/tagged/spring-mvc
[Stack Overflow]: http://stackoverflow.com/faq
[Commercial support]: http://springsource.com/support/springsupport
[Spring Framework JIRA]: http://jira.springsource.org/browse/SPR
[the lifecycle of an issue]: https://github.com/cbeams/spring-framework/wiki/The-Lifecycle-of-an-Issue
[spring-framework-issues]: https://github.com/SpringSource/spring-framework-issues#readme
[readme]: https://github.com/SpringSource/spring-framework-issues#readme
[Gradle]: http://gradle.org
[`./gradlew`]: http://vimeo.com/34436402
[Git]: http://help.github.com/set-up-git-redirect
[JDK18 build 88]: https://jdk8.java.net/archive/8-b88.html
[Gradle build and release FAQ]: https://github.com/SpringSource/spring-framework/wiki/Gradle-build-and-release-FAQ
[Pull requests]: http://help.github.com/send-pull-requests
[contributor guidelines]: https://github.com/SpringSource/spring-framework/blob/master/CONTRIBUTING.md
[@springframework]: http://twitter.com/springframework
[team members]: http://twitter.com/springframework/team/members
[team blog]: http://blog.springsource.org
[news feed]: http://www.springsource.org/news-events
[Apache License]: http://www.apache.org/licenses/LICENSE-2.0
+41
View File
@@ -0,0 +1,41 @@
antora:
extensions:
- require: '@springio/antora-extensions'
root_component_name: 'framework'
site:
title: Spring Framework
url: https://docs.spring.io/spring-framework/reference
robots: allow
git:
ensure_git_suffix: false
content:
sources:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
# tags: include all releases from 6.2.0 to 9.*.*.
tags: ['v6.2.+({0..9})', 'v{7..9}.+({0..9}).+({0..9})?(-{RC,M}*)']
start_path: framework-docs
asciidoc:
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
- '@springio/asciidoctor-extensions/include-code-extension'
attributes:
page-stackoverflow-url: https://stackoverflow.com/questions/tagged/spring
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
include-java: 'example$docs-src/main/java/org/springframework/docs'
urls:
latest_version_segment_strategy: redirect:to
latest_version_segment: ''
redirect_facility: httpd
runtime:
log:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip
+7 -988
View File
File diff suppressed because it is too large Load Diff
@@ -1,158 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
/**
* Gradle plugin that detects identically named, non-empty packages split across multiple
* subprojects, e.g. "org.springframework.context.annotation" existing in both spring-core
* and spring-aspects. Adds a 'detectSplitPackages' task to the current project's task
* collection. If the project already contains a 'check' task (i.e. is a typical Gradle
* project with the "java" plugin applied), the 'check' task will be updated to depend on
* the execution of 'detectSplitPackages'.
*
* By default, all subprojects will be scanned. Use the 'projectsToScan' task property to
* modify this value. Example usage:
*
* apply plugin: 'detect-split-packages // typically applied to root project
*
* detectSplitPackages {
* packagesToScan -= project(":spring-xyz") // scan every project but spring-xyz
* }
*
* @author Rob Winch
* @author Glyn Normington
* @author Chris Beams
*/
public class DetectSplitPackagesPlugin implements Plugin<Project> {
public void apply(Project project) {
def tasks = project.tasks
Task detectSplitPackages = tasks.add("detectSplitPackages", DetectSplitPackagesTask.class)
if (tasks.asMap.containsKey("check")) {
tasks.getByName("check").dependsOn detectSplitPackages
}
}
}
public class DetectSplitPackagesTask extends DefaultTask {
private static final String JAVA_FILE_SUFFIX = ".java"
private static final String PACKAGE_SEPARATOR = "."
private static final String HIDDEN_DIRECTORY_PREFIX = "."
@Input
Set<Project> projectsToScan = project.subprojects
public DetectSplitPackagesTask() {
this.group = "Verification"
this.description = "Detects packages split across two or more subprojects."
}
@TaskAction
public void detectSplitPackages() {
def splitPackages = doDetectSplitPackages()
if (!splitPackages.isEmpty()) {
def message = "The following split package(s) have been detected:\n"
splitPackages.each { pkg, mod ->
message += " - ${pkg} (split across ${mod[0].name} and ${mod[1].name})\n"
}
throw new GradleException(message)
}
}
private Map<String, List<Project>> doDetectSplitPackages() {
def splitPackages = [:]
def mergedProjects = findMergedProjects()
def packagesByProject = mapPackagesByProject()
def projects = packagesByProject.keySet().toArray()
def nProjects = projects.length
for (int i = 0; i < nProjects - 1; i++) {
for (int j = i + 1; j < nProjects - 1; j++) {
def prj_i = projects[i]
def prj_j = projects[j]
def pkgs_i = new HashSet(packagesByProject.get(prj_i))
def pkgs_j = packagesByProject.get(prj_j)
pkgs_i.retainAll(pkgs_j)
if (!pkgs_i.isEmpty()
&& mergedProjects.get(prj_i) != prj_j
&& mergedProjects.get(prj_j) != prj_i) {
pkgs_i.each { pkg ->
def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR)
splitPackages[readablePkg] = [prj_i, prj_j]
}
}
}
}
return splitPackages;
}
private Map<Project, Set<String>> mapPackagesByProject() {
def packagesByProject = [:]
this.projectsToScan.each { Project p ->
def packages = new HashSet<String>()
p.sourceSets.main.java.srcDirs.each { File dir ->
findPackages(packages, dir, "")
}
if (!packages.isEmpty()) {
packagesByProject.put(p, packages)
}
}
return packagesByProject;
}
private Map<Project, Project> findMergedProjects() {
def mergedProjects = [:]
this.projectsToScan.findAll { p ->
p.plugins.findPlugin(MergePlugin)
}.findAll { p ->
p.merge.into
}.each { p ->
mergedProjects.put(p, p.merge.into)
}
return mergedProjects
}
private static void findPackages(Set<String> packages, File dir, String packagePath) {
def scanDir = new File(dir, packagePath)
def File[] javaFiles = scanDir.listFiles({ file ->
!file.isDirectory() && file.name.endsWith(JAVA_FILE_SUFFIX)
} as FileFilter)
if (javaFiles != null && javaFiles.length != 0) {
packages.add(packagePath)
}
scanDir.listFiles({ File file ->
file.isDirectory() && !file.name.startsWith(HIDDEN_DIRECTORY_PREFIX)
} as FileFilter).each { File subDir ->
findPackages(packages, dir, packagePath + File.separator + subDir.name)
}
}
}
@@ -1,160 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.gradle
import org.gradle.api.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.maven.Conf2ScopeMapping
import org.gradle.api.plugins.MavenPlugin
import org.gradle.api.tasks.*
import org.gradle.plugins.ide.eclipse.EclipsePlugin
import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
import org.gradle.plugins.ide.idea.IdeaPlugin
import org.gradle.api.invocation.*
/**
* Gradle plugin that allows projects to merged together. Primarily developed to
* allow Spring to support multiple multiple incompatible versions of third-party
* dependencies (for example Hibernate v3 and v4).
* <p>
* The 'merge' extension should be used to define how projects are merged, for example:
* <pre class="code">
* configure(subprojects) {
* apply plugin: MergePlugin
* }
*
* project("myproject") {
* }
*
* project("myproject-extra") {
* merge.into = project("myproject")
* }
* </pre>
* <p>
* This plugin adds two new configurations:
* <ul>
* <li>merging - Contains the projects being merged into this project<li>
* <li>runtimeMerge - Contains all dependencies that are merge projects. These are used
* to allow an IDE to reference merge projects.</li>
* <ul>
*
* @author Rob Winch
* @author Phillip Webb
*/
class MergePlugin implements Plugin<Project> {
private static boolean attachedProjectsEvaluated;
public void apply(Project project) {
project.plugins.apply(MavenPlugin)
project.plugins.apply(EclipsePlugin)
project.plugins.apply(IdeaPlugin)
MergeModel model = project.extensions.create("merge", MergeModel)
project.configurations.add("merging")
Configuration runtimeMerge = project.configurations.add("runtimeMerge")
// Ensure the IDE can reference merged projects
project.eclipse.classpath.plusConfigurations += [runtimeMerge]
project.idea.module.scopes.PROVIDED.plus += runtimeMerge
// Hook to perform the actual merge logic
project.afterEvaluate{
if(it.merge.into != null) {
setup(it)
}
}
// Hook to build runtimeMerge dependencies
if(!attachedProjectsEvaluated) {
project.gradle.projectsEvaluated{
postProcessProjects(it)
}
attachedProjectsEvaluated = true;
}
}
private void setup(Project project) {
project.merge.into.dependencies.add("merging", project)
project.dependencies.add("provided", project.merge.into.sourceSets.main.output)
project.dependencies.add("runtimeMerge", project.merge.into)
setupTaskDependencies(project)
setupMaven(project)
}
private void setupTaskDependencies(Project project) {
// invoking a task will invoke the task with the same name on 'into' project
["sourcesJar", "jar", "javadocJar", "javadoc", "install", "artifactoryPublish"].each {
def task = project.tasks.findByPath(it)
if(task) {
task.enabled = false
task.dependsOn(project.merge.into.tasks.findByPath(it))
}
}
// update 'into' project artifacts to contain the source artifact contents
project.merge.into.sourcesJar.from(project.sourcesJar.source)
project.merge.into.jar.from(project.sourceSets.main.output)
project.merge.into.javadoc {
source += project.javadoc.source
classpath += project.javadoc.classpath
}
}
private void setupMaven(Project project) {
project.configurations.each { configuration ->
Conf2ScopeMapping mapping = project.conf2ScopeMappings.getMapping([configuration])
if(mapping.scope) {
Configuration intoConfiguration = project.merge.into.configurations.add(
project.name + "-" + configuration.name)
configuration.excludeRules.each {
configuration.exclude([
(ExcludeRule.GROUP_KEY) : it.group,
(ExcludeRule.MODULE_KEY) : it.module])
}
configuration.dependencies.each {
def intoCompile = project.merge.into.configurations.getByName("compile")
// Protect against changing a compile scope dependency (SPR-10218)
if(!intoCompile.dependencies.contains(it)) {
intoConfiguration.dependencies.add(it)
}
}
project.merge.into.install.repositories.mavenInstaller.pom.scopeMappings.addMapping(
mapping.priority + 100, intoConfiguration, mapping.scope)
}
}
}
private postProcessProjects(Gradle gradle) {
gradle.allprojects(new Action<Project>() {
public void execute(Project project) {
project.configurations.getByName("runtime").allDependencies.withType(ProjectDependency).each{
Configuration dependsOnMergedFrom = it.dependencyProject.configurations.getByName("merging");
dependsOnMergedFrom.dependencies.each{ dep ->
project.dependencies.add("runtimeMerge", dep.dependencyProject)
}
}
}
});
}
}
class MergeModel {
Project into;
}
@@ -1,57 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ProjectDependency;
/**
* Gradle plugin that automatically updates testCompile dependencies to include
* the test source sets of project dependencies.
*
* @author Phillip Webb
*/
class TestSourceSetDependenciesPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.afterEvaluate {
Set<ProjectDependency> projectDependencies = new LinkedHashSet<ProjectDependency>()
collectProjectDependencies(projectDependencies, project)
projectDependencies.each {
project.dependencies.add("testCompile", it.dependencyProject.sourceSets.test.output)
}
}
}
private void collectProjectDependencies(Set<ProjectDependency> projectDependencies,
Project project) {
for(def configurationName in ["compile", "optional", "provided", "testCompile"]) {
Configuration configuration = project.getConfigurations().findByName(configurationName)
if(configuration) {
configuration.dependencies.findAll { it instanceof ProjectDependency }.each {
projectDependencies.add(it)
collectProjectDependencies(projectDependencies, it.dependencyProject)
}
}
}
}
}
@@ -1 +0,0 @@
implementation-class=org.springframework.build.gradle.DetectSplitPackagesPlugin
@@ -1 +0,0 @@
implementation-class=org.springframework.build.gradle.MergePlugin
@@ -1 +0,0 @@
implementation-class=org.springframework.build.gradle.TestSourceSetDependenciesPlugin
+2 -1
View File
@@ -1 +1,2 @@
version=4.0.0.M1
group=org.springframework
description=Spring Framework Docs Site
-105
View File
@@ -1,105 +0,0 @@
import org.gradle.plugins.ide.eclipse.model.ProjectDependency
import org.gradle.plugins.ide.eclipse.model.SourceFolder
apply plugin: "propdeps-eclipse"
apply plugin: "propdeps-idea"
// Until eclipse fully supports Java 8 use 1.7 source level
eclipse.jdt {
sourceCompatibility = 1.7
targetCompatibility = 1.7
}
// Replace classpath entries with project dependencies (GRADLE-1116)
eclipse.classpath.file.whenMerged { classpath ->
def regexp = /.*?\/([^\/]+)\/build\/[^\/]+\/(?:main|test)/ // only match those that end in main or test (avoids removing necessary entries like build/classes/jaxb)
def projectOutputDependencies = classpath.entries.findAll { entry -> entry.path =~ regexp }
projectOutputDependencies.each { entry ->
def matcher = (entry.path =~ regexp)
if(matcher) {
def projectName = matcher[0][1]
def path = "/${projectName}"
if(!classpath.entries.find { e -> e instanceof ProjectDependency && e.path == path }) {
def dependency = new ProjectDependency(path, project(":${projectName}").path)
dependency.exported = true
classpath.entries.add(dependency)
}
classpath.entries.remove(entry)
}
}
classpath.entries.removeAll { entry -> (entry.path =~ /(?!.*?repack.*\.jar).*?\/([^\/]+)\/build\/libs\/[^\/]+\.jar/) }
}
// Use separate main/test outputs (prevents WTP from packaging test classes)
eclipse.classpath.defaultOutputDir = file(project.name+"/bin/eclipse")
eclipse.classpath.file.beforeMerged { classpath ->
classpath.entries.findAll{ it instanceof SourceFolder }.each {
if(it.output.startsWith("bin/")) {
it.output = null
}
}
}
eclipse.classpath.file.whenMerged { classpath ->
classpath.entries.findAll{ it instanceof SourceFolder }.each {
it.output = "bin/" + it.path.split("/")[1]
}
}
// Allow projects to be used as WPT modules
eclipse.project.natures "org.eclipse.wst.common.project.facet.core.nature"
// Include project specific settings
task eclipseSettings(type: Copy) {
from rootProject.files(
"src/eclipse/org.eclipse.jdt.ui.prefs",
"src/eclipse/org.eclipse.wst.common.project.facet.core.xml")
into project.file('.settings/')
outputs.upToDateWhen { false }
}
task eclipseWstComponent(type: Copy) {
from rootProject.files(
"src/eclipse/org.eclipse.wst.common.component")
into project.file('.settings/')
expand(deployname: project.name)
outputs.upToDateWhen { false }
}
task eclipseJdtPrepare(type: Copy) {
from rootProject.file("src/eclipse/org.eclipse.jdt.core.prefs")
into project.file(".settings/")
outputs.upToDateWhen { false }
}
task cleanEclipseJdtUi(type: Delete) {
delete project.file(".settings/org.eclipse.jdt.ui.prefs")
delete project.file("org.eclipse.jdt.core.prefs")
delete project.file(".settings/org.eclipse.wst.common.component")
delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml")
}
tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare)
tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi)
tasks["eclipse"].dependsOn(eclipseSettings, eclipseWstComponent)
// Filter 'build' folder
eclipse.project.file.withXml {
def node = it.asNode()
def filteredResources = node.get("filteredResources")
if(filteredResources) {
node.remove(filteredResources)
}
def filterNode = node.appendNode("filteredResources").appendNode("filter")
filterNode.appendNode("id", "1359048889071")
filterNode.appendNode("name", "")
filterNode.appendNode("type", "30")
def matcherNode = filterNode.appendNode("matcher")
matcherNode.appendNode("id", "org.eclipse.ui.ide.multiFilter")
matcherNode.appendNode("arguments", "1.0-projectRelativePath-matches-false-false-build")
}
-75
View File
@@ -1,75 +0,0 @@
/**
* Generate a JDiff report between the current version and an older version.
*
* Usage:
* gradle jdiff -D OLD_VERSION=3.1.3.RELEASE -D OLD_VERSION_ROOT=/path/to/3.1.3.RELEASE
*
* View generated report at:
* build/reports/jdiff/changes.html
*
* @param OLD_VERSION required
* @param OLD_VERSION_ROOT required, typically pointing to a separate git clone dir
*/
task jdiff {
description = "Generates a JDiff report"
group = "Documentation"
def jdiffHome = "${rootProject.rootDir}/gradle/jdiff"
ant.taskdef(
name: "jdiff",
classname: "jdiff.JDiffAntTask",
classpath: "${jdiffHome}/antjdiff.jar")
def currentVersion = rootProject.version
def currentVersionRoot = rootProject.rootDir
def oldVersion = System.getProperty("OLD_VERSION")
def oldVersionRoot = System.getProperty("OLD_VERSION_ROOT")
def outputDir = "${rootProject.buildDir}/reports/jdiff/${oldVersion}_to_${currentVersion}"
doLast {
if (oldVersion == null)
throw new IllegalArgumentException(
"Set OLD_VERSION property to indicate older of the two versions being compared")
if (oldVersionRoot == null)
throw new IllegalArgumentException(
"Set OLD_VERSION_ROOT property to indicate the root directory for ${oldVersion}")
oldVersionRoot = new File(oldVersionRoot)
ant.property(name: "JDIFF_HOME", value: jdiffHome)
ant.mkdir(dir: outputDir)
ant.jdiff(
destdir: outputDir,
verbose: "off",
stats: "on",
docchanges: "off") {
old(name: "Spring Framework ${oldVersion}") {
oldVersionRoot.eachDirMatch( {
def candidate = new File(it)
candidate.name.matches("org.springframework.*") ||
candidate.name.matches("spring-.*") }) { match ->
match.eachDirRecurse { subdir ->
if (subdir.path ==~ '.*/src/main/java$') {
dirset(dir: subdir.path, includes: "org/**")
}
}
}
}
"new"(name: "Spring Framework ${currentVersion}") {
currentVersionRoot.eachDirMatch( {
def candidate = new File(it)
candidate.name.matches("org.springframework.*") ||
candidate.name.matches("spring-.*") }) { match ->
match.eachDirRecurse { subdir ->
if (subdir.path ==~ '.*/src/main/java$') {
dirset(dir: subdir.path, includes: "org/**")
}
}
}
}
}
}
}
-506
View File
@@ -1,506 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
-9
View File
@@ -1,9 +0,0 @@
/**
* This class is used only as a "null" argument for Javadoc when comparing
* two API files. Javadoc has to have a package, .java or .class file as an
* argument, even though JDiff doesn't use it.
*/
public class Null {
public Null() {
}
}
-6
View File
@@ -1,6 +0,0 @@
This distribution of JDiff 1.1.1 is included in the Spring Framework build
because JDiff has a hard requirement on a JDIFF_HOME directory containing
jdiff.jar and xerces.jar as well as other presentation resources.
The actual generation of JDiff reports is driven by the `jdiff` task declared
in build.gradle in the project root.
-59
View File
@@ -1,59 +0,0 @@
JDiff Doclet
------------
Matthew Doar
mdoar@pobox.com
The JDiff doclet is used to generate a report describing the
difference between two public Java APIs.
The file jdiff.html contains the reference page for JDiff. The latest
version of JDiff can be downloaded at:
http://sourceforge.net/projects/javadiff
To use the Ant task on your own project, see example.xml. More examples
of using JDiff to compare the public APIs of J2SE1.3 and J2SE1.4 can
be seen at http://www.jdiff.org
For an example with the source distribution, run "ant" and
look at the HTML output in ./build/reports/example/changes.html
The page at ./build/reports/example/changes/com.acme.sp.SPImpl.html
shows what a typical page of changes looks like.
System Requirements
-------------------
JDiff has been tested with all releases of Java since J2SE1.2 but
releases of JDiff after 1.10.0 focus on JDK1.5.
License
-------
JDiff is licensed under the Lesser GNU General Public License (LGPL).
See the file LICENSE.txt.
Acknowledgements
----------------
JDiff uses Stuart D. Gathman's Java translation of Gene Myers' O(ND)
difference algorithm.
JDiff uses Xerces 1.4.2 from http://www.apache.org.
JDiff also includes a script to use the classdoc application from
http://classdoc.sourceforge.net or http://www.jensgulden.de, by Jens
Gulden, (mail@jensgulden.de), to call a doclet such as jdiff on a .jar
file rather than on source.
Many thanks to the reviewers at Sun and Vitria who gave feedback on early
versions of JDiff output, and also to the distillers of Laphroaig, and to
Arturo Fuente for his consistently fine cigars which helped inspire
much of this work.
Footnote:
If you are looking for a generalized diff tool for XML, try diffmk from
http://wwws.sun.com/software/xml/developers/diffmk/
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

Binary file not shown.
-51
View File
@@ -1,51 +0,0 @@
apply plugin: "propdeps-maven"
install {
repositories.mavenInstaller {
customizePom(pom, project)
}
}
def customizePom(pom, gradleProject) {
pom.whenConfigured { generatedPom ->
// eliminate test-scoped dependencies (no need in maven central poms)
generatedPom.dependencies.removeAll { dep ->
dep.scope == "test"
}
// sort to make pom dependencies order consistent to ease comparison of older poms
generatedPom.dependencies = generatedPom.dependencies.sort { dep ->
"$dep.scope:$dep.groupId:$dep.artifactId"
}
// add all items necessary for maven central publication
generatedPom.project {
name = gradleProject.description
description = gradleProject.description
url = "https://github.com/SpringSource/spring-framework"
organization {
name = "SpringSource"
url = "http://springsource.org/spring-framework"
}
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/SpringSource/spring-framework"
connection = "scm:git:git://github.com/SpringSource/spring-framework"
developerConnection = "scm:git:git://github.com/SpringSource/spring-framework"
}
developers {
developer {
id = "jhoeller"
name = "Juergen Hoeller"
email = "jhoeller@vmware.com"
}
}
}
}
}
Binary file not shown.
+6 -5
View File
@@ -1,6 +1,7 @@
#Mon May 13 17:38:05 CDT 2013
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionBase=GRADLE_USER_HOME
distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+205 -119
View File
@@ -1,80 +1,129 @@
#!/usr/bin/env bash
#!/bin/sh
#
# Copyright © 2015-2021 the original 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
#
# https://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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
GRADLE_OPTS="-XX:MaxPermSize=1024m -Xmx1024m $GRADLE_OPTS"
DEFAULT_JVM_OPTS=""
# Attempt to set APP_HOME
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn ( ) {
warn () {
echo "$*"
}
} >&2
die ( ) {
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# 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
;;
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=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
CLASSPATH="\\\"\\\""
# 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"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -83,83 +132,120 @@ 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.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
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
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" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
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"
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+47 -44
View File
@@ -1,4 +1,22 @@
@if "%DEBUG%" == "" @echo off
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -8,27 +26,30 @@
@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 GRADLE_OPTS=-XX:MaxPermSize=1024m -Xmx1024m -XX:MaxHeapSize=256m %GRADLE_OPTS%
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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="-Xmx64m" "-Xms64m"
@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
if %ERRORLEVEL% equ 0 goto execute
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.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -36,54 +57,36 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
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.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
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
set CLASSPATH=
@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%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 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
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
-117
View File
@@ -1,117 +0,0 @@
@echo off
set STS_TEST_VERSION='2.9.2.RELEASE'
set CURRENT_DIR=%~dp0
cd %CURRENT_DIR%
cls
echo.
echo -----------------------------------------------------------------------
echo Spring Framework Eclipse/STS project import guide
echo.
echo This script will guide you through the process of importing the
echo Spring Framework sources into Eclipse/STS. It is recommended that you
echo have a recent version of the SpringSource Tool Suite (this script has
echo been tested against STS %STS_TEST_VERSION%), but at the minimum you will
echo need Eclipse + AJDT.
echo.
echo If you need to download and install STS, please do that now by
echo visiting http://springsource.org/downloads/sts
echo.
echo Otherwise, press enter and we'll begin.
pause
REM this command:
REM - wipes out any existing Eclipse metadata
REM - generates OXM test classes to avoid errors on import into Eclipse
REM - generates metadata for all subprojects
REM - skips metadata gen for the root project (-x :eclipse) to work
REM around Eclipse's inability to import hierarchical project structures
REM SET COMMAND="./gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse"
SET COMMAND=gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse
echo.
echo -----------------------------------------------------------------------
echo STEP 1: Generate subproject Eclipse metadata
echo.
echo The first step will be to generate Eclipse project metadata for each
echo of the spring-* subprojects. This happens via the built-in
echo "Gradle wrapper" script (./gradlew in this directory). If this is your
echo first time using the Gradle wrapper, this step may take a few minutes
echo while a Gradle distribution is downloaded for you.
echo.
echo The command run will be:
echo.
echo %COMMAND%
echo.
echo Press enter when ready.
pause
call %COMMAND%
if not "%ERRORLEVEL%" == "0" exit /B %ERRORLEVEL%
echo.
echo -----------------------------------------------------------------------
echo STEP 2: Import subprojects into Eclipse/STS
echo.
echo Within Eclipse/STS, do the following:
echo.
echo File ^> Import... ^> Existing Projects into Workspace
echo ^> When prompted for the 'root directory', provide %CURRENT_DIR%
echo ^> Press enter. You will see the modules show up under "Projects"
echo ^> All projects should be selected/checked. Click Finish.
echo ^> When the project import is complete, you should have no errors.
echo.
echo When the above is complete, return here and press the enter key.
pause
set COMMAND=gradlew :eclipse
echo.
echo -----------------------------------------------------------------------
echo STEP 3: generate root project Eclipse metadata
echo.
echo Unfortunately, Eclipse does not allow for importing project
echo hierarchies, so we had to skip root project metadata generation in the
echo during step 1. In this step we simply generate root project metadata
echo so you can import it in the next step.
echo.
echo The command run will be:
echo.
echo %COMMAND%
echo.
echo Press the enter key when ready.
pause
call %COMMAND%
if not "%ERRORLEVEL%" == "0" exit /B %ERRORLEVEL%
echo.
echo -----------------------------------------------------------------------
echo STEP 4: Import root project into Eclipse/STS
echo.
echo Follow the project import steps listed in step 2 above to import the
echo root project.
echo.
echo Press enter when complete, and move on to the final step.
pause
echo.
echo -----------------------------------------------------------------------
echo STEP 5: Enable Git support for all projects
echo.
echo - In the Eclipse/STS Package Explorer, select all spring* projects.
echo - Right-click to open the context menu and select Team ^> Share Project...
echo - In the Share Project dialog that appears, select Git and press Next
echo - Check "Use or create repository in parent folder of project"
echo - Click Finish
echo.
echo When complete, you'll have Git support enabled for all projects.
echo.
echo You're ready to code! Goodbye!
-120
View File
@@ -1,120 +0,0 @@
STS_TEST_VERSION='2.9.2.RELEASE'
cd `dirname $0`
clear
cat <<EOM
-----------------------------------------------------------------------
Spring Framework Eclipse/STS project import guide
This script will guide you through the process of importing the
Spring Framework sources into Eclipse/STS. It is recommended that you
have a recent version of the SpringSource Tool Suite (this script has
been tested against STS $STS_TEST_VERSION), but at the minimum you will
need Eclipse + AJDT.
If you need to download and install STS, please do that now by
visiting http://springsource.org/downloads/sts
Otherwise, press enter and we'll begin.
EOM
read
# this command:
# - wipes out any existing Eclipse metadata
# - generates OXM test classes to avoid errors on import into Eclipse
# - generates metadata for all subprojects
# - skips metadata gen for the root project (-x :eclipse) to work
# around Eclipse's inability to import hierarchical project structures
COMMAND="./gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse"
cat <<EOM
-----------------------------------------------------------------------
STEP 1: Generate subproject Eclipse metadata
The first step will be to generate Eclipse project metadata for each
of the spring-* subprojects. This happens via the built-in
"Gradle wrapper" script (./gradlew in this directory). If this is your
first time using the Gradle wrapper, this step may take a few minutes
while a Gradle distribution is downloaded for you.
The command run will be:
$COMMAND
Press enter when ready.
EOM
read
$COMMAND || exit
cat <<EOM
-----------------------------------------------------------------------
STEP 2: Import subprojects into Eclipse/STS
Within Eclipse/STS, do the following:
File > Import... > Existing Projects into Workspace
> When prompted for the 'root directory', provide $PWD
> Press enter. You will see the modules show up under "Projects"
> All projects should be selected/checked. Click Finish.
> When the project import is complete, you should have no errors.
When the above is complete, return here and press the enter key.
EOM
read
COMMAND="./gradlew :eclipse"
cat <<EOM
-----------------------------------------------------------------------
STEP 3: generate root project Eclipse metadata
Unfortunately, Eclipse does not allow for importing project
hierarchies, so we had to skip root project metadata generation in the
during step 1. In this step we simply generate root project metadata
so you can import it in the next step.
The command run will be:
$COMMAND
Press the enter key when ready.
EOM
read
$COMMAND || exit
cat <<EOM
-----------------------------------------------------------------------
STEP 4: Import root project into Eclipse/STS
Follow the project import steps listed in step 2 above to import the
root project.
Press enter when complete, and move on to the final step.
EOM
read
cat <<EOM
-----------------------------------------------------------------------
STEP 5: Enable Git support for all projects
- In the Eclipse/STS Package Explorer, select all spring* projects.
- Right-click to open the context menu and select Team > Share Project...
- In the Share Project dialog that appears, select Git and press Next
- Check "Use or create repository in parent folder of project"
- Click Finish
When complete, you'll have Git support enabled for all projects.
You're ready to code! Goodbye!
EOM
-32
View File
@@ -1,32 +0,0 @@
The following has been tested against Intellij IDEA 12.0
## Steps
_Within your locally cloned spring-framework working directory:_
1. Generate IDEA metadata with `./gradlew :spring-oxm:compileTestJava cleanIdea idea`
2. Import into IDEA as usual
3. Set the Project JDK as appropriate
4. Add git support
5. Code away
## Known issues
1. `spring-aspects` does not compile out of the box due to references to aspect types unknown to IDEA.
See http://youtrack.jetbrains.com/issue/IDEA-64446 for details. In the meantime, the 'spring-aspects'
module has been excluded from the overall project to avoid compilation errors.
2. While all JUnit tests pass from the command line with Gradle, many will fail when run from IDEA.
Resolving this is a work in progress. If attempting to run all JUnit tests from within IDEA, you will
likely need to set the following VM options to avoid out of memory errors:
-XX:MaxPermSize=2048m -Xmx2048m -XX:MaxHeapSize=2048m
## Tips
In any case, please do not check in your own generated .iml, .ipr, or .iws files.
You'll notice these files are already intentionally in .gitignore. The same policy goes for eclipse metadata.
## FAQ
Q. What about IDEA's own [Gradle support](http://confluence.jetbrains.net/display/IDEADEV/Gradle+integration)?
A. Keep an eye on http://youtrack.jetbrains.com/issue/IDEA-53476
+11
View File
@@ -0,0 +1,11 @@
{
"dependencies": {
"antora": "3.2.0-alpha.12",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.12",
"fast-xml-parser": "5.7.0",
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
}
}
+3 -27
View File
@@ -1,28 +1,4 @@
rootProject.name = "spring"
plugins {
id "org.gradle.toolchains.foojay-resolver-convention" version "1.0.0"
}
include "spring-aop"
include "spring-aspects"
include "spring-beans"
include "spring-context"
include "spring-context-support"
include "spring-core"
include "spring-expression"
include "spring-instrument"
include "spring-instrument-tomcat"
include "spring-jdbc"
include "spring-jms"
include "spring-orm"
include "spring-orm-hibernate4"
include "spring-oxm"
include "spring-test"
include "spring-test-mvc"
include "spring-tx"
include "spring-web"
include "spring-webmvc"
include "spring-webmvc-portlet"
include "spring-webmvc-tiles3"
include "spring-websocket"
// Exposes gradle buildSrc for IDE support
include "buildSrc"
rootProject.children.find{ it.name == "buildSrc" }.name = "spring-build-src"
@@ -1,60 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import org.aopalliance.aop.Advice;
/**
* Base interface holding AOP <b>advice</b> (action to take at a joinpoint)
* and a filter determining the applicability of the advice (such as
* a pointcut). <i>This interface is not for use by Spring users, but to
* allow for commonality in support for different types of advice.</i>
*
* <p>Spring AOP is based around <b>around advice</b> delivered via method
* <b>interception</b>, compliant with the AOP Alliance interception API.
* The Advisor interface allows support for different types of advice,
* such as <b>before</b> and <b>after</b> advice, which need not be
* implemented using interception.
*
* @author Rod Johnson
*/
public interface Advisor {
/**
* Return the advice part of this aspect. An advice may be an
* interceptor, a before advice, a throws advice, etc.
* @return the advice that should apply if the pointcut matches
* @see org.aopalliance.intercept.MethodInterceptor
* @see BeforeAdvice
* @see ThrowsAdvice
* @see AfterReturningAdvice
*/
Advice getAdvice();
/**
* Return whether this advice is associated with a particular instance
* (for example, creating a mixin) or shared with all instances of
* the advised class obtained from the same Spring bean factory.
* <p><b>Note that this method is not currently used by the framework.</b>
* Typical Advisor implementations always return {@code true}.
* Use singleton/prototype bean definitions or appropriate programmatic
* proxy creation to ensure that Advisors have the correct lifecycle model.
* @return whether this advice is associated with a particular target instance
*/
boolean isPerInstance();
}
@@ -1,31 +0,0 @@
/*
* Copyright 2002-2007 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.aop;
import org.aopalliance.aop.Advice;
/**
* Common marker interface for after advice,
* such as {@link AfterReturningAdvice} and {@link ThrowsAdvice}.
*
* @author Juergen Hoeller
* @since 2.0.3
* @see BeforeAdvice
*/
public interface AfterAdvice extends Advice {
}
@@ -1,44 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.lang.reflect.Method;
/**
* After returning advice is invoked only on normal method return, not if an
* exception is thrown. Such advice can see the return value, but cannot change it.
*
* @author Rod Johnson
* @see MethodBeforeAdvice
* @see ThrowsAdvice
*/
public interface AfterReturningAdvice extends AfterAdvice {
/**
* Callback after a given method successfully returned.
* @param returnValue the value returned by the method, if any
* @param method method being invoked
* @param args arguments to the method
* @param target target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}
@@ -1,48 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import org.springframework.core.NestedRuntimeException;
/**
* Exception that gets thrown when an AOP invocation failed
* because of misconfiguration or unexpected runtime issues.
*
* @author Juergen Hoeller
* @since 2.0
*/
@SuppressWarnings("serial")
public class AopInvocationException extends NestedRuntimeException {
/**
* Constructor for AopInvocationException.
* @param msg the detail message
*/
public AopInvocationException(String msg) {
super(msg);
}
/**
* Constructor for AopInvocationException.
* @param msg the detail message
* @param cause the root cause
*/
public AopInvocationException(String msg, Throwable cause) {
super(msg, cause);
}
}
@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.aop;
import org.aopalliance.aop.Advice;
/**
* Common marker interface for before advice, such as {@link MethodBeforeAdvice}.
*
* <p>Spring supports only method before advice. Although this is unlikely to change,
* this API is designed to allow field advice in future if desired.
*
* @author Rod Johnson
* @see AfterAdvice
*/
public interface BeforeAdvice extends Advice {
}
@@ -1,45 +0,0 @@
/*
* Copyright 2002-2007 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.aop;
/**
* Filter that restricts matching of a pointcut or introduction to
* a given set of target classes.
*
* <p>Can be used as part of a {@link Pointcut} or for the entire
* targeting of an {@link IntroductionAdvisor}.
*
* @author Rod Johnson
* @see Pointcut
* @see MethodMatcher
*/
public interface ClassFilter {
/**
* Should the pointcut apply to the given interface or target class?
* @param clazz the candidate target class
* @return whether the advice should apply to the given target class
*/
boolean matches(Class<?> clazz);
/**
* Canonical instance of a ClassFilter that matches all classes.
*/
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
@@ -1,48 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import org.aopalliance.aop.Advice;
/**
* Subinterface of AOP Alliance Advice that allows additional interfaces
* to be implemented by an Advice, and available via a proxy using that
* interceptor. This is a fundamental AOP concept called <b>introduction</b>.
*
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
* objects that can achieve many of the goals of multiple inheritance in Java.
*
* <p>Compared to {qlink IntroductionInfo}, this interface allows an advice to
* implement a range of interfaces that is not necessarily known in advance.
* Thus an {@link IntroductionAdvisor} can be used to specify which interfaces
* will be exposed in an advised object.
*
* @author Rod Johnson
* @since 1.1.1
* @see IntroductionInfo
* @see IntroductionAdvisor
*/
public interface DynamicIntroductionAdvice extends Advice {
/**
* Does this introduction advice implement the given interface?
* @param intf the interface to check
* @return whether the advice implements the specified interface
*/
boolean implementsInterface(Class<?> intf);
}
@@ -1,51 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
/**
* Superinterface for advisors that perform one or more AOP <b>introductions</b>.
*
* <p>This interface cannot be implemented directly; subinterfaces must
* provide the advice type implementing the introduction.
*
* <p>Introduction is the implementation of additional interfaces
* (not implemented by a target) via AOP advice.
*
* @author Rod Johnson
* @since 04.04.2003
* @see IntroductionInterceptor
*/
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
/**
* Return the filter determining which target classes this introduction
* should apply to.
* <p>This represents the class part of a pointcut. Note that method
* matching doesn't make sense to introductions.
* @return the class filter
*/
ClassFilter getClassFilter();
/**
* Can the advised interfaces be implemented by the introduction advice?
* Invoked before adding an IntroductionAdvisor.
* @throws IllegalArgumentException if the advised interfaces can't be
* implemented by the introduction advice
*/
void validateInterfaces() throws IllegalArgumentException;
}
@@ -1,44 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.lang.reflect.Method;
/**
* A specialized type of MethodMatcher that takes into account introductions when
* matching methods. If there are no introductions on the target class, a method
* matcher may be able to optimize matching more effectively for example.
*
* @author Adrian Colyer
* @since 2.0
*/
public interface IntroductionAwareMethodMatcher extends MethodMatcher {
/**
* Perform static checking whether the given method matches. This may be invoked
* instead of the 2-arg {@link #matches(java.lang.reflect.Method, Class)} method
* if the caller supports the extended IntroductionAwareMethodMatcher interface.
* @param method the candidate method
* @param targetClass the target class (may be {@code null}, in which case
* the candidate class must be taken to be the method's declaring class)
* @param hasIntroductions {@code true} if the object on whose behalf we are
* asking is the subject on one or more introductions; {@code false} otherwise
* @return whether or not this method matches statically
*/
boolean matches(Method method, Class targetClass, boolean hasIntroductions);
}
@@ -1,39 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
/**
* Interface supplying the information necessary to describe an introduction.
*
* <p>{@link IntroductionAdvisor IntroductionAdvisors} must implement this
* interface. If an {@link org.aopalliance.aop.Advice} implements this,
* it may be used as an introduction without an {@link IntroductionAdvisor}.
* In this case, the advice is self-describing, providing not only the
* necessary behavior, but describing the interfaces it introduces.
*
* @author Rod Johnson
* @since 1.1.1
*/
public interface IntroductionInfo {
/**
* Return the additional interfaces introduced by this Advisor or Advice.
* @return the introduced interfaces
*/
Class[] getInterfaces();
}
@@ -1,34 +0,0 @@
/*
* Copyright 2002-2007 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.aop;
import org.aopalliance.intercept.MethodInterceptor;
/**
* Subinterface of AOP Alliance MethodInterceptor that allows additional interfaces
* to be implemented by the interceptor, and available via a proxy using that
* interceptor. This is a fundamental AOP concept called <b>introduction</b>.
*
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
* objects that can achieve many of the goals of multiple inheritance in Java.
*
* @author Rod Johnson
* @see DynamicIntroductionAdvice
*/
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
}
@@ -1,44 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.lang.reflect.Method;
/**
* Advice invoked before a method is invoked. Such advices cannot
* prevent the method call proceeding, unless they throw a Throwable.
*
* @see AfterReturningAdvice
* @see ThrowsAdvice
*
* @author Rod Johnson
*/
public interface MethodBeforeAdvice extends BeforeAdvice {
/**
* Callback before a given method is invoked.
* @param method method being invoked
* @param args arguments to the method
* @param target target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void before(Method method, Object[] args, Object target) throws Throwable;
}
@@ -1,97 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.lang.reflect.Method;
/**
* Part of a {@link Pointcut}: Checks whether the target method is eligible for advice.
*
* <p>A MethodMatcher may be evaluated <b>statically</b> or at <b>runtime</b> (dynamically).
* Static matching involves method and (possibly) method attributes. Dynamic matching
* also makes arguments for a particular call available, and any effects of running
* previous advice applying to the joinpoint.
*
* <p>If an implementation returns {@code false} from its {@link #isRuntime()}
* method, evaluation can be performed statically, and the result will be the same
* for all invocations of this method, whatever their arguments. This means that
* if the {@link #isRuntime()} method returns {@code false}, the 3-arg
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method will never be invoked.
*
* <p>If an implementation returns {@code true} from its 2-arg
* {@link #matches(java.lang.reflect.Method, Class)} method and its {@link #isRuntime()} method
* returns {@code true}, the 3-arg {@link #matches(java.lang.reflect.Method, Class, Object[])}
* method will be invoked <i>immediately before each potential execution of the related advice</i>,
* to decide whether the advice should run. All previous advice, such as earlier interceptors
* in an interceptor chain, will have run, so any state changes they have produced in
* parameters or ThreadLocal state will be available at the time of evaluation.
*
* @author Rod Johnson
* @since 11.11.2003
* @see Pointcut
* @see ClassFilter
*/
public interface MethodMatcher {
/**
* Perform static checking whether the given method matches. If this
* returns {@code false} or if the {@link #isRuntime()} method
* returns {@code false}, no runtime check (i.e. no.
* {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made.
* @param method the candidate method
* @param targetClass the target class (may be {@code null}, in which case
* the candidate class must be taken to be the method's declaring class)
* @return whether or not this method matches statically
*/
boolean matches(Method method, Class<?> targetClass);
/**
* Is this MethodMatcher dynamic, that is, must a final call be made on the
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
* runtime even if the 2-arg matches method returns {@code true}?
* <p>Can be invoked when an AOP proxy is created, and need not be invoked
* again before each method invocation,
* @return whether or not a runtime match via the 3-arg
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method
* is required if static matching passed
*/
boolean isRuntime();
/**
* Check whether there a runtime (dynamic) match for this method,
* which must have matched statically.
* <p>This method is invoked only if the 2-arg matches method returns
* {@code true} for the given method and target class, and if the
* {@link #isRuntime()} method returns {@code true}. Invoked
* immediately before potential running of the advice, after any
* advice earlier in the advice chain has run.
* @param method the candidate method
* @param targetClass the target class (may be {@code null}, in which case
* the candidate class must be taken to be the method's declaring class)
* @param args arguments to the method
* @return whether there's a runtime match
* @see MethodMatcher#matches(Method, Class)
*/
boolean matches(Method method, Class<?> targetClass, Object[] args);
/**
* Canonical instance that matches all methods.
*/
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
@@ -1,53 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
/**
* Core Spring pointcut abstraction.
*
* <p>A pointcut is composed of a {@link ClassFilter} and a {@link MethodMatcher}.
* Both these basic terms and a Pointcut itself can be combined to build up combinations
* (e.g. through {@link org.springframework.aop.support.ComposablePointcut}).
*
* @author Rod Johnson
* @see ClassFilter
* @see MethodMatcher
* @see org.springframework.aop.support.Pointcuts
* @see org.springframework.aop.support.ClassFilters
* @see org.springframework.aop.support.MethodMatchers
*/
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
@@ -1,33 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
/**
* Superinterface for all Advisors that are driven by a pointcut.
* This covers nearly all advisors except introduction advisors,
* for which method-level matching doesn't apply.
*
* @author Rod Johnson
*/
public interface PointcutAdvisor extends Advisor {
/**
* Get the Pointcut that drives this advisor.
*/
Pointcut getPointcut();
}
@@ -1,86 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import org.aopalliance.intercept.MethodInvocation;
/**
* Extension of the AOP Alliance {@link org.aopalliance.intercept.MethodInvocation}
* interface, allowing access to the proxy that the method invocation was made through.
*
* <p>Useful to be able to substitute return values with the proxy,
* if necessary, for example if the invocation target returned itself.
*
* @author Juergen Hoeller
* @author Adrian Colyer
* @since 1.1.3
* @see org.springframework.aop.framework.ReflectiveMethodInvocation
* @see org.springframework.aop.support.DelegatingIntroductionInterceptor
*/
public interface ProxyMethodInvocation extends MethodInvocation {
/**
* Return the proxy that this method invocation was made through.
* @return the original proxy object
*/
Object getProxy();
/**
* Create a clone of this object. If cloning is done before {@code proceed()}
* is invoked on this object, {@code proceed()} can be invoked once per clone
* to invoke the joinpoint (and the rest of the advice chain) more than once.
* @return an invocable clone of this invocation.
* {@code proceed()} can be called once per clone.
*/
MethodInvocation invocableClone();
/**
* Create a clone of this object. If cloning is done before {@code proceed()}
* is invoked on this object, {@code proceed()} can be invoked once per clone
* to invoke the joinpoint (and the rest of the advice chain) more than once.
* @param arguments the arguments that the cloned invocation is supposed to use,
* overriding the original arguments
* @return an invocable clone of this invocation.
* {@code proceed()} can be called once per clone.
*/
MethodInvocation invocableClone(Object[] arguments);
/**
* Set the arguments to be used on subsequent invocations in the any advice
* in this chain.
* @param arguments the argument array
*/
void setArguments(Object[] arguments);
/**
* Add the specified user attribute with the given value to this invocation.
* <p>Such attributes are not used within the AOP framework itself. They are
* just kept as part of the invocation object, for use in special interceptors.
* @param key the name of the attribute
* @param value the value of the attribute, or {@code null} to reset it
*/
void setUserAttribute(String key, Object value);
/**
* Return the value of the specified user attribute.
* @param key the name of the attribute
* @return the value of the attribute, or {@code null} if not set
* @see #setUserAttribute
*/
Object getUserAttribute(String key);
}
@@ -1,37 +0,0 @@
/*
* Copyright 2002-2007 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.aop;
/**
* Marker for AOP proxy interfaces (in particular: introduction interfaces)
* that explicitly intend to return the raw target object (which would normally
* get replaced with the proxy object when returned from a method invocation).
*
* <p>Note that this is a marker interface in the style of {@link java.io.Serializable},
* semantically applying to a declared interface rather than to the full class
* of a concrete object. In other words, this marker applies to a particular
* interface only (typically an introduction interface that does not serve
* as the primary interface of an AOP proxy), and hence does not affect
* other interfaces that a concrete AOP proxy may implement.
*
* @author Juergen Hoeller
* @since 2.0.5
* @see org.springframework.aop.scope.ScopedObject
*/
public interface RawTargetAccess {
}
@@ -1,29 +0,0 @@
/*
* Copyright 2002-2007 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.aop;
/**
* Marker interface implemented by all AOP proxies. Used to detect
* whether or not objects are Spring-generated proxies.
*
* @author Rob Harrop
* @since 2.0.1
* @see org.springframework.aop.support.AopUtils#isAopProxy(Object)
*/
public interface SpringProxy {
}
@@ -1,39 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
/**
* Minimal interface for exposing the target class behind a proxy.
*
* <p>Implemented by AOP proxy objects and proxy factories
* (via {@link org.springframework.aop.framework.Advised}}
* as well as by {@link TargetSource TargetSources}.
*
* @author Juergen Hoeller
* @since 2.0.3
* @see org.springframework.aop.support.AopUtils#getTargetClass(Object)
*/
public interface TargetClassAware {
/**
* Return the target class behind the implementing object
* (typically a proxy configuration or an actual proxy).
* @return the target Class, or {@code null} if not known
*/
Class<?> getTargetClass();
}
@@ -1,71 +0,0 @@
/*<
* Copyright 2002-2012 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.aop;
/**
* A {@code TargetSource} is used to obtain the current "target" of
* an AOP invocation, which will be invoked via reflection if no around
* advice chooses to end the interceptor chain itself.
*
* <p>If a {@code TargetSource} is "static", it will always return
* the same target, allowing optimizations in the AOP framework. Dynamic
* target sources can support pooling, hot swapping, etc.
*
* <p>Application developers don't usually need to work with
* {@code TargetSources} directly: this is an AOP framework interface.
*
* @author Rod Johnson
*/
public interface TargetSource extends TargetClassAware {
/**
* Return the type of targets returned by this {@link TargetSource}.
* <p>Can return {@code null}, although certain usages of a
* {@code TargetSource} might just work with a predetermined
* target class.
* @return the type of targets returned by this {@link TargetSource}
*/
@Override
Class<?> getTargetClass();
/**
* Will all calls to {@link #getTarget()} return the same object?
* <p>In that case, there will be no need to invoke
* {@link #releaseTarget(Object)}, and the AOP framework can cache
* the return value of {@link #getTarget()}.
* @return {@code true} if the target is immutable
* @see #getTarget
*/
boolean isStatic();
/**
* Return a target instance. Invoked immediately before the
* AOP framework calls the "target" of an AOP method invocation.
* @return the target object, which contains the joinpoint
* @throws Exception if the target object can't be resolved
*/
Object getTarget() throws Exception;
/**
* Release the given target object obtained from the
* {@link #getTarget()} method.
* @param target object obtained from a call to {@link #getTarget()}
* @throws Exception if the object can't be released
*/
void releaseTarget(Object target) throws Exception;
}
@@ -1,53 +0,0 @@
/*
* Copyright 2002-2008 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.aop;
/**
* Tag interface for throws advice.
*
* <p>There are not any methods on this interface, as methods are invoked by
* reflection. Implementing classes must implement methods of the form:
*
* <pre class="code">void afterThrowing([Method, args, target], ThrowableSubclass);</pre>
*
* <p>Some examples of valid methods would be:
*
* <pre class="code">public void afterThrowing(Exception ex)</pre>
* <pre class="code">public void afterThrowing(RemoteException)</pre>
* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
*
* The first three arguments are optional, and only useful if we want further
* information about the joinpoint, as in AspectJ <b>after-throwing</b> advice.
*
* <p><b>Note:</b> If a throws-advice method throws an exception itself, it will
* override the original exception (i.e. change the exception thrown to the user).
* The overriding exception will typically be a RuntimeException; this is compatible
* with any method signature. However, if a throws-advice method throws a checked
* exception, it will have to match the declared exceptions of the target method
* and is hence to some degree coupled to specific target method signatures.
* <b>Do not throw an undeclared checked exception that is incompatible with
* the target method's signature!</b>
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see AfterReturningAdvice
* @see MethodBeforeAdvice
*/
public interface ThrowsAdvice extends AfterAdvice {
}
@@ -1,56 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.io.Serializable;
/**
* Canonical ClassFilter instance that matches all classes.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
class TrueClassFilter implements ClassFilter, Serializable {
public static final TrueClassFilter INSTANCE = new TrueClassFilter();
/**
* Enforce Singleton pattern.
*/
private TrueClassFilter() {
}
@Override
public boolean matches(Class clazz) {
return true;
}
/**
* Required to support serialization. Replaces with canonical
* instance on deserialization, protecting Singleton pattern.
* Alternative to overriding {@code equals()}.
*/
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "ClassFilter.TRUE";
}
}
@@ -1,68 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.io.Serializable;
import java.lang.reflect.Method;
/**
* Canonical MethodMatcher instance that matches all methods.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
class TrueMethodMatcher implements MethodMatcher, Serializable {
public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();
/**
* Enforce Singleton pattern.
*/
private TrueMethodMatcher() {
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class targetClass) {
return true;
}
@Override
public boolean matches(Method method, Class targetClass, Object[] args) {
// Should never be invoked as isRuntime returns false.
throw new UnsupportedOperationException();
}
/**
* Required to support serialization. Replaces with canonical
* instance on deserialization, protecting Singleton pattern.
* Alternative to overriding {@code equals()}.
*/
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "MethodMatcher.TRUE";
}
}
@@ -1,61 +0,0 @@
/*
* Copyright 2002-2012 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.aop;
import java.io.Serializable;
/**
* Canonical Pointcut instance that always matches.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
class TruePointcut implements Pointcut, Serializable {
public static final TruePointcut INSTANCE = new TruePointcut();
/**
* Enforce Singleton pattern.
*/
private TruePointcut() {
}
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
/**
* Required to support serialization. Replaces with canonical
* instance on deserialization, protecting Singleton pattern.
* Alternative to overriding {@code equals()}.
*/
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "Pointcut.TRUE";
}
}
@@ -1,707 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.aspectj.weaver.tools.PointcutParameter;
import org.springframework.aop.AopInvocationException;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.MethodMatchers;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PrioritizedParameterNameDiscoverer;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Base class for AOP Alliance {@link org.aopalliance.aop.Advice} classes
* wrapping an AspectJ aspect or an AspectJ-annotated advice method.
*
* @author Rod Johnson
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @since 2.0
*/
public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation {
/**
* Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint.
*/
protected static final String JOIN_POINT_KEY = JoinPoint.class.getName();
/**
* Lazily instantiate joinpoint for the current invocation.
* Requires MethodInvocation to be bound with ExposeInvocationInterceptor.
* <p>Do not use if access is available to the current ReflectiveMethodInvocation
* (in an around advice).
* @return current AspectJ joinpoint, or through an exception if we're not in a
* Spring AOP invocation.
*/
public static JoinPoint currentJoinPoint() {
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
if (jp == null) {
jp = new MethodInvocationProceedingJoinPoint(pmi);
pmi.setUserAttribute(JOIN_POINT_KEY, jp);
}
return jp;
}
protected final Method aspectJAdviceMethod;
/** The total number of arguments we have to populate on advice dispatch */
private final int adviceInvocationArgumentCount;
private final AspectJExpressionPointcut pointcut;
private final AspectInstanceFactory aspectInstanceFactory;
/**
* The name of the aspect (ref bean) in which this advice was defined (used
* when determining advice precedence so that we can determine
* whether two pieces of advice come from the same aspect).
*/
private String aspectName;
/**
* The order of declaration of this advice within the aspect.
*/
private int declarationOrder;
/**
* This will be non-null if the creator of this advice object knows the argument names
* and sets them explicitly
*/
private String[] argumentNames = null;
/** Non-null if after throwing advice binds the thrown value */
private String throwingName = null;
/** Non-null if after returning advice binds the return value */
private String returningName = null;
private Class discoveredReturningType = Object.class;
private Class discoveredThrowingType = Object.class;
/**
* Index for thisJoinPoint argument (currently only
* supported at index 0 if present at all)
*/
private int joinPointArgumentIndex = -1;
/**
* Index for thisJoinPointStaticPart argument (currently only
* supported at index 0 if present at all)
*/
private int joinPointStaticPartArgumentIndex = -1;
private Map<String, Integer> argumentBindings = null;
private boolean argumentsIntrospected = false;
private Type discoveredReturningGenericType;
// Note: Unlike return type, no such generic information is needed for the throwing type,
// since Java doesn't allow exception types to be parameterized.
/**
* Create a new AbstractAspectJAdvice for the given advice method.
* @param aspectJAdviceMethod the AspectJ-style advice method
* @param pointcut the AspectJ expression pointcut
* @param aspectInstanceFactory the factory for aspect instances
*/
public AbstractAspectJAdvice(
Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.adviceInvocationArgumentCount = this.aspectJAdviceMethod.getParameterTypes().length;
this.pointcut = pointcut;
this.aspectInstanceFactory = aspectInstanceFactory;
}
/**
* Return the AspectJ-style advice method.
*/
public final Method getAspectJAdviceMethod() {
return this.aspectJAdviceMethod;
}
/**
* Return the AspectJ expression pointcut.
*/
public final AspectJExpressionPointcut getPointcut() {
calculateArgumentBindings();
return this.pointcut;
}
/**
* Build a 'safe' pointcut that excludes the AspectJ advice method itself.
* @return a composable pointcut that builds on the original AspectJ expression pointcut
* @see #getPointcut()
*/
public final Pointcut buildSafePointcut() {
Pointcut pc = getPointcut();
MethodMatcher safeMethodMatcher = MethodMatchers.intersection(
new AdviceExcludingMethodMatcher(this.aspectJAdviceMethod), pc.getMethodMatcher());
return new ComposablePointcut(pc.getClassFilter(), safeMethodMatcher);
}
/**
* Return the factory for aspect instances.
*/
public final AspectInstanceFactory getAspectInstanceFactory() {
return this.aspectInstanceFactory;
}
/**
* Return the ClassLoader for aspect instances.
*/
public final ClassLoader getAspectClassLoader() {
return this.aspectInstanceFactory.getAspectClassLoader();
}
@Override
public int getOrder() {
return this.aspectInstanceFactory.getOrder();
}
public void setAspectName(String name) {
this.aspectName = name;
}
@Override
public String getAspectName() {
return this.aspectName;
}
/**
* Sets the <b>declaration order</b> of this advice within the aspect
*/
public void setDeclarationOrder(int order) {
this.declarationOrder = order;
}
@Override
public int getDeclarationOrder() {
return this.declarationOrder;
}
/**
* Set by creator of this advice object if the argument names are known.
* <p>This could be for example because they have been explicitly specified in XML,
* or in an advice annotation.
* @param argNames comma delimited list of arg names
*/
public void setArgumentNames(String argNames) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(argNames);
setArgumentNamesFromStringArray(tokens);
}
public void setArgumentNamesFromStringArray(String[] args) {
this.argumentNames = new String[args.length];
for (int i = 0; i < args.length; i++) {
this.argumentNames[i] = StringUtils.trimWhitespace(args[i]);
if (!isVariableName(this.argumentNames[i])) {
throw new IllegalArgumentException(
"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
this.argumentNames[i] + "' that is not a valid Java identifier");
}
}
if (argumentNames != null) {
if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) {
// May need to add implicit join point arg name...
Class firstArgType = aspectJAdviceMethod.getParameterTypes()[0];
if (firstArgType == JoinPoint.class ||
firstArgType == ProceedingJoinPoint.class ||
firstArgType == JoinPoint.StaticPart.class) {
String[] oldNames = argumentNames;
argumentNames = new String[oldNames.length + 1];
argumentNames[0] = "THIS_JOIN_POINT";
System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length);
}
}
}
}
public void setReturningName(String name) {
throw new UnsupportedOperationException("Only afterReturning advice can be used to bind a return value");
}
/**
* We need to hold the returning name at this level for argument binding calculations,
* this method allows the afterReturning advice subclass to set the name.
*/
protected void setReturningNameNoCheck(String name) {
// name could be a variable or a type...
if (isVariableName(name)) {
this.returningName = name;
}
else {
// assume a type
try {
this.discoveredReturningType = ClassUtils.forName(name, getAspectClassLoader());
}
catch (Throwable ex) {
throw new IllegalArgumentException("Returning name '" + name +
"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
"Root cause: " + ex);
}
}
}
protected Class getDiscoveredReturningType() {
return this.discoveredReturningType;
}
protected Type getDiscoveredReturningGenericType() {
return this.discoveredReturningGenericType;
}
public void setThrowingName(String name) {
throw new UnsupportedOperationException("Only afterThrowing advice can be used to bind a thrown exception");
}
/**
* We need to hold the throwing name at this level for argument binding calculations,
* this method allows the afterThrowing advice subclass to set the name.
*/
protected void setThrowingNameNoCheck(String name) {
// name could be a variable or a type...
if (isVariableName(name)) {
this.throwingName = name;
}
else {
// assume a type
try {
this.discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader());
}
catch (Throwable ex) {
throw new IllegalArgumentException("Throwing name '" + name +
"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
"Root cause: " + ex);
}
}
}
protected Class getDiscoveredThrowingType() {
return this.discoveredThrowingType;
}
private boolean isVariableName(String name) {
char[] chars = name.toCharArray();
if (!Character.isJavaIdentifierStart(chars[0])) {
return false;
}
for (int i = 1; i < chars.length; i++) {
if (!Character.isJavaIdentifierPart(chars[i])) {
return false;
}
}
return true;
}
/**
* Do as much work as we can as part of the set-up so that argument binding
* on subsequent advice invocations can be as fast as possible.
* <p>If the first argument is of type JoinPoint or ProceedingJoinPoint then we
* pass a JoinPoint in that position (ProceedingJoinPoint for around advice).
* <p>If the first argument is of type {@code JoinPoint.StaticPart}
* then we pass a {@code JoinPoint.StaticPart} in that position.
* <p>Remaining arguments have to be bound by pointcut evaluation at
* a given join point. We will get back a map from argument name to
* value. We need to calculate which advice parameter needs to be bound
* to which argument name. There are multiple strategies for determining
* this binding, which are arranged in a ChainOfResponsibility.
*/
public synchronized final void calculateArgumentBindings() {
// The simple case... nothing to bind.
if (this.argumentsIntrospected || this.adviceInvocationArgumentCount == 0) {
return;
}
int numUnboundArgs = this.adviceInvocationArgumentCount;
Class[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
numUnboundArgs--;
}
else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
numUnboundArgs--;
}
if (numUnboundArgs > 0) {
// need to bind arguments by name as returned from the pointcut match
bindArgumentsByName(numUnboundArgs);
}
this.argumentsIntrospected = true;
}
private boolean maybeBindJoinPoint(Class candidateParameterType) {
if (candidateParameterType.equals(JoinPoint.class)) {
this.joinPointArgumentIndex = 0;
return true;
}
else {
return false;
}
}
private boolean maybeBindProceedingJoinPoint(Class candidateParameterType) {
if (candidateParameterType.equals(ProceedingJoinPoint.class)) {
if (!supportsProceedingJoinPoint()) {
throw new IllegalArgumentException("ProceedingJoinPoint is only supported for around advice");
}
this.joinPointArgumentIndex = 0;
return true;
}
else {
return false;
}
}
protected boolean supportsProceedingJoinPoint() {
return false;
}
private boolean maybeBindJoinPointStaticPart(Class candidateParameterType) {
if (candidateParameterType.equals(JoinPoint.StaticPart.class)) {
this.joinPointStaticPartArgumentIndex = 0;
return true;
}
else {
return false;
}
}
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
if (this.argumentNames == null) {
this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
}
if (this.argumentNames != null) {
// We have been able to determine the arg names.
bindExplicitArguments(numArgumentsExpectingToBind);
}
else {
throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
"the argument names were not specified and could not be discovered.");
}
}
/**
* Create a ParameterNameDiscoverer to be used for argument binding.
* <p>The default implementation creates a {@link PrioritizedParameterNameDiscoverer}
* containing a {@link LocalVariableTableParameterNameDiscoverer} and an
* {@link AspectJAdviceParameterNameDiscoverer}.
*/
protected ParameterNameDiscoverer createParameterNameDiscoverer() {
// We need to discover them, or if that fails, guess,
// and if we can't guess with 100% accuracy, fail.
PrioritizedParameterNameDiscoverer discoverer = new PrioritizedParameterNameDiscoverer();
discoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer =
new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression());
adviceParameterNameDiscoverer.setReturningName(this.returningName);
adviceParameterNameDiscoverer.setThrowingName(this.throwingName);
// Last in chain, so if we're called and we fail, that's bad...
adviceParameterNameDiscoverer.setRaiseExceptions(true);
discoverer.addDiscoverer(adviceParameterNameDiscoverer);
return discoverer;
}
private void bindExplicitArguments(int numArgumentsLeftToBind) {
this.argumentBindings = new HashMap<String, Integer>();
int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length;
if (this.argumentNames.length != numExpectedArgumentNames) {
throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames
+ " arguments to bind by name in advice, but actually found " +
this.argumentNames.length + " arguments.");
}
// So we match in number...
int argumentIndexOffset = this.adviceInvocationArgumentCount - numArgumentsLeftToBind;
for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {
this.argumentBindings.put(this.argumentNames[i], i);
}
// Check that returning and throwing were in the argument names list if
// specified, and find the discovered argument types.
if (this.returningName != null) {
if (!this.argumentBindings.containsKey(this.returningName)) {
throw new IllegalStateException("Returning argument name '"
+ this.returningName + "' was not bound in advice arguments");
}
else {
Integer index = this.argumentBindings.get(this.returningName);
this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index];
this.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index];
}
}
if (this.throwingName != null) {
if (!this.argumentBindings.containsKey(this.throwingName)) {
throw new IllegalStateException("Throwing argument name '"
+ this.throwingName + "' was not bound in advice arguments");
}
else {
Integer index = this.argumentBindings.get(this.throwingName);
this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index];
}
}
// configure the pointcut expression accordingly.
configurePointcutParameters(argumentIndexOffset);
}
/**
* All parameters from argumentIndexOffset onwards are candidates for
* pointcut parameters - but returning and throwing vars are handled differently
* and must be removed from the list if present.
*/
private void configurePointcutParameters(int argumentIndexOffset) {
int numParametersToRemove = argumentIndexOffset;
if (this.returningName != null) {
numParametersToRemove++;
}
if (this.throwingName != null) {
numParametersToRemove++;
}
String[] pointcutParameterNames = new String[this.argumentNames.length - numParametersToRemove];
Class[] pointcutParameterTypes = new Class[pointcutParameterNames.length];
Class[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes();
int index = 0;
for (int i = 0; i < this.argumentNames.length; i++) {
if (i < argumentIndexOffset) {
continue;
}
if (this.argumentNames[i].equals(this.returningName) ||
this.argumentNames[i].equals(this.throwingName)) {
continue;
}
pointcutParameterNames[index] = this.argumentNames[i];
pointcutParameterTypes[index] = methodParameterTypes[i];
index++;
}
this.pointcut.setParameterNames(pointcutParameterNames);
this.pointcut.setParameterTypes(pointcutParameterTypes);
}
/**
* Take the arguments at the method execution join point and output a set of arguments
* to the advice method
* @param jp the current JoinPoint
* @param jpMatch the join point match that matched this execution join point
* @param returnValue the return value from the method execution (may be null)
* @param ex the exception thrown by the method execution (may be null)
* @return the empty array if there are no arguments
*/
protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
calculateArgumentBindings();
// AMC start
Object[] adviceInvocationArgs = new Object[this.adviceInvocationArgumentCount];
int numBound = 0;
if (this.joinPointArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
numBound++;
}
else if (this.joinPointStaticPartArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
numBound++;
}
if (!CollectionUtils.isEmpty(this.argumentBindings)) {
// binding from pointcut match
if (jpMatch != null) {
PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
for (PointcutParameter parameter : parameterBindings) {
String name = parameter.getName();
Integer index = this.argumentBindings.get(name);
adviceInvocationArgs[index] = parameter.getBinding();
numBound++;
}
}
// binding from returning clause
if (this.returningName != null) {
Integer index = this.argumentBindings.get(this.returningName);
adviceInvocationArgs[index] = returnValue;
numBound++;
}
// binding from thrown exception
if (this.throwingName != null) {
Integer index = this.argumentBindings.get(this.throwingName);
adviceInvocationArgs[index] = ex;
numBound++;
}
}
if (numBound != this.adviceInvocationArgumentCount) {
throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount
+ " arguments, but only bound " + numBound + " (JoinPointMatch " +
(jpMatch == null ? "was NOT" : "WAS") +
" bound in invocation)");
}
return adviceInvocationArgs;
}
/**
* Invoke the advice method.
* @param jpMatch the JoinPointMatch that matched this execution join point
* @param returnValue the return value from the method execution (may be null)
* @param ex the exception thrown by the method execution (may be null)
* @return the invocation result
* @throws Throwable in case of invocation failure
*/
protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
// As above, but in this case we are given the join point.
protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
/**
* Overridden in around advice to return proceeding join point.
*/
protected JoinPoint getJoinPoint() {
return currentJoinPoint();
}
/**
* Get the current join point match at the join point we are being dispatched on.
*/
protected JoinPointMatch getJoinPointMatch() {
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
return getJoinPointMatch((ProxyMethodInvocation) mi);
}
// Note: We can't use JoinPointMatch.getClass().getName() as the key, since
// Spring AOP does all the matching at a join point, and then all the invocations.
// Under this scenario, if we just use JoinPointMatch as the key, then
// 'last man wins' which is not what we want at all.
// Using the expression is guaranteed to be safe, since 2 identical expressions
// are guaranteed to bind in exactly the same way.
protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
return (JoinPointMatch) pmi.getUserAttribute(this.pointcut.getExpression());
}
@Override
public String toString() {
return getClass().getName() + ": advice method [" + this.aspectJAdviceMethod + "]; " +
"aspect name '" + this.aspectName + "'";
}
/**
* MethodMatcher that excludes the specified advice method.
* @see AbstractAspectJAdvice#buildSafePointcut()
*/
private static class AdviceExcludingMethodMatcher extends StaticMethodMatcher {
private final Method adviceMethod;
public AdviceExcludingMethodMatcher(Method adviceMethod) {
this.adviceMethod = adviceMethod;
}
@Override
public boolean matches(Method method, Class targetClass) {
return !this.adviceMethod.equals(method);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AdviceExcludingMethodMatcher)) {
return false;
}
AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other;
return this.adviceMethod.equals(otherMm.adviceMethod);
}
@Override
public int hashCode() {
return this.adviceMethod.hashCode();
}
}
}
@@ -1,47 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.springframework.core.Ordered;
/**
* Interface implemented to provide an instance of an AspectJ aspect.
* Decouples from Spring's bean factory.
*
* <p>Extends the {@link org.springframework.core.Ordered} interface
* to express an order value for the underlying aspect in a chain.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.beans.factory.BeanFactory#getBean
*/
public interface AspectInstanceFactory extends Ordered {
/**
* Create an instance of this factory's aspect.
* @return the aspect instance (never {@code null})
*/
Object getAspectInstance();
/**
* Expose the aspect class loader that this factory uses.
* @return the aspect class loader (never {@code null})
*/
ClassLoader getAspectClassLoader();
}
@@ -1,807 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.StringUtils;
/**
* {@link ParameterNameDiscoverer} implementation that tries to deduce parameter names
* for an advice method from the pointcut expression, returning, and throwing clauses.
* If an unambiguous interpretation is not available, it returns {@code null}.
*
* <p>This class interprets arguments in the following way:
* <ol>
* <li>If the first parameter of the method is of type {@link JoinPoint}
* or {@link ProceedingJoinPoint}, it is assumed to be for passing
* {@code thisJoinPoint} to the advice, and the parameter name will
* be assigned the value {@code "thisJoinPoint"}.</li>
* <li>If the first parameter of the method is of type
* {@code JoinPoint.StaticPart}, it is assumed to be for passing
* {@code "thisJoinPointStaticPart"} to the advice, and the parameter name
* will be assigned the value {@code "thisJoinPointStaticPart"}.</li>
* <li>If a {@link #setThrowingName(String) throwingName} has been set, and
* there are no unbound arguments of type {@code Throwable+}, then an
* {@link IllegalArgumentException} is raised. If there is more than one
* unbound argument of type {@code Throwable+}, then an
* {@link AmbiguousBindingException} is raised. If there is exactly one
* unbound argument of type {@code Throwable+}, then the corresponding
* parameter name is assigned the value &lt;throwingName&gt;.</li>
* <li>If there remain unbound arguments, then the pointcut expression is
* examined. Let {@code a} be the number of annotation-based pointcut
* expressions (&#64;annotation, &#64;this, &#64;target, &#64;args,
* &#64;within, &#64;withincode) that are used in binding form. Usage in
* binding form has itself to be deduced: if the expression inside the
* pointcut is a single string literal that meets Java variable name
* conventions it is assumed to be a variable name. If {@code a} is
* zero we proceed to the next stage. If {@code a} &gt; 1 then an
* {@code AmbiguousBindingException} is raised. If {@code a} == 1,
* and there are no unbound arguments of type {@code Annotation+},
* then an {@code IllegalArgumentException} is raised. if there is
* exactly one such argument, then the corresponding parameter name is
* assigned the value from the pointcut expression.</li>
* <li>If a returningName has been set, and there are no unbound arguments
* then an {@code IllegalArgumentException} is raised. If there is
* more than one unbound argument then an
* {@code AmbiguousBindingException} is raised. If there is exactly
* one unbound argument then the corresponding parameter name is assigned
* the value &lt;returningName&gt;.</li>
* <li>If there remain unbound arguments, then the pointcut expression is
* examined once more for {@code this}, {@code target}, and
* {@code args} pointcut expressions used in the binding form (binding
* forms are deduced as described for the annotation based pointcuts). If
* there remains more than one unbound argument of a primitive type (which
* can only be bound in {@code args}) then an
* {@code AmbiguousBindingException} is raised. If there is exactly
* one argument of a primitive type, then if exactly one {@code args}
* bound variable was found, we assign the corresponding parameter name
* the variable name. If there were no {@code args} bound variables
* found an {@code IllegalStateException} is raised. If there are
* multiple {@code args} bound variables, an
* {@code AmbiguousBindingException} is raised. At this point, if
* there remains more than one unbound argument we raise an
* {@code AmbiguousBindingException}. If there are no unbound arguments
* remaining, we are done. If there is exactly one unbound argument
* remaining, and only one candidate variable name unbound from
* {@code this}, {@code target}, or {@code args}, it is
* assigned as the corresponding parameter name. If there are multiple
* possibilities, an {@code AmbiguousBindingException} is raised.</li>
* </ol>
*
* <p>The behavior on raising an {@code IllegalArgumentException} or
* {@code AmbiguousBindingException} is configurable to allow this discoverer
* to be used as part of a chain-of-responsibility. By default the condition will
* be logged and the {@code getParameterNames(..)} method will simply return
* {@code null}. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property is set to {@code true}, the conditions will be thrown as
* {@code IllegalArgumentException} and {@code AmbiguousBindingException},
* respectively.
*
* <p>Was that perfectly clear? ;)
*
* <p>Short version: If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* @author Adrian Colyer
* @since 2.0
*/
public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscoverer {
private static final String THIS_JOIN_POINT = "thisJoinPoint";
private static final String THIS_JOIN_POINT_STATIC_PART = "thisJoinPointStaticPart";
// Steps in the binding algorithm...
private static final int STEP_JOIN_POINT_BINDING = 1;
private static final int STEP_THROWING_BINDING = 2;
private static final int STEP_ANNOTATION_BINDING = 3;
private static final int STEP_RETURNING_BINDING = 4;
private static final int STEP_PRIMITIVE_ARGS_BINDING = 5;
private static final int STEP_THIS_TARGET_ARGS_BINDING = 6;
private static final int STEP_REFERENCE_PCUT_BINDING = 7;
private static final int STEP_FINISHED = 8;
private static final Set<String> singleValuedAnnotationPcds = new HashSet<String>();
private static final Set<String> nonReferencePointcutTokens = new HashSet<String>();
static {
singleValuedAnnotationPcds.add("@this");
singleValuedAnnotationPcds.add("@target");
singleValuedAnnotationPcds.add("@within");
singleValuedAnnotationPcds.add("@withincode");
singleValuedAnnotationPcds.add("@annotation");
Set pointcutPrimitives = PointcutParser.getAllSupportedPointcutPrimitives();
for (Iterator iterator = pointcutPrimitives.iterator(); iterator.hasNext();) {
PointcutPrimitive primitive = (PointcutPrimitive) iterator.next();
nonReferencePointcutTokens.add(primitive.getName());
}
nonReferencePointcutTokens.add("&&");
nonReferencePointcutTokens.add("!");
nonReferencePointcutTokens.add("||");
nonReferencePointcutTokens.add("and");
nonReferencePointcutTokens.add("or");
nonReferencePointcutTokens.add("not");
}
private boolean raiseExceptions;
/**
* If the advice is afterReturning, and binds the return value, this is the parameter name used.
*/
private String returningName;
/**
* If the advice is afterThrowing, and binds the thrown value, this is the parameter name used.
*/
private String throwingName;
/**
* The pointcut expression associated with the advice, as a simple String.
*/
private String pointcutExpression;
private Class[] argumentTypes;
private String[] parameterNameBindings;
private int numberOfRemainingUnboundArguments;
/**
* Create a new discoverer that attempts to discover parameter names
* from the given pointcut expression.
*/
public AspectJAdviceParameterNameDiscoverer(String pointcutExpression) {
this.pointcutExpression = pointcutExpression;
}
/**
* Indicate whether {@link IllegalArgumentException} and {@link AmbiguousBindingException}
* must be thrown as appropriate in the case of failing to deduce advice parameter names.
* @param raiseExceptions {@code true} if exceptions are to be thrown
*/
public void setRaiseExceptions(boolean raiseExceptions) {
this.raiseExceptions = raiseExceptions;
}
/**
* If {@code afterReturning} advice binds the return value, the
* returning variable name must be specified.
* @param returningName the name of the returning variable
*/
public void setReturningName(String returningName) {
this.returningName = returningName;
}
/**
* If {@code afterThrowing} advice binds the thrown value, the
* throwing variable name must be specified.
* @param throwingName the name of the throwing variable
*/
public void setThrowingName(String throwingName) {
this.throwingName = throwingName;
}
/**
* Deduce the parameter names for an advice method.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
* for this class for details of the algorithm used.
* @param method the target {@link Method}
* @return the parameter names
*/
@Override
public String[] getParameterNames(Method method) {
this.argumentTypes = method.getParameterTypes();
this.numberOfRemainingUnboundArguments = this.argumentTypes.length;
this.parameterNameBindings = new String[this.numberOfRemainingUnboundArguments];
int minimumNumberUnboundArgs = 0;
if (this.returningName != null) {
minimumNumberUnboundArgs++;
}
if (this.throwingName != null) {
minimumNumberUnboundArgs++;
}
if (this.numberOfRemainingUnboundArguments < minimumNumberUnboundArgs) {
throw new IllegalStateException(
"Not enough arguments in method to satisfy binding of returning and throwing variables");
}
try {
int algorithmicStep = STEP_JOIN_POINT_BINDING;
while ((this.numberOfRemainingUnboundArguments > 0) && algorithmicStep < STEP_FINISHED) {
switch (algorithmicStep++) {
case STEP_JOIN_POINT_BINDING:
if (!maybeBindThisJoinPoint()) {
maybeBindThisJoinPointStaticPart();
}
break;
case STEP_THROWING_BINDING:
maybeBindThrowingVariable();
break;
case STEP_ANNOTATION_BINDING:
maybeBindAnnotationsFromPointcutExpression();
break;
case STEP_RETURNING_BINDING:
maybeBindReturningVariable();
break;
case STEP_PRIMITIVE_ARGS_BINDING:
maybeBindPrimitiveArgsFromPointcutExpression();
break;
case STEP_THIS_TARGET_ARGS_BINDING:
maybeBindThisOrTargetOrArgsFromPointcutExpression();
break;
case STEP_REFERENCE_PCUT_BINDING:
maybeBindReferencePointcutParameter();
break;
default:
throw new IllegalStateException("Unknown algorithmic step: " + (algorithmicStep - 1));
}
}
}
catch (AmbiguousBindingException ambigEx) {
if (this.raiseExceptions) {
throw ambigEx;
}
else {
return null;
}
}
catch (IllegalArgumentException ex) {
if (this.raiseExceptions) {
throw ex;
}
else {
return null;
}
}
if (this.numberOfRemainingUnboundArguments == 0) {
return this.parameterNameBindings;
}
else {
if (this.raiseExceptions) {
throw new IllegalStateException("Failed to bind all argument names: " +
this.numberOfRemainingUnboundArguments + " argument(s) could not be bound");
}
else {
// convention for failing is to return null, allowing participation in a chain of responsibility
return null;
}
}
}
/**
* An advice method can never be a constructor in Spring.
* @return {@code null}
* @throws UnsupportedOperationException if
* {@link #setRaiseExceptions(boolean) raiseExceptions} has been set to {@code true}
*/
@Override
public String[] getParameterNames(Constructor ctor) {
if (this.raiseExceptions) {
throw new UnsupportedOperationException("An advice method can never be a constructor");
}
else {
// we return null rather than throw an exception so that we behave well
// in a chain-of-responsibility.
return null;
}
}
private void bindParameterName(int index, String name) {
this.parameterNameBindings[index] = name;
this.numberOfRemainingUnboundArguments--;
}
/**
* If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
* parameter name and return true, else return false.
*/
private boolean maybeBindThisJoinPoint() {
if ((this.argumentTypes[0] == JoinPoint.class) || (this.argumentTypes[0] == ProceedingJoinPoint.class)) {
bindParameterName(0, THIS_JOIN_POINT);
return true;
}
else {
return false;
}
}
private void maybeBindThisJoinPointStaticPart() {
if (this.argumentTypes[0] == JoinPoint.StaticPart.class) {
bindParameterName(0, THIS_JOIN_POINT_STATIC_PART);
}
}
/**
* If a throwing name was specified and there is exactly one choice remaining
* (argument that is a subtype of Throwable) then bind it.
*/
private void maybeBindThrowingVariable() {
if (this.throwingName == null) {
return;
}
// So there is binding work to do...
int throwableIndex = -1;
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && isSubtypeOf(Throwable.class, i)) {
if (throwableIndex == -1) {
throwableIndex = i;
}
else {
// Second candidate we've found - ambiguous binding
throw new AmbiguousBindingException("Binding of throwing parameter '" +
this.throwingName + "' is ambiguous: could be bound to argument " +
throwableIndex + " or argument " + i);
}
}
}
if (throwableIndex == -1) {
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName
+ "' could not be completed as no available arguments are a subtype of Throwable");
}
else {
bindParameterName(throwableIndex, this.throwingName);
}
}
/**
* If a returning variable was specified and there is only one choice remaining, bind it.
*/
private void maybeBindReturningVariable() {
if (this.numberOfRemainingUnboundArguments == 0) {
throw new IllegalStateException(
"Algorithm assumes that there must be at least one unbound parameter on entry to this method");
}
if (this.returningName != null) {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Binding of returning parameter '" + this.returningName +
"' is ambiguous, there are " + this.numberOfRemainingUnboundArguments + " candidates.");
}
// We're all set... find the unbound parameter, and bind it.
for (int i = 0; i < this.parameterNameBindings.length; i++) {
if (this.parameterNameBindings[i] == null) {
bindParameterName(i, this.returningName);
break;
}
}
}
}
/**
* Parse the string pointcut expression looking for:
* &#64;this, &#64;target, &#64;args, &#64;within, &#64;withincode, &#64;annotation.
* If we find one of these pointcut expressions, try and extract a candidate variable
* name (or variable names, in the case of args).
* <p>Some more support from AspectJ in doing this exercise would be nice... :)
*/
private void maybeBindAnnotationsFromPointcutExpression() {
List<String> varNames = new ArrayList<String>();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
for (int i = 0; i < tokens.length; i++) {
String toMatch = tokens[i];
int firstParenIndex = toMatch.indexOf("(");
if (firstParenIndex != -1) {
toMatch = toMatch.substring(0, firstParenIndex);
}
if (singleValuedAnnotationPcds.contains(toMatch)) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
String varName = maybeExtractVariableName(body.text);
if (varName != null) {
varNames.add(varName);
}
}
else if (tokens[i].startsWith("@args(") || tokens[i].equals("@args")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
maybeExtractVariableNamesFromArgs(body.text, varNames);
}
}
bindAnnotationsFromVarNames(varNames);
}
/**
* Match the given list of extracted variable names to argument slots.
*/
private void bindAnnotationsFromVarNames(List<String> varNames) {
if (!varNames.isEmpty()) {
// we have work to do...
int numAnnotationSlots = countNumberOfUnboundAnnotationArguments();
if (numAnnotationSlots > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" potential annotation variable(s), and " +
numAnnotationSlots + " potential argument slots");
}
else if (numAnnotationSlots == 1) {
if (varNames.size() == 1) {
// it's a match
findAndBind(Annotation.class, varNames.get(0));
}
else {
// multiple candidate vars, but only one slot
throw new IllegalArgumentException("Found " + varNames.size() +
" candidate annotation binding variables" +
" but only one potential argument binding slot");
}
}
else {
// no slots so presume those candidate vars were actually type names
}
}
}
/*
* If the token starts meets Java identifier conventions, it's in.
*/
private String maybeExtractVariableName(String candidateToken) {
if (candidateToken == null || candidateToken.equals("")) {
return null;
}
if (Character.isJavaIdentifierStart(candidateToken.charAt(0)) &&
Character.isLowerCase(candidateToken.charAt(0))) {
char[] tokenChars = candidateToken.toCharArray();
for (char tokenChar : tokenChars) {
if (!Character.isJavaIdentifierPart(tokenChar)) {
return null;
}
}
return candidateToken;
}
else {
return null;
}
}
/**
* Given an args pointcut body (could be {@code args} or {@code at_args}),
* add any candidate variable names to the given list.
*/
private void maybeExtractVariableNamesFromArgs(String argsSpec, List<String> varNames) {
if (argsSpec == null) {
return;
}
String[] tokens = StringUtils.tokenizeToStringArray(argsSpec, ",");
for (int i = 0; i < tokens.length; i++) {
tokens[i] = StringUtils.trimWhitespace(tokens[i]);
String varName = maybeExtractVariableName(tokens[i]);
if (varName != null) {
varNames.add(varName);
}
}
}
/**
* Parse the string pointcut expression looking for this(), target() and args() expressions.
* If we find one, try and extract a candidate variable name and bind it.
*/
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at this(),target(),args() binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<String>();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("this") ||
tokens[i].startsWith("this(") ||
tokens[i].equals("target") ||
tokens[i].startsWith("target(")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
String varName = maybeExtractVariableName(body.text);
if (varName != null) {
varNames.add(varName);
}
}
else if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
List<String> candidateVarNames = new ArrayList<String>();
maybeExtractVariableNamesFromArgs(body.text, candidateVarNames);
// we may have found some var names that were bound in previous primitive args binding step,
// filter them out...
for (String varName : candidateVarNames) {
if (!alreadyBound(varName)) {
varNames.add(varName);
}
}
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate this(), target() or args() variables but only one unbound argument slot");
}
else if (varNames.size() == 1) {
for (int j = 0; j < this.parameterNameBindings.length; j++) {
if (isUnbound(j)) {
bindParameterName(j, varNames.get(0));
break;
}
}
}
// else varNames.size must be 0 and we have nothing to bind.
}
private void maybeBindReferencePointcutParameter() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at reference pointcut binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<String>();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
for (int i = 0; i < tokens.length; i++) {
String toMatch = tokens[i];
if (toMatch.startsWith("!")) {
toMatch = toMatch.substring(1);
}
int firstParenIndex = toMatch.indexOf("(");
if (firstParenIndex != -1) {
toMatch = toMatch.substring(0, firstParenIndex);
}
else {
if (tokens.length < i + 2) {
// no "(" and nothing following
continue;
}
else {
String nextToken = tokens[i + 1];
if (nextToken.charAt(0) != '(') {
// next token is not "(" either, can't be a pc...
continue;
}
}
}
// eat the body
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
if (!nonReferencePointcutTokens.contains(toMatch)) {
// then it could be a reference pointcut
String varName = maybeExtractVariableName(body.text);
if (varName != null) {
varNames.add(varName);
}
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate reference pointcut variables but only one unbound argument slot");
}
else if (varNames.size() == 1) {
for (int j = 0; j < this.parameterNameBindings.length; j++) {
if (isUnbound(j)) {
bindParameterName(j, varNames.get(0));
break;
}
}
}
// else varNames.size must be 0 and we have nothing to bind.
}
/*
* We've found the start of a binding pointcut at the given index into the
* token array. Now we need to extract the pointcut body and return it.
*/
private PointcutBody getPointcutBody(String[] tokens, int startIndex) {
int numTokensConsumed = 0;
String currentToken = tokens[startIndex];
int bodyStart = currentToken.indexOf('(');
if (currentToken.charAt(currentToken.length() - 1) == ')') {
// It's an all in one... get the text between the first (and the last)
return new PointcutBody(0, currentToken.substring(bodyStart + 1, currentToken.length() - 1));
}
else {
StringBuilder sb = new StringBuilder();
if (bodyStart >= 0 && bodyStart != (currentToken.length() - 1)) {
sb.append(currentToken.substring(bodyStart + 1));
sb.append(" ");
}
numTokensConsumed++;
int currentIndex = startIndex + numTokensConsumed;
while (currentIndex < tokens.length) {
if (tokens[currentIndex].equals("(")) {
currentIndex++;
continue;
}
if (tokens[currentIndex].endsWith(")")) {
sb.append(tokens[currentIndex].substring(0, tokens[currentIndex].length() - 1));
return new PointcutBody(numTokensConsumed, sb.toString().trim());
}
String toAppend = tokens[currentIndex];
if (toAppend.startsWith("(")) {
toAppend = toAppend.substring(1);
}
sb.append(toAppend);
sb.append(" ");
currentIndex++;
numTokensConsumed++;
}
}
// We looked and failed...
return new PointcutBody(numTokensConsumed, null);
}
/**
* Match up args against unbound arguments of primitive types
*/
private void maybeBindPrimitiveArgsFromPointcutExpression() {
int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments();
if (numUnboundPrimitives > 1) {
throw new AmbiguousBindingException("Found '" + numUnboundPrimitives +
"' unbound primitive arguments with no way to distinguish between them.");
}
if (numUnboundPrimitives == 1) {
// Look for arg variable and bind it if we find exactly one...
List<String> varNames = new ArrayList<String>();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression, " ");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("args") || tokens[i].startsWith("args(")) {
PointcutBody body = getPointcutBody(tokens, i);
i += body.numTokensConsumed;
maybeExtractVariableNamesFromArgs(body.text, varNames);
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate variable names but only one candidate binding slot when matching primitive args");
}
else if (varNames.size() == 1) {
// 1 primitive arg, and one candidate...
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
bindParameterName(i, varNames.get(0));
break;
}
}
}
}
}
/*
* Return true if the parameter name binding for the given parameter
* index has not yet been assigned.
*/
private boolean isUnbound(int i) {
return this.parameterNameBindings[i] == null;
}
private boolean alreadyBound(String varName) {
for (int i = 0; i < this.parameterNameBindings.length; i++) {
if (!isUnbound(i) && varName.equals(this.parameterNameBindings[i])) {
return true;
}
}
return false;
}
/*
* Return {@code true} if the given argument type is a subclass
* of the given supertype.
*/
private boolean isSubtypeOf(Class supertype, int argumentNumber) {
return supertype.isAssignableFrom(this.argumentTypes[argumentNumber]);
}
private int countNumberOfUnboundAnnotationArguments() {
int count = 0;
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && isSubtypeOf(Annotation.class, i)) {
count++;
}
}
return count;
}
private int countNumberOfUnboundPrimitiveArguments() {
int count = 0;
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
count++;
}
}
return count;
}
/*
* Find the argument index with the given type, and bind the given
* {@code varName} in that position.
*/
private void findAndBind(Class argumentType, String varName) {
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && isSubtypeOf(argumentType, i)) {
bindParameterName(i, varName);
return;
}
}
throw new IllegalStateException("Expected to find an unbound argument of type '" +
argumentType.getName() + "'");
}
/**
* Simple struct to hold the extracted text from a pointcut body, together
* with the number of tokens consumed in extracting it.
*/
private static class PointcutBody {
private int numTokensConsumed;
private String text;
public PointcutBody(int tokens, String text) {
this.numTokensConsumed = tokens;
this.text = text;
}
}
/**
* Thrown in response to an ambiguous binding being detected when
* trying to resolve a method's parameter names.
*/
@SuppressWarnings("serial")
public static class AmbiguousBindingException extends RuntimeException {
/**
* Construct a new AmbiguousBindingException with the specified message.
* @param msg the detail message
*/
public AmbiguousBindingException(String msg) {
super(msg);
}
}
}
@@ -1,60 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
/**
* Spring AOP advice wrapping an AspectJ after advice method.
*
* @author Rod Johnson
* @since 2.0
*/
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.util.ClassUtils;
import org.springframework.util.TypeUtils;
/**
* Spring AOP advice wrapping an AspectJ after-returning advice method.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @since 2.0
*/
public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice {
public AspectJAfterReturningAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
@Override
public void setReturningName(String name) {
setReturningNameNoCheck(name);
}
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
if (shouldInvokeOnReturnValueOf(method, returnValue)) {
invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
}
}
/**
* Following AspectJ semantics, if a returning clause was specified, then the
* advice is only invoked if the returned value is an instance of the given
* returning type and generic type parameters, if any, match the assignment
* rules. If the returning type is Object, the advice is *always* invoked.
* @param returnValue the return value of the target method
* @return whether to invoke the advice method for the given return value
*/
private boolean shouldInvokeOnReturnValueOf(Method method, Object returnValue) {
Class type = getDiscoveredReturningType();
Type genericType = getDiscoveredReturningGenericType();
// If we aren't dealing with a raw type, check if generic parameters are assignable.
return (ClassUtils.isAssignableValue(type, returnValue) &&
(genericType == null || genericType == type ||
TypeUtils.isAssignable(genericType, method.getGenericReturnType())));
}
}
@@ -1,77 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
/**
* Spring AOP advice wrapping an AspectJ after-throwing advice method.
*
* @author Rod Johnson
* @since 2.0
*/
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {
public AspectJAfterThrowingAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
@Override
public void setThrowingName(String name) {
setThrowingNameNoCheck(name);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable t) {
if (shouldInvokeOnThrowing(t)) {
invokeAdviceMethod(getJoinPointMatch(), null, t);
}
throw t;
}
}
/**
* In AspectJ semantics, after throwing advice that specifies a throwing clause
* is only invoked if the thrown exception is a subtype of the given throwing type.
*/
private boolean shouldInvokeOnThrowing(Throwable t) {
Class throwingType = getDiscoveredThrowingType();
return throwingType.isAssignableFrom(t.getClass());
}
}
@@ -1,72 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.BeforeAdvice;
/**
* Utility methods for dealing with AspectJ advisors.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class AspectJAopUtils {
/**
* Return {@code true} if the advisor is a form of before advice.
*/
public static boolean isBeforeAdvice(Advisor anAdvisor) {
AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);
if (precedenceInfo != null) {
return precedenceInfo.isBeforeAdvice();
}
return (anAdvisor.getAdvice() instanceof BeforeAdvice);
}
/**
* Return {@code true} if the advisor is a form of after advice.
*/
public static boolean isAfterAdvice(Advisor anAdvisor) {
AspectJPrecedenceInformation precedenceInfo = getAspectJPrecedenceInformationFor(anAdvisor);
if (precedenceInfo != null) {
return precedenceInfo.isAfterAdvice();
}
return (anAdvisor.getAdvice() instanceof AfterAdvice);
}
/**
* Return the AspectJPrecedenceInformation provided by this advisor or its advice.
* If neither the advisor nor the advice have precedence information, this method
* will return {@code null}.
*/
public static AspectJPrecedenceInformation getAspectJPrecedenceInformationFor(Advisor anAdvisor) {
if (anAdvisor instanceof AspectJPrecedenceInformation) {
return (AspectJPrecedenceInformation) anAdvisor;
}
Advice advice = anAdvisor.getAdvice();
if (advice instanceof AspectJPrecedenceInformation) {
return (AspectJPrecedenceInformation) advice;
}
return null;
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.springframework.aop.ProxyMethodInvocation;
/**
* Spring AOP around advice (MethodInterceptor) that wraps
* an AspectJ advice method. Exposes ProceedingJoinPoint.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor {
public AspectJAroundAdvice(
Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJAroundAdviceMethod, pointcut, aif);
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return false;
}
@Override
protected boolean supportsProceedingJoinPoint() {
return true;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
}
/**
* Return the ProceedingJoinPoint for the current invocation,
* instantiating it lazily if it hasn't been bound to the thread already.
* @param rmi the current Spring AOP ReflectiveMethodInvocation,
* which we'll use for attribute binding
* @return the ProceedingJoinPoint to make available to advice methods
*/
protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
return new MethodInvocationProceedingJoinPoint(rmi);
}
}
@@ -1,662 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.patterns.NamePattern;
import org.aspectj.weaver.reflect.ReflectionWorld;
import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException;
import org.aspectj.weaver.reflect.ShadowMatchImpl;
import org.aspectj.weaver.tools.ContextBasedMatcher;
import org.aspectj.weaver.tools.FuzzyBoolean;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.aspectj.weaver.tools.MatchingContext;
import org.aspectj.weaver.tools.PointcutDesignatorHandler;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.AbstractExpressionPointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Spring {@link org.springframework.aop.Pointcut} implementation
* that uses the AspectJ weaver to evaluate a pointcut expression.
*
* <p>The pointcut expression value is an AspectJ expression. This can
* reference other pointcuts and use composition and other operations.
*
* <p>Naturally, as this is to be processed by Spring AOP's proxy-based model,
* only method execution pointcuts are supported.
*
* @author Rob Harrop
* @author Adrian Colyer
* @author Rod Johnson
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @author Dave Syer
* @since 2.0
*/
@SuppressWarnings("serial")
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();
static {
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
}
private static final Log logger = LogFactory.getLog(AspectJExpressionPointcut.class);
private Class pointcutDeclarationScope;
private String[] pointcutParameterNames = new String[0];
private Class[] pointcutParameterTypes = new Class[0];
private BeanFactory beanFactory;
private transient PointcutExpression pointcutExpression;
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
/**
* Create a new default AspectJExpressionPointcut.
*/
public AspectJExpressionPointcut() {
}
/**
* Create a new AspectJExpressionPointcut with the given settings.
* @param declarationScope the declaration scope for the pointcut
* @param paramNames the parameter names for the pointcut
* @param paramTypes the parameter types for the pointcut
*/
public AspectJExpressionPointcut(Class declarationScope, String[] paramNames, Class[] paramTypes) {
this.pointcutDeclarationScope = declarationScope;
if (paramNames.length != paramTypes.length) {
throw new IllegalStateException(
"Number of pointcut parameter names must match number of pointcut parameter types");
}
this.pointcutParameterNames = paramNames;
this.pointcutParameterTypes = paramTypes;
}
/**
* Set the declaration scope for the pointcut.
*/
public void setPointcutDeclarationScope(Class pointcutDeclarationScope) {
this.pointcutDeclarationScope = pointcutDeclarationScope;
}
/**
* Set the parameter names for the pointcut.
*/
public void setParameterNames(String[] names) {
this.pointcutParameterNames = names;
}
/**
* Set the parameter types for the pointcut.
*/
public void setParameterTypes(Class[] types) {
this.pointcutParameterTypes = types;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public ClassFilter getClassFilter() {
checkReadyToMatch();
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
checkReadyToMatch();
return this;
}
/**
* Check whether this pointcut is ready to match,
* lazily building the underlying AspectJ pointcut expression.
*/
private void checkReadyToMatch() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutExpression = buildPointcutExpression();
}
}
/**
* Build the underlying AspectJ pointcut expression.
*/
private PointcutExpression buildPointcutExpression() {
ClassLoader cl = (this.beanFactory instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) this.beanFactory)
.getBeanClassLoader() : Thread.currentThread()
.getContextClassLoader());
return buildPointcutExpression(cl);
}
/**
* Build the underlying AspectJ pointcut expression.
*/
private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
PointcutParser parser = initializePointcutParser(classLoader);
PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
for (int i = 0; i < pointcutParameters.length; i++) {
pointcutParameters[i] = parser.createPointcutParameter(
this.pointcutParameterNames[i],
this.pointcutParameterTypes[i]);
}
return parser.parsePointcutExpression(
replaceBooleanOperators(getExpression()),
this.pointcutDeclarationScope, pointcutParameters);
}
/**
* Initialize the underlying AspectJ pointcut parser.
*/
private PointcutParser initializePointcutParser(ClassLoader cl) {
PointcutParser parser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
SUPPORTED_PRIMITIVES, cl);
parser.registerPointcutDesignatorHandler(new BeanNamePointcutDesignatorHandler());
return parser;
}
/**
* If a pointcut expression has been specified in XML, the user cannot
* write {@code and} as "&&" (though &amp;&amp; will work).
* We also allow {@code and} between two pointcut sub-expressions.
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
*/
private String replaceBooleanOperators(String pcExpr) {
String result = StringUtils.replace(pcExpr, " and ", " && ");
result = StringUtils.replace(result, " or ", " || ");
result = StringUtils.replace(result, " not ", " ! ");
return result;
}
/**
* Return the underlying AspectJ pointcut expression.
*/
public PointcutExpression getPointcutExpression() {
checkReadyToMatch();
return this.pointcutExpression;
}
@Override
public boolean matches(Class targetClass) {
checkReadyToMatch();
try {
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
} catch (ReflectionWorldException e) {
logger.debug("PointcutExpression matching rejected target class", e);
try {
// Actually this is still a "maybe" - treat the pointcut as dynamic if we
// don't know enough yet
return getFallbackPointcutExpression(targetClass).couldMatchJoinPointsInType(targetClass);
} catch (BCException ex) {
logger.debug(
"Fallback PointcutExpression matching rejected target class",
ex);
return false;
}
}
catch (BCException ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
return false;
}
}
@Override
public boolean matches(Method method, Class targetClass, boolean beanHasIntroductions) {
checkReadyToMatch();
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
// Special handling for this, target, @this, @target, @annotation
// in Spring - we can optimize since we know we have exactly this class,
// and there will never be matching subclass at runtime.
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
return (beanHasIntroductions || matchesIgnoringSubtypes(shadowMatch) || matchesTarget(shadowMatch, targetClass));
}
}
@Override
public boolean matches(Method method, Class targetClass) {
return matches(method, targetClass, false);
}
@Override
public boolean isRuntime() {
checkReadyToMatch();
return this.pointcutExpression.mayNeedDynamicTest();
}
@Override
public boolean matches(Method method, Class targetClass, Object[] args) {
checkReadyToMatch();
ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
ShadowMatch originalShadowMatch = getShadowMatch(method, method);
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
// consistent with return of MethodInvocationProceedingJoinPoint
ProxyMethodInvocation pmi = null;
Object targetObject = null;
Object thisObject = null;
try {
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
targetObject = mi.getThis();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
pmi = (ProxyMethodInvocation) mi;
thisObject = pmi.getProxy();
}
catch (IllegalStateException ex) {
// No current invocation...
// TODO: Should we really proceed here?
logger.debug("Couldn't access current invocation - matching with limited context: " + ex);
}
JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
/*
* Do a final check to see if any this(TYPE) kind of residue match. For
* this purpose, we use the original method's (proxy method's) shadow to
* ensure that 'this' is correctly checked against. Without this check,
* we get incorrect match on this(TYPE) where TYPE matches the target
* type but not 'this' (as would be the case of JDK dynamic proxies).
* <p>See SPR-2979 for the original bug.
*/
if (pmi != null) { // there is a current invocation
RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
return false;
}
}
if (joinPointMatch.matches() && pmi != null) {
bindParameters(pmi, joinPointMatch);
}
return joinPointMatch.matches();
}
protected String getCurrentProxiedBeanName() {
return ProxyCreationContext.getCurrentProxiedBeanName();
}
/**
* Get a new pointcut expression based on a target class's loader, rather
* than the default.
*/
private PointcutExpression getFallbackPointcutExpression(
Class<?> targetClass) {
ClassLoader classLoader = targetClass.getClassLoader();
return classLoader == null ? this.pointcutExpression : buildPointcutExpression(classLoader);
}
/**
* A match test returned maybe - if there are any subtype sensitive variables
* involved in the test (this, target, at_this, at_target, at_annotation) then
* we say this is not a match as in Spring there will never be a different
* runtime subtype.
*/
private boolean matchesIgnoringSubtypes(ShadowMatch shadowMatch) {
return !(getRuntimeTestWalker(shadowMatch).testsSubtypeSensitiveVars());
}
private boolean matchesTarget(ShadowMatch shadowMatch, Class targetClass) {
return getRuntimeTestWalker(shadowMatch).testTargetInstanceOfResidue(targetClass);
}
private RuntimeTestWalker getRuntimeTestWalker(ShadowMatch shadowMatch) {
if (shadowMatch instanceof DefensiveShadowMatch) {
return new RuntimeTestWalker(((DefensiveShadowMatch)shadowMatch).primary);
}
return new RuntimeTestWalker(shadowMatch);
}
private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) {
// Note: Can't use JoinPointMatch.getClass().getName() as the key, since
// Spring AOP does all the matching at a join point, and then all the invocations
// under this scenario, if we just use JoinPointMatch as the key, then
// 'last man wins' which is not what we want at all.
// Using the expression is guaranteed to be safe, since 2 identical expressions
// are guaranteed to bind in exactly the same way.
invocation.setUserAttribute(getExpression(), jpm);
}
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
// Avoid lock contention for known Methods through concurrent access...
ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
if (shadowMatch == null) {
synchronized (this.shadowMatchCache) {
// Not found - now check again with full lock...
Method methodToMatch = targetMethod;
PointcutExpression fallbackPointcutExpression = null;
shadowMatch = this.shadowMatchCache.get(methodToMatch);
if (shadowMatch == null) {
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod);
}
catch (ReflectionWorld.ReflectionWorldException ex) {
// Failed to introspect target method, probably because it has been loaded
// in a special ClassLoader. Let's try the original method instead...
try {
fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
} catch (ReflectionWorld.ReflectionWorldException e) {
if (targetMethod == originalMethod) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
else {
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod);
}
catch (ReflectionWorld.ReflectionWorldException ex2) {
// Could neither introspect the target class nor the proxy class ->
// let's simply consider this method as non-matching.
methodToMatch = originalMethod;
fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
try {
shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
} catch (ReflectionWorld.ReflectionWorldException e2) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
}
}
}
}
if (shadowMatch.maybeMatches() && fallbackPointcutExpression!=null) {
shadowMatch = new DefensiveShadowMatch(shadowMatch,
fallbackPointcutExpression.matchesMethodExecution(methodToMatch));
}
this.shadowMatchCache.put(targetMethod, shadowMatch);
}
}
}
return shadowMatch;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AspectJExpressionPointcut)) {
return false;
}
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
}
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AspectJExpressionPointcut: ");
if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) {
sb.append("(");
for (int i = 0; i < this.pointcutParameterTypes.length; i++) {
sb.append(this.pointcutParameterTypes[i].getName());
sb.append(" ");
sb.append(this.pointcutParameterNames[i]);
if ((i+1) < this.pointcutParameterTypes.length) {
sb.append(", ");
}
}
sb.append(")");
}
sb.append(" ");
if (getExpression() != null) {
sb.append(getExpression());
}
else {
sb.append("<pointcut expression not set>");
}
return sb.toString();
}
/**
* Handler for the Spring-specific {@code bean()} pointcut designator
* extension to AspectJ.
* <p>This handler must be added to each pointcut object that needs to
* handle the {@code bean()} PCD. Matching context is obtained
* automatically by examining a thread local variable and therefore a matching
* context need not be set on the pointcut.
*/
private class BeanNamePointcutDesignatorHandler implements PointcutDesignatorHandler {
private static final String BEAN_DESIGNATOR_NAME = "bean";
@Override
public String getDesignatorName() {
return BEAN_DESIGNATOR_NAME;
}
@Override
public ContextBasedMatcher parse(String expression) {
return new BeanNameContextMatcher(expression);
}
}
/**
* Matcher class for the BeanNamePointcutDesignatorHandler.
* <p>Dynamic match tests for this matcher always return true,
* since the matching decision is made at the proxy creation time.
* For static match tests, this matcher abstains to allow the overall
* pointcut to match even when negation is used with the bean() pointcut.
*/
private class BeanNameContextMatcher implements ContextBasedMatcher {
private final NamePattern expressionPattern;
public BeanNameContextMatcher(String expression) {
this.expressionPattern = new NamePattern(expression);
}
@Override
public boolean couldMatchJoinPointsInType(Class someClass) {
return (contextMatch(someClass) == FuzzyBoolean.YES);
}
@Override
public boolean couldMatchJoinPointsInType(Class someClass, MatchingContext context) {
return (contextMatch(someClass) == FuzzyBoolean.YES);
}
@Override
public boolean matchesDynamically(MatchingContext context) {
return true;
}
@Override
public FuzzyBoolean matchesStatically(MatchingContext context) {
return contextMatch(null);
}
@Override
public boolean mayNeedDynamicTest() {
return false;
}
private FuzzyBoolean contextMatch(Class targetType) {
String advisedBeanName = getCurrentProxiedBeanName();
if (advisedBeanName == null) { // no proxy creation in progress
// abstain; can't return YES, since that will make pointcut with negation fail
return FuzzyBoolean.MAYBE;
}
if (BeanFactoryUtils.isGeneratedBeanName(advisedBeanName)) {
return FuzzyBoolean.NO;
}
if (targetType != null) {
boolean isFactory = FactoryBean.class.isAssignableFrom(targetType);
return FuzzyBoolean.fromBoolean(
matchesBeanName(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
}
else {
return FuzzyBoolean.fromBoolean(matchesBeanName(advisedBeanName) ||
matchesBeanName(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
}
}
private boolean matchesBeanName(String advisedBeanName) {
if (this.expressionPattern.matches(advisedBeanName)) {
return true;
}
if (beanFactory != null) {
String[] aliases = beanFactory.getAliases(advisedBeanName);
for (String alias : aliases) {
if (this.expressionPattern.matches(alias)) {
return true;
}
}
}
return false;
}
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization, just initialize state after deserialization.
ois.defaultReadObject();
// Initialize transient fields.
// pointcutExpression will be initialized lazily by checkReadyToMatch()
this.shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
}
private static class DefensiveShadowMatch implements ShadowMatch {
private final ShadowMatch primary;
private final ShadowMatch other;
public DefensiveShadowMatch(ShadowMatch primary, ShadowMatch other) {
this.primary = primary;
this.other = other;
}
@Override
public boolean alwaysMatches() {
return primary.alwaysMatches();
}
@Override
public boolean maybeMatches() {
return primary.maybeMatches();
}
@Override
public boolean neverMatches() {
return primary.neverMatches();
}
@Override
public JoinPointMatch matchesJoinPoint(Object thisObject,
Object targetObject, Object[] args) {
try {
return primary.matchesJoinPoint(thisObject, targetObject, args);
} catch (ReflectionWorldException e) {
return other.matchesJoinPoint(thisObject, targetObject, args);
}
}
@Override
public void setMatchingContext(MatchingContext aMatchContext) {
primary.setMatchingContext(aMatchContext);
other.setMatchingContext(aMatchContext);
}
}
}
@@ -1,63 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractGenericPointcutAdvisor;
/**
* Spring AOP Advisor that can be used for any AspectJ pointcut expression.
*
* @author Rob Harrop
* @since 2.0
*/
@SuppressWarnings("serial")
public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdvisor {
private final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
public void setExpression(String expression) {
this.pointcut.setExpression(expression);
}
public void setLocation(String location) {
this.pointcut.setLocation(location);
}
public void setParameterTypes(Class[] types) {
this.pointcut.setParameterTypes(types);
}
public void setParameterNames(String[] names) {
this.pointcut.setParameterNames(names);
}
public String getLocation() {
return this.pointcut.getLocation();
}
public String getExpression() {
return this.pointcut.getExpression();
}
}
@@ -1,53 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
* Spring AOP advice that wraps an AspectJ before method.
*
* @author Rod Johnson
* @author Adrian Colyer
* @since 2.0
*/
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {
public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
@Override
public boolean isBeforeAdvice() {
return true;
}
@Override
public boolean isAfterAdvice() {
return false;
}
}
@@ -1,102 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* AspectJPointcutAdvisor that adapts an {@link AbstractAspectJAdvice}
* to the {@link org.springframework.aop.PointcutAdvisor} interface.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
*/
public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
private final AbstractAspectJAdvice advice;
private final Pointcut pointcut;
private Integer order;
/**
* Create a new AspectJPointcutAdvisor for the given advice
* @param advice the AbstractAspectJAdvice to wrap
*/
public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
this.pointcut = advice.buildSafePointcut();
}
public void setOrder(int order) {
this.order = order;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Advice getAdvice() {
return this.advice;
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public int getOrder() {
if (this.order != null) {
return this.order;
}
else {
return this.advice.getOrder();
}
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AspectJPointcutAdvisor)) {
return false;
}
AspectJPointcutAdvisor otherAdvisor = (AspectJPointcutAdvisor) other;
return (ObjectUtils.nullSafeEquals(this.advice, otherAdvisor.advice));
}
@Override
public int hashCode() {
return AspectJPointcutAdvisor.class.hashCode();
}
}
@@ -1,58 +0,0 @@
/*
* Copyright 2002-2006 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.aop.aspectj;
import org.springframework.core.Ordered;
/**
* Interface to be implemented by types that can supply the information
* needed to sort advice/advisors by AspectJ's precedence rules.
*
* @author Adrian Colyer
* @since 2.0
* @see org.springframework.aop.aspectj.autoproxy.AspectJPrecedenceComparator
*/
public interface AspectJPrecedenceInformation extends Ordered {
// Implementation note:
// We need the level of indirection this interface provides as otherwise the
// AspectJPrecedenceComparator must ask an Advisor for its Advice in all cases
// in order to sort advisors. This causes problems with the
// InstantiationModelAwarePointcutAdvisor which needs to delay creating
// its advice for aspects with non-singleton instantiation models.
/**
* The name of the aspect (bean) in which the advice was declared.
*/
String getAspectName();
/**
* The declaration order of the advice member within the aspect.
*/
int getDeclarationOrder();
/**
* Return whether this is a before advice.
*/
boolean isBeforeAdvice();
/**
* Return whether this is an after advice.
*/
boolean isAfterAdvice();
}
@@ -1,72 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.util.List;
import org.springframework.aop.Advisor;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
/**
* Utility methods for working with AspectJ proxies.
*
* @author Rod Johnson
* @author Ramnivas Laddad
* @since 2.0
*/
public abstract class AspectJProxyUtils {
/**
* Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors.
* This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut matching)
* and make available the current AspectJ JoinPoint. The call will have no effect if there are no
* AspectJ advisors in the advisor chain.
* @param advisors Advisors available
* @return {@code true} if any special {@link Advisor Advisors} were added, otherwise {@code false}.
*/
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not required
if (!advisors.isEmpty()) {
boolean foundAspectJAdvice = false;
for (Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as
// this might eagerly instantiate a non-singleton AspectJ aspect
if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true;
}
}
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true;
}
}
return false;
}
/**
* Determine whether the given Advisor contains an AspectJ advice.
* @param advisor the Advisor to check
*/
private static boolean isAspectJAdvice(Advisor advisor) {
return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
advisor.getAdvice() instanceof AbstractAspectJAdvice ||
(advisor instanceof PointcutAdvisor &&
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}
}
@@ -1,107 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.aspectj;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessage.Kind;
import org.aspectj.bridge.IMessageHandler;
/**
* Implementation of AspectJ's {@link IMessageHandler} interface that
* routes AspectJ weaving messages through the same logging system as the
* regular Spring messages.
*
* <p>Pass the option...
*
* <p><code class="code">-XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler</code>
*
* <p>to the weaver; for example, specifying the following in a
* "{@code META-INF/aop.xml} file:
*
* <p><code class="code">&lt;weaver options="..."/&gt;</code>
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
*/
public class AspectJWeaverMessageHandler implements IMessageHandler {
private static final String AJ_ID = "[AspectJ] ";
private static final Log logger = LogFactory.getLog("AspectJ Weaver");
@Override
public boolean handleMessage(IMessage message) throws AbortException {
Kind messageKind = message.getKind();
if (messageKind == IMessage.DEBUG) {
if (logger.isDebugEnabled()) {
logger.debug(makeMessageFor(message));
return true;
}
}
else if (messageKind == IMessage.INFO || messageKind == IMessage.WEAVEINFO) {
if (logger.isInfoEnabled()) {
logger.info(makeMessageFor(message));
return true;
}
}
else if (messageKind == IMessage.WARNING) {
if (logger.isWarnEnabled()) {
logger.warn(makeMessageFor(message));
return true;
}
}
else if (messageKind == IMessage.ERROR) {
if (logger.isErrorEnabled()) {
logger.error(makeMessageFor(message));
return true;
}
}
else if (messageKind == IMessage.ABORT) {
if (logger.isFatalEnabled()) {
logger.fatal(makeMessageFor(message));
return true;
}
}
return false;
}
private String makeMessageFor(IMessage aMessage) {
return AJ_ID + aMessage.getMessage();
}
@Override
public boolean isIgnoring(Kind messageKind) {
// We want to see everything, and allow configuration of log levels dynamically.
return false;
}
@Override
public void dontIgnore(Kind messageKind) {
// We weren't ignoring anything anyway...
}
@Override
public void ignore(Kind kind) {
// We weren't ignoring anything anyway...
}
}
@@ -1,116 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.aopalliance.aop.Advice;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.support.ClassFilters;
import org.springframework.aop.support.DelegatePerTargetObjectIntroductionInterceptor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
* Introduction advisor delegating to the given object.
* Implements AspectJ annotation-style behavior for the DeclareParents annotation.
*
* @author Rod Johnson
* @author Ramnivas Laddad
* @since 2.0
*/
public class DeclareParentsAdvisor implements IntroductionAdvisor {
private final Class introducedInterface;
private final ClassFilter typePatternClassFilter;
private final Advice advice;
/**
* Create a new advisor for this DeclareParents field.
* @param interfaceType static field defining the introduction
* @param typePattern type pattern the introduction is restricted to
* @param defaultImpl the default implementation class
*/
public DeclareParentsAdvisor(Class interfaceType, String typePattern, Class defaultImpl) {
this(interfaceType, typePattern, defaultImpl,
new DelegatePerTargetObjectIntroductionInterceptor(defaultImpl, interfaceType));
}
/**
* Create a new advisor for this DeclareParents field.
* @param interfaceType static field defining the introduction
* @param typePattern type pattern the introduction is restricted to
* @param delegateRef the delegate implementation object
*/
public DeclareParentsAdvisor(Class interfaceType, String typePattern, Object delegateRef) {
this(interfaceType, typePattern, delegateRef.getClass(),
new DelegatingIntroductionInterceptor(delegateRef));
}
/**
* Private constructor to share common code between impl-based delegate and reference-based delegate
* (cannot use method such as init() to share common code, due the the use of final fields)
* @param interfaceType static field defining the introduction
* @param typePattern type pattern the introduction is restricted to
* @param implementationClass implementation class
* @param advice delegation advice
*/
private DeclareParentsAdvisor(Class interfaceType, String typePattern, Class implementationClass, Advice advice) {
this.introducedInterface = interfaceType;
ClassFilter typePatternFilter = new TypePatternClassFilter(typePattern);
// Excludes methods implemented.
ClassFilter exclusion = new ClassFilter() {
@Override
public boolean matches(Class clazz) {
return !(introducedInterface.isAssignableFrom(clazz));
}
};
this.typePatternClassFilter = ClassFilters.intersection(typePatternFilter, exclusion);
this.advice = advice;
}
@Override
public ClassFilter getClassFilter() {
return this.typePatternClassFilter;
}
@Override
public void validateInterfaces() throws IllegalArgumentException {
// Do nothing
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Advice getAdvice() {
return this.advice;
}
@Override
public Class[] getInterfaces() {
return new Class[] {this.introducedInterface};
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2006 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.aop.aspectj;
import org.springframework.aop.PointcutAdvisor;
/**
* Interface to be implemented by Spring AOP Advisors wrapping AspectJ
* aspects that may have a lazy initialization strategy. For example,
* a perThis instantiation model would mean lazy initialization of the advice.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
public interface InstantiationModelAwarePointcutAdvisor extends PointcutAdvisor {
/**
* Return whether this advisor is lazily initializing its underlying advice.
*/
boolean isLazy();
/**
* Return whether this advisor has already instantiated its advice.
*/
boolean isAdviceInstantiated();
}
@@ -1,320 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.aspectj.runtime.internal.AroundClosure;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.util.Assert;
/**
* Implementation of AspectJ ProceedingJoinPoint interface
* wrapping an AOP Alliance MethodInvocation.
*
* <p><b>Note</b>: the {@code getThis()} method returns the current Spring AOP proxy.
* The {@code getTarget()} method returns the current Spring AOP target (which may be
* {@code null} if there is no target), and is a plain POJO without any advice.
* <b>If you want to call the object and have the advice take effect, use
* {@code getThis()}.</b> A common example is casting the object to an
* introduced interface in the implementation of an introduction.
*
* <p>Of course there is no such distinction between target and proxy in AspectJ.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Adrian Colyer
* @author Ramnivas Laddad
* @since 2.0
*/
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
private final ProxyMethodInvocation methodInvocation;
private Object[] defensiveCopyOfArgs;
/** Lazily initialized signature object */
private Signature signature;
/** Lazily initialized source location object */
private SourceLocation sourceLocation;
/**
* Create a new MethodInvocationProceedingJoinPoint, wrapping the given
* Spring ProxyMethodInvocation object.
* @param methodInvocation the Spring ProxyMethodInvocation object
*/
public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
Assert.notNull(methodInvocation, "MethodInvocation must not be null");
this.methodInvocation = methodInvocation;
}
@Override
public void set$AroundClosure(AroundClosure aroundClosure) {
throw new UnsupportedOperationException();
}
@Override
public Object proceed() throws Throwable {
return this.methodInvocation.invocableClone().proceed();
}
@Override
public Object proceed(Object[] arguments) throws Throwable {
Assert.notNull(arguments, "Argument array passed to proceed cannot be null");
if (arguments.length != this.methodInvocation.getArguments().length) {
throw new IllegalArgumentException("Expecting " +
this.methodInvocation.getArguments().length + " arguments to proceed, " +
"but was passed " + arguments.length + " arguments");
}
this.methodInvocation.setArguments(arguments);
return this.methodInvocation.invocableClone(arguments).proceed();
}
/**
* Returns the Spring AOP proxy. Cannot be {@code null}.
*/
@Override
public Object getThis() {
return this.methodInvocation.getProxy();
}
/**
* Returns the Spring AOP target. May be {@code null} if there is no target.
*/
@Override
public Object getTarget() {
return this.methodInvocation.getThis();
}
@Override
public Object[] getArgs() {
if (this.defensiveCopyOfArgs == null) {
Object[] argsSource = this.methodInvocation.getArguments();
this.defensiveCopyOfArgs = new Object[argsSource.length];
System.arraycopy(argsSource, 0, this.defensiveCopyOfArgs, 0, argsSource.length);
}
return this.defensiveCopyOfArgs;
}
@Override
public Signature getSignature() {
if (this.signature == null) {
this.signature = new MethodSignatureImpl();
}
return signature;
}
@Override
public SourceLocation getSourceLocation() {
if (this.sourceLocation == null) {
this.sourceLocation = new SourceLocationImpl();
}
return this.sourceLocation;
}
@Override
public String getKind() {
return ProceedingJoinPoint.METHOD_EXECUTION;
}
@Override
public int getId() {
// TODO: It's just an adapter but returning 0 might still have side effects...
return 0;
}
@Override
public JoinPoint.StaticPart getStaticPart() {
return this;
}
@Override
public String toShortString() {
return "execution(" + getSignature().toShortString() + ")";
}
@Override
public String toLongString() {
return "execution(" + getSignature().toLongString() + ")";
}
@Override
public String toString() {
return "execution(" + getSignature().toString() + ")";
}
/**
* Lazily initialized MethodSignature.
*/
private class MethodSignatureImpl implements MethodSignature {
private volatile String[] parameterNames;
@Override
public String getName() {
return methodInvocation.getMethod().getName();
}
@Override
public int getModifiers() {
return methodInvocation.getMethod().getModifiers();
}
@Override
public Class getDeclaringType() {
return methodInvocation.getMethod().getDeclaringClass();
}
@Override
public String getDeclaringTypeName() {
return methodInvocation.getMethod().getDeclaringClass().getName();
}
@Override
public Class getReturnType() {
return methodInvocation.getMethod().getReturnType();
}
@Override
public Method getMethod() {
return methodInvocation.getMethod();
}
@Override
public Class[] getParameterTypes() {
return methodInvocation.getMethod().getParameterTypes();
}
@Override
public String[] getParameterNames() {
if (this.parameterNames == null) {
this.parameterNames = (new LocalVariableTableParameterNameDiscoverer()).getParameterNames(getMethod());
}
return this.parameterNames;
}
@Override
public Class[] getExceptionTypes() {
return methodInvocation.getMethod().getExceptionTypes();
}
@Override
public String toShortString() {
return toString(false, false, false, false);
}
@Override
public String toLongString() {
return toString(true, true, true, true);
}
@Override
public String toString() {
return toString(false, true, false, true);
}
private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,
boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {
StringBuilder sb = new StringBuilder();
if (includeModifier) {
sb.append(Modifier.toString(getModifiers()));
sb.append(" ");
}
if (includeReturnTypeAndArgs) {
appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);
sb.append(" ");
}
appendType(sb, getDeclaringType(), useLongTypeName);
sb.append(".");
sb.append(getMethod().getName());
sb.append("(");
Class[] parametersTypes = getParameterTypes();
appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);
sb.append(")");
return sb.toString();
}
private void appendTypes(StringBuilder sb, Class<?>[] types,
boolean includeArgs, boolean useLongReturnAndArgumentTypeName) {
if (includeArgs) {
for (int size = types.length, i = 0; i < size; i++) {
appendType(sb, types[i], useLongReturnAndArgumentTypeName);
if (i < size - 1) {
sb.append(",");
}
}
}
else {
if (types.length != 0) {
sb.append("..");
}
}
}
private void appendType(StringBuilder sb, Class<?> type, boolean useLongTypeName) {
if (type.isArray()) {
appendType(sb, type.getComponentType(), useLongTypeName);
sb.append("[]");
}
else {
sb.append(useLongTypeName ? type.getName() : type.getSimpleName());
}
}
}
/**
* Lazily initialized SourceLocation.
*/
private class SourceLocationImpl implements SourceLocation {
@Override
public Class getWithinType() {
if (methodInvocation.getThis() == null) {
throw new UnsupportedOperationException("No source location joinpoint available: target is null");
}
return methodInvocation.getThis().getClass();
}
@Override
public String getFileName() {
throw new UnsupportedOperationException();
}
@Override
public int getLine() {
throw new UnsupportedOperationException();
}
@Override
public int getColumn() {
throw new UnsupportedOperationException();
}
}
}
@@ -1,269 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import java.lang.reflect.Field;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.ast.And;
import org.aspectj.weaver.ast.Call;
import org.aspectj.weaver.ast.FieldGetCall;
import org.aspectj.weaver.ast.HasAnnotation;
import org.aspectj.weaver.ast.ITestVisitor;
import org.aspectj.weaver.ast.Instanceof;
import org.aspectj.weaver.ast.Literal;
import org.aspectj.weaver.ast.Not;
import org.aspectj.weaver.ast.Or;
import org.aspectj.weaver.ast.Test;
import org.aspectj.weaver.internal.tools.MatchingContextBasedTest;
import org.aspectj.weaver.reflect.ReflectionVar;
import org.aspectj.weaver.reflect.ShadowMatchImpl;
import org.aspectj.weaver.tools.ShadowMatch;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* This class encapsulates some AspectJ internal knowledge that should be
* pushed back into the AspectJ project in a future release.
*
* <p>It relies on implementation specific knowledge in AspectJ to break
* encapsulation and do something AspectJ was not designed to do: query
* the types of runtime tests that will be performed. The code here should
* migrate to {@code ShadowMatch.getVariablesInvolvedInRuntimeTest()}
* or some similar operation.
*
* <p>See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593"/>.
*
* @author Adrian Colyer
* @author Ramnivas Laddad
* @since 2.0
*/
class RuntimeTestWalker {
private final Test runtimeTest;
public RuntimeTestWalker(ShadowMatch shadowMatch) {
ShadowMatchImpl shadowMatchImplementation = (ShadowMatchImpl) shadowMatch;
try {
Field testField = shadowMatchImplementation.getClass().getDeclaredField("residualTest");
ReflectionUtils.makeAccessible(testField);
this.runtimeTest = (Test) testField.get(shadowMatch);
}
catch (NoSuchFieldException noSuchFieldEx) {
throw new IllegalStateException("The version of aspectjtools.jar / aspectjweaver.jar " +
"on the classpath is incompatible with this version of Spring: Expected field " +
"'runtimeTest' is not present on ShadowMatchImpl class.");
}
catch (IllegalAccessException illegalAccessEx) {
// Famous last words... but I don't see how this can happen given the
// makeAccessible call above
throw new IllegalStateException("Unable to access ShadowMatchImpl.residualTest field");
}
}
/**
* If the test uses any of the this, target, at_this, at_target, and at_annotation vars,
* then it tests subtype sensitive vars.
*/
public boolean testsSubtypeSensitiveVars() {
return (this.runtimeTest != null &&
new SubtypeSensitiveVarTypeTestVisitor().testsSubtypeSensitiveVars(this.runtimeTest));
}
public boolean testThisInstanceOfResidue(Class thisClass) {
return (this.runtimeTest != null &&
new ThisInstanceOfResidueTestVisitor(thisClass).thisInstanceOfMatches(this.runtimeTest));
}
public boolean testTargetInstanceOfResidue(Class targetClass) {
return (this.runtimeTest != null &&
new TargetInstanceOfResidueTestVisitor(targetClass).targetInstanceOfMatches(this.runtimeTest));
}
private static class TestVisitorAdapter implements ITestVisitor {
protected static final int THIS_VAR = 0;
protected static final int TARGET_VAR = 1;
protected static final int AT_THIS_VAR = 3;
protected static final int AT_TARGET_VAR = 4;
protected static final int AT_ANNOTATION_VAR = 8;
@Override
public void visit(And e) {
e.getLeft().accept(this);
e.getRight().accept(this);
}
@Override
public void visit(Or e) {
e.getLeft().accept(this);
e.getRight().accept(this);
}
@Override
public void visit(Not e) {
e.getBody().accept(this);
}
@Override
public void visit(Instanceof i) {
}
@Override
public void visit(Literal literal) {
}
@Override
public void visit(Call call) {
}
@Override
public void visit(FieldGetCall fieldGetCall) {
}
@Override
public void visit(HasAnnotation hasAnnotation) {
}
@Override
public void visit(MatchingContextBasedTest matchingContextTest) {
}
protected int getVarType(ReflectionVar v) {
try {
Field varTypeField = ReflectionVar.class.getDeclaredField("varType");
ReflectionUtils.makeAccessible(varTypeField);
return (Integer) varTypeField.get(v);
}
catch (NoSuchFieldException noSuchFieldEx) {
throw new IllegalStateException("the version of aspectjtools.jar / aspectjweaver.jar " +
"on the classpath is incompatible with this version of Spring:- expected field " +
"'varType' is not present on ReflectionVar class");
}
catch (IllegalAccessException illegalAccessEx) {
// Famous last words... but I don't see how this can happen given the
// makeAccessible call above
throw new IllegalStateException("Unable to access ReflectionVar.varType field");
}
}
}
private static abstract class InstanceOfResidueTestVisitor extends TestVisitorAdapter {
private Class matchClass;
private boolean matches;
private int matchVarType;
public InstanceOfResidueTestVisitor(Class matchClass, boolean defaultMatches, int matchVarType) {
this.matchClass = matchClass;
this.matches = defaultMatches;
this.matchVarType = matchVarType;
}
public boolean instanceOfMatches(Test test) {
test.accept(this);
return matches;
}
@Override
public void visit(Instanceof i) {
ResolvedType type = (ResolvedType) i.getType();
int varType = getVarType((ReflectionVar) i.getVar());
if (varType != this.matchVarType) {
return;
}
try {
Class typeClass = ClassUtils.forName(type.getName(), this.matchClass.getClassLoader());
// Don't use ReflectionType.isAssignableFrom() as it won't be aware of (Spring) mixins
this.matches = typeClass.isAssignableFrom(this.matchClass);
}
catch (ClassNotFoundException ex) {
this.matches = false;
}
}
}
/**
* Check if residue of target(TYPE) kind. See SPR-3783 for more details.
*/
private static class TargetInstanceOfResidueTestVisitor extends InstanceOfResidueTestVisitor {
public TargetInstanceOfResidueTestVisitor(Class targetClass) {
super(targetClass, false, TARGET_VAR);
}
public boolean targetInstanceOfMatches(Test test) {
return instanceOfMatches(test);
}
}
/**
* Check if residue of this(TYPE) kind. See SPR-2979 for more details.
*/
private static class ThisInstanceOfResidueTestVisitor extends InstanceOfResidueTestVisitor {
public ThisInstanceOfResidueTestVisitor(Class thisClass) {
super(thisClass, true, THIS_VAR);
}
// TODO: Optimization: Process only if this() specifies a type and not an identifier.
public boolean thisInstanceOfMatches(Test test) {
return instanceOfMatches(test);
}
}
private static class SubtypeSensitiveVarTypeTestVisitor extends TestVisitorAdapter {
private final Object thisObj = new Object();
private final Object targetObj = new Object();
private final Object[] argsObjs = new Object[0];
private boolean testsSubtypeSensitiveVars = false;
public boolean testsSubtypeSensitiveVars(Test aTest) {
aTest.accept(this);
return this.testsSubtypeSensitiveVars;
}
@Override
public void visit(Instanceof i) {
ReflectionVar v = (ReflectionVar) i.getVar();
Object varUnderTest = v.getBindingAtJoinPoint(thisObj,targetObj,argsObjs);
if ((varUnderTest == thisObj) || (varUnderTest == targetObj)) {
this.testsSubtypeSensitiveVars = true;
}
}
@Override
public void visit(HasAnnotation hasAnn) {
// If you thought things were bad before, now we sink to new levels of horror...
ReflectionVar v = (ReflectionVar) hasAnn.getVar();
int varType = getVarType(v);
if ((varType == AT_THIS_VAR) || (varType == AT_TARGET_VAR) || (varType == AT_ANNOTATION_VAR)) {
this.testsSubtypeSensitiveVars = true;
}
}
}
}
@@ -1,94 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
/**
* Implementation of {@link AspectInstanceFactory} that creates a new instance
* of the specified aspect class for every {@link #getAspectInstance()} call.
*
* @author Juergen Hoeller
* @since 2.0.4
*/
public class SimpleAspectInstanceFactory implements AspectInstanceFactory {
private final Class aspectClass;
/**
* Create a new SimpleAspectInstanceFactory for the given aspect class.
* @param aspectClass the aspect class
*/
public SimpleAspectInstanceFactory(Class aspectClass) {
Assert.notNull(aspectClass, "Aspect class must not be null");
this.aspectClass = aspectClass;
}
/**
* Return the specified aspect class (never {@code null}).
*/
public final Class getAspectClass() {
return this.aspectClass;
}
@Override
public final Object getAspectInstance() {
try {
return this.aspectClass.newInstance();
}
catch (InstantiationException ex) {
throw new AopConfigException("Unable to instantiate aspect class [" + this.aspectClass.getName() + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopConfigException("Cannot access element class [" + this.aspectClass.getName() + "]", ex);
}
}
@Override
public ClassLoader getAspectClassLoader() {
return this.aspectClass.getClassLoader();
}
/**
* Determine the order for this factory's aspect instance,
* either an instance-specific order expressed through implementing
* the {@link org.springframework.core.Ordered} interface,
* or a fallback order.
* @see org.springframework.core.Ordered
* @see #getOrderForAspectClass
*/
@Override
public int getOrder() {
return getOrderForAspectClass(this.aspectClass);
}
/**
* Determine a fallback order for the case that the aspect instance
* does not express an instance-specific order through implementing
* the {@link org.springframework.core.Ordered} interface.
* <p>The default implementation simply returns {@code Ordered.LOWEST_PRECEDENCE}.
* @param aspectClass the aspect class
*/
protected int getOrderForAspectClass(Class<?> aspectClass) {
return Ordered.LOWEST_PRECEDENCE;
}
}
@@ -1,84 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
/**
* Implementation of {@link AspectInstanceFactory} that is backed by a
* specified singleton object, returning the same instance for every
* {@link #getAspectInstance()} call.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see SimpleAspectInstanceFactory
*/
public class SingletonAspectInstanceFactory implements AspectInstanceFactory {
private final Object aspectInstance;
/**
* Create a new SingletonAspectInstanceFactory for the given aspect instance.
* @param aspectInstance the singleton aspect instance
*/
public SingletonAspectInstanceFactory(Object aspectInstance) {
Assert.notNull(aspectInstance, "Aspect instance must not be null");
this.aspectInstance = aspectInstance;
}
@Override
public final Object getAspectInstance() {
return this.aspectInstance;
}
@Override
public ClassLoader getAspectClassLoader() {
return this.aspectInstance.getClass().getClassLoader();
}
/**
* Determine the order for this factory's aspect instance,
* either an instance-specific order expressed through implementing
* the {@link org.springframework.core.Ordered} interface,
* or a fallback order.
* @see org.springframework.core.Ordered
* @see #getOrderForAspectClass
*/
@Override
public int getOrder() {
if (this.aspectInstance instanceof Ordered) {
return ((Ordered) this.aspectInstance).getOrder();
}
return getOrderForAspectClass(this.aspectInstance.getClass());
}
/**
* Determine a fallback order for the case that the aspect instance
* does not express an instance-specific order through implementing
* the {@link org.springframework.core.Ordered} interface.
* <p>The default implementation simply returns {@code Ordered.LOWEST_PRECEDENCE}.
* @param aspectClass the aspect class
*/
protected int getOrderForAspectClass(Class<?> aspectClass) {
return Ordered.LOWEST_PRECEDENCE;
}
}
@@ -1,116 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.TypePatternMatcher;
import org.springframework.aop.ClassFilter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Spring AOP {@link ClassFilter} implementation using AspectJ type matching.
*
* @author Rod Johnson
* @since 2.0
*/
public class TypePatternClassFilter implements ClassFilter {
private String typePattern;
private TypePatternMatcher aspectJTypePatternMatcher;
/**
* Creates a new instance of the {@link TypePatternClassFilter} class.
* <p>This is the JavaBean constructor; be sure to set the
* {@link #setTypePattern(String) typePattern} property, else a
* no doubt fatal {@link IllegalStateException} will be thrown
* when the {@link #matches(Class)} method is first invoked.
*/
public TypePatternClassFilter() {
}
/**
* Create a fully configured {@link TypePatternClassFilter} using the
* given type pattern.
* @param typePattern the type pattern that AspectJ weaver should parse
* @throws IllegalArgumentException if the supplied {@code typePattern} is {@code null}
* or is recognized as invalid
*/
public TypePatternClassFilter(String typePattern) {
setTypePattern(typePattern);
}
/**
* Set the AspectJ type pattern to match.
* <p>Examples include:
* <code class="code">
* org.springframework.beans.*
* </code>
* This will match any class or interface in the given package.
* <code class="code">
* org.springframework.beans.ITestBean+
* </code>
* This will match the {@code ITestBean} interface and any class
* that implements it.
* <p>These conventions are established by AspectJ, not Spring AOP.
* @param typePattern the type pattern that AspectJ weaver should parse
* @throws IllegalArgumentException if the supplied {@code typePattern} is {@code null}
* or is recognized as invalid
*/
public void setTypePattern(String typePattern) {
Assert.notNull(typePattern);
this.typePattern = typePattern;
this.aspectJTypePatternMatcher =
PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution().
parseTypePattern(replaceBooleanOperators(typePattern));
}
public String getTypePattern() {
return typePattern;
}
/**
* Should the pointcut apply to the given interface or target class?
* @param clazz candidate target class
* @return whether the advice should apply to this candidate target class
* @throws IllegalStateException if no {@link #setTypePattern(String)} has been set
*/
@Override
public boolean matches(Class clazz) {
if (this.aspectJTypePatternMatcher == null) {
throw new IllegalStateException("No 'typePattern' has been set via ctor/setter.");
}
return this.aspectJTypePatternMatcher.matches(clazz);
}
/**
* If a type pattern has been specified in XML, the user cannot
* write {@code and} as "&&" (though &amp;&amp; will work).
* We also allow {@code and} between two sub-expressions.
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
*/
private String replaceBooleanOperators(String pcExpr) {
pcExpr = StringUtils.replace(pcExpr," and "," && ");
pcExpr = StringUtils.replace(pcExpr, " or ", " || ");
pcExpr = StringUtils.replace(pcExpr, " not ", " ! ");
return pcExpr;
}
}
@@ -1,342 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.AjType;
import org.aspectj.lang.reflect.AjTypeSystem;
import org.aspectj.lang.reflect.PerClauseKind;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PrioritizedParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;
/**
* Abstract base class for factories that can create Spring AOP Advisors
* given AspectJ classes from classes honoring the AspectJ 5 annotation syntax.
*
* <p>This class handles annotation parsing and validation functionality.
* It does not actually generate Spring AOP Advisors, which is deferred to subclasses.
*
* @author Rod Johnson
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
protected static final ParameterNameDiscoverer ASPECTJ_ANNOTATION_PARAMETER_NAME_DISCOVERER =
new AspectJAnnotationParameterNameDiscoverer();
private static final String AJC_MAGIC = "ajc$";
/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...)
*/
@SuppressWarnings("unchecked")
protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
Class<? extends Annotation>[] classesToLookFor = new Class[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<? extends Annotation> c : classesToLookFor) {
AspectJAnnotation foundAnnotation = findAnnotation(method, c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<A>(result);
}
else {
return null;
}
}
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
protected final ParameterNameDiscoverer parameterNameDiscoverer;
protected AbstractAspectJAdvisorFactory() {
PrioritizedParameterNameDiscoverer prioritizedParameterNameDiscoverer = new PrioritizedParameterNameDiscoverer();
prioritizedParameterNameDiscoverer.addDiscoverer(ASPECTJ_ANNOTATION_PARAMETER_NAME_DISCOVERER);
this.parameterNameDiscoverer = prioritizedParameterNameDiscoverer;
}
/**
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
* is that aspects written in the code-style (AspectJ language) also have the annotation present
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
*/
@Override
public boolean isAspect(Class<?> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
/**
* We need to detect this as "code-style" AspectJ aspects should not be
* interpreted by Spring AOP.
*/
private boolean compiledByAjc(Class<?> clazz) {
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
// an implementation detail of the AspectJ compiler.
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
}
@Override
public void validate(Class<?> aspectClass) throws AopConfigException {
// If the parent has the annotation and isn't abstract it's an error
if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
!Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
aspectClass.getSuperclass().getName() + "]");
}
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
if (!ajType.isAspect()) {
throw new NotAnAtAspectException(aspectClass);
}
if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOW) {
throw new AopConfigException(aspectClass.getName() + " uses percflow instantiation model: " +
"This is not supported in Spring AOP.");
}
if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOWBELOW) {
throw new AopConfigException(aspectClass.getName() + " uses percflowbelow instantiation model: " +
"This is not supported in Spring AOP.");
}
}
/**
* The pointcut and advice annotations both have an "argNames" member which contains a
* comma-separated list of the argument names. We use this (if non-empty) to build the
* formal parameters for the pointcut.
*/
protected AspectJExpressionPointcut createPointcutExpression(
Method annotatedMethod, Class declarationScope, String[] pointcutParameterNames) {
Class<?> [] pointcutParameterTypes = new Class<?>[0];
if (pointcutParameterNames != null) {
pointcutParameterTypes = extractPointcutParameterTypes(pointcutParameterNames,annotatedMethod);
}
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(declarationScope,pointcutParameterNames,pointcutParameterTypes);
ajexp.setLocation(annotatedMethod.toString());
return ajexp;
}
/**
* Create the pointcut parameters needed by aspectj based on the given argument names
* and the argument types that are available from the adviceMethod. Needs to take into
* account (ignore) any JoinPoint based arguments as these are not pointcut context but
* rather part of the advice execution context (thisJoinPoint, thisJoinPointStaticPart)
*/
private Class<?>[] extractPointcutParameterTypes(String[] argNames, Method adviceMethod) {
Class<?>[] ret = new Class<?>[argNames.length];
Class<?>[] paramTypes = adviceMethod.getParameterTypes();
if (argNames.length > paramTypes.length) {
throw new IllegalStateException("Expecting at least " + argNames.length +
" arguments in the advice declaration, but only found " + paramTypes.length);
}
// Make the simplifying assumption for now that all of the JoinPoint based arguments
// come first in the advice declaration.
int typeOffset = paramTypes.length - argNames.length;
for (int i = 0; i < ret.length; i++) {
ret[i] = paramTypes[i + typeOffset];
}
return ret;
}
protected enum AspectJAnnotationType {
AtPointcut,
AtBefore,
AtAfter,
AtAfterReturning,
AtAfterThrowing,
AtAround
}
/**
* Class modelling an AspectJ annotation, exposing its type enumeration and
* pointcut String.
*/
protected static class AspectJAnnotation<A extends Annotation> {
private static final String[] EXPRESSION_PROPERTIES = new String[] {"value", "pointcut"};
private static Map<Class, AspectJAnnotationType> annotationTypes =
new HashMap<Class, AspectJAnnotationType>();
static {
annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
}
private final A annotation;
private final AspectJAnnotationType annotationType;
private final String pointcutExpression;
private final String argumentNames;
public AspectJAnnotation(A annotation) {
this.annotation = annotation;
this.annotationType = determineAnnotationType(annotation);
// We know these methods exist with the same name on each object,
// but need to invoke them reflectively as there isn't a common interface.
try {
this.pointcutExpression = resolveExpression(annotation);
this.argumentNames = (String) annotation.getClass().getMethod("argNames").invoke(annotation);
}
catch (Exception ex) {
throw new IllegalArgumentException(annotation + " cannot be an AspectJ annotation", ex);
}
}
private AspectJAnnotationType determineAnnotationType(A annotation) {
for (Class type : annotationTypes.keySet()) {
if (type.isInstance(annotation)) {
return annotationTypes.get(type);
}
}
throw new IllegalStateException("Unknown annotation type: " + annotation.toString());
}
private String resolveExpression(A annotation) throws Exception {
String expression = null;
for (String methodName : EXPRESSION_PROPERTIES) {
Method method;
try {
method = annotation.getClass().getDeclaredMethod(methodName);
}
catch (NoSuchMethodException ex) {
method = null;
}
if (method != null) {
String candidate = (String) method.invoke(annotation);
if (StringUtils.hasText(candidate)) {
expression = candidate;
}
}
}
return expression;
}
public AspectJAnnotationType getAnnotationType() {
return this.annotationType;
}
public A getAnnotation() {
return this.annotation;
}
public String getPointcutExpression() {
return this.pointcutExpression;
}
public String getArgumentNames() {
return this.argumentNames;
}
@Override
public String toString() {
return this.annotation.toString();
}
}
/**
* ParameterNameDiscoverer implementation that analyzes the arg names
* specified at the AspectJ annotation level.
*/
private static class AspectJAnnotationParameterNameDiscoverer implements ParameterNameDiscoverer {
@Override
public String[] getParameterNames(Method method) {
if (method.getParameterTypes().length == 0) {
return new String[0];
}
AspectJAnnotation annotation = findAspectJAnnotationOnMethod(method);
if (annotation == null) {
return null;
}
StringTokenizer strTok = new StringTokenizer(annotation.getArgumentNames(), ",");
if (strTok.countTokens() > 0) {
String[] names = new String[strTok.countTokens()];
for (int i = 0; i < names.length; i++) {
names[i] = strTok.nextToken();
}
return names;
}
else {
return null;
}
}
@Override
public String[] getParameterNames(Constructor ctor) {
throw new UnsupportedOperationException("Spring AOP cannot handle constructor advice");
}
}
}
@@ -1,142 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.util.Assert;
/**
* {@link AspectJAwareAdvisorAutoProxyCreator} subclass that processes all AspectJ
* annotation aspects in the current application context, as well as Spring Advisors.
*
* <p>Any AspectJ annotated classes will automatically be recognized, and their
* advice applied if Spring AOP's proxy-based model is capable of applying it.
* This covers method execution joinpoints.
*
* <p>If the &lt;aop:include&gt; element is used, only @AspectJ beans with names matched by
* an include pattern will be considered as defining aspects to use for Spring auto-proxying.
*
* <p>Processing of Spring Advisors follows the rules established in
* {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.aop.aspectj.annotation.AspectJAdvisorFactory
*/
@SuppressWarnings("serial")
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
private List<Pattern> includePatterns;
private AspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory();
private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;
/**
* Set a list of regex patterns, matching eligible @AspectJ bean names.
* <p>Default is to consider all @AspectJ beans as eligible.
*/
public void setIncludePatterns(List<String> patterns) {
this.includePatterns = new ArrayList<Pattern>(patterns.size());
for (String patternText : patterns) {
this.includePatterns.add(Pattern.compile(patternText));
}
}
public void setAspectJAdvisorFactory(AspectJAdvisorFactory aspectJAdvisorFactory) {
Assert.notNull(this.aspectJAdvisorFactory, "AspectJAdvisorFactory must not be null");
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
}
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
this.aspectJAdvisorsBuilder =
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
@Override
protected boolean isInfrastructureClass(Class beanClass) {
// Previously we setProxyTargetClass(true) in the constructor, but that has too
// broad an impact. Instead we now override isInfrastructureClass to avoid proxying
// aspects. I'm not entirely happy with that as there is no good reason not
// to advise aspects, except that it causes advice invocation to go through a
// proxy, and if the aspect implements e.g the Ordered interface it will be
// proxied by that interface and fail at runtime as the advice method is not
// defined on the interface. We could potentially relax the restriction about
// not advising aspects in the future.
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
}
/**
* Check whether the given aspect bean is eligible for auto-proxying.
* <p>If no &lt;aop:include&gt; elements were used then "includePatterns" will be
* {@code null} and all beans are included. If "includePatterns" is non-null,
* then one of the patterns must match.
*/
protected boolean isEligibleAspectBean(String beanName) {
if (this.includePatterns == null) {
return true;
}
else {
for (Pattern pattern : this.includePatterns) {
if (pattern.matcher(beanName).matches()) {
return true;
}
}
return false;
}
}
/**
* Subclass of BeanFactoryAspectJAdvisorsBuilderAdapter that delegates to
* surrounding AnnotationAwareAspectJAutoProxyCreator facilities.
*/
private class BeanFactoryAspectJAdvisorsBuilderAdapter extends BeanFactoryAspectJAdvisorsBuilder {
public BeanFactoryAspectJAdvisorsBuilderAdapter(
ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
super(beanFactory, advisorFactory);
}
@Override
protected boolean isEligibleBean(String beanName) {
return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName);
}
}
}
@@ -1,104 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import java.lang.reflect.Method;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.AopConfigException;
/**
* Interface for factories that can create Spring AOP Advisors from classes
* annotated with AspectJ annotation syntax.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see AspectMetadata
* @see org.aspectj.lang.reflect.AjTypeSystem
*/
public interface AspectJAdvisorFactory {
/**
* Determine whether or not the given class is an aspect, as reported
* by AspectJ's {@link org.aspectj.lang.reflect.AjTypeSystem}.
* <p>Will simply return {@code false} if the supposed aspect is
* invalid (such as an extension of a concrete aspect class).
* Will return true for some aspects that Spring AOP cannot process,
* such as those with unsupported instantiation models.
* Use the {@link #validate} method to handle these cases if necessary.
* @param clazz the supposed annotation-style AspectJ class
* @return whether or not this class is recognized by AspectJ as an aspect class
*/
boolean isAspect(Class<?> clazz);
/**
* Is the given class a valid AspectJ aspect class?
* @param aspectClass the supposed AspectJ annotation-style class to validate
* @throws AopConfigException if the class is an invalid aspect
* (which can never be legal)
* @throws NotAnAtAspectException if the class is not an aspect at all
* (which may or may not be legal, depending on the context)
*/
void validate(Class<?> aspectClass) throws AopConfigException;
/**
* Build Spring AOP Advisors for all annotated At-AspectJ methods
* on the specified aspect instance.
* @param aif the aspect instance factory (not the aspect instance itself
* in order to avoid eager instantiation)
* @return a list of advisors for this class
*/
List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aif);
/**
* Build a Spring AOP Advisor for the given AspectJ advice method.
* @param candidateAdviceMethod the candidate advice method
* @param aif the aspect instance factory
* @param declarationOrderInAspect the declaration order within the aspect
* @param aspectName the name of the aspect
* @return {@code null} if the method is not an AspectJ advice method
* or if it is a pointcut that will be used by other advice but will not
* create a Spring advice in its own right
*/
Advisor getAdvisor(Method candidateAdviceMethod,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName);
/**
* Build a Spring AOP Advice for the given AspectJ advice method.
* @param candidateAdviceMethod the candidate advice method
* @param pointcut the corresponding AspectJ expression pointcut
* @param aif the aspect instance factory
* @param declarationOrderInAspect the declaration order within the aspect
* @param aspectName the name of the aspect
* @return {@code null} if the method is not an AspectJ advice method
* or if it is a pointcut that will be used by other advice but will not
* create a Spring advice in its own right
* @see org.springframework.aop.aspectj.AspectJAroundAdvice
* @see org.springframework.aop.aspectj.AspectJMethodBeforeAdvice
* @see org.springframework.aop.aspectj.AspectJAfterAdvice
* @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice
* @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
*/
Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut pointcut,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName);
}
@@ -1,211 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.aspectj.lang.reflect.PerClauseKind;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJProxyUtils;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.framework.ProxyCreatorSupport;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.OrderComparator;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* AspectJ-based proxy factory, allowing for programmatic building
* of proxies which include AspectJ aspects (code style as well
* Java 5 annotation style).
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @since 2.0
* @see #addAspect(Object)
* @see #addAspect(Class)
* @see #getProxy()
* @see #getProxy(ClassLoader)
* @see org.springframework.aop.framework.ProxyFactory
*/
@SuppressWarnings("serial")
public class AspectJProxyFactory extends ProxyCreatorSupport {
/** Cache for singleton aspect instances */
private static final Map<Class, Object> aspectCache = new HashMap<Class, Object>();
private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();
/**
* Create a new AspectJProxyFactory.
*/
public AspectJProxyFactory() {
}
/**
* Create a new AspectJProxyFactory.
* <p>Will proxy all interfaces that the given target implements.
* @param target the target object to be proxied
*/
public AspectJProxyFactory(Object target) {
Assert.notNull(target, "Target object must not be null");
setInterfaces(ClassUtils.getAllInterfaces(target));
setTarget(target);
}
/**
* Create a new {@code AspectJProxyFactory}.
* No target, only interfaces. Must add interceptors.
*/
public AspectJProxyFactory(Class[] interfaces) {
setInterfaces(interfaces);
}
/**
* Add the supplied aspect instance to the chain. The type of the aspect instance
* supplied must be a singleton aspect. True singleton lifecycle is not honoured when
* using this method - the caller is responsible for managing the lifecycle of any
* aspects added in this way.
* @param aspectInstance the AspectJ aspect instance
*/
public void addAspect(Object aspectInstance) {
Class aspectClass = aspectInstance.getClass();
String aspectName = aspectClass.getName();
AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
throw new IllegalArgumentException(
"Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
}
addAdvisorsFromAspectInstanceFactory(
new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
}
/**
* Add an aspect of the supplied type to the end of the advice chain.
* @param aspectClass the AspectJ aspect class
*/
public void addAspect(Class aspectClass) {
String aspectName = aspectClass.getName();
AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
addAdvisorsFromAspectInstanceFactory(instanceFactory);
}
/**
* Add all {@link Advisor Advisors} from the supplied {@link MetadataAwareAspectInstanceFactory}
* to the current chain. Exposes any special purpose {@link Advisor Advisors} if needed.
* @see AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary(List)
*/
private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
advisors = AopUtils.findAdvisorsThatCanApply(advisors, getTargetClass());
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
OrderComparator.sort(advisors);
addAdvisors(advisors);
}
/**
* Create an {@link AspectMetadata} instance for the supplied aspect type.
*/
private AspectMetadata createAspectMetadata(Class aspectClass, String aspectName) {
AspectMetadata am = new AspectMetadata(aspectClass, aspectName);
if (!am.getAjType().isAspect()) {
throw new IllegalArgumentException("Class [" + aspectClass.getName() + "] is not a valid aspect type");
}
return am;
}
/**
* Create a {@link MetadataAwareAspectInstanceFactory} for the supplied aspect type. If the aspect type
* has no per clause, then a {@link SingletonMetadataAwareAspectInstanceFactory} is returned, otherwise
* a {@link PrototypeAspectInstanceFactory} is returned.
*/
private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
AspectMetadata am, Class aspectClass, String aspectName) {
MetadataAwareAspectInstanceFactory instanceFactory = null;
if (am.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// Create a shared aspect instance.
Object instance = getSingletonAspectInstance(aspectClass);
instanceFactory = new SingletonMetadataAwareAspectInstanceFactory(instance, aspectName);
}
else {
// Create a factory for independent aspect instances.
instanceFactory = new SimpleMetadataAwareAspectInstanceFactory(aspectClass, aspectName);
}
return instanceFactory;
}
/**
* Get the singleton aspect instance for the supplied aspect type. An instance
* is created if one cannot be found in the instance cache.
*/
private Object getSingletonAspectInstance(Class aspectClass) {
synchronized (aspectCache) {
Object instance = aspectCache.get(aspectClass);
if (instance != null) {
return instance;
}
try {
instance = aspectClass.newInstance();
aspectCache.put(aspectClass, instance);
return instance;
}
catch (InstantiationException ex) {
throw new AopConfigException("Unable to instantiate aspect class [" + aspectClass.getName() + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopConfigException("Cannot access aspect class [" + aspectClass.getName() + "]", ex);
}
}
}
/**
* Create a new proxy according to the settings in this factory.
* <p>Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
* <p>Uses a default class loader: Usually, the thread context class loader
* (if necessary for proxy creation).
* @return the new proxy
*/
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) createAopProxy().getProxy();
}
/**
* Create a new proxy according to the settings in this factory.
* <p>Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
* <p>Uses the given class loader (if necessary for proxy creation).
* @param classLoader the class loader to create the proxy with
* @return the new proxy
*/
@SuppressWarnings("unchecked")
public <T> T getProxy(ClassLoader classLoader) {
return (T) createAopProxy().getProxy(classLoader);
}
}
@@ -1,176 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.AjType;
import org.aspectj.lang.reflect.AjTypeSystem;
import org.aspectj.lang.reflect.PerClauseKind;
import org.springframework.aop.Pointcut;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.TypePatternClassFilter;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.support.ComposablePointcut;
/**
* Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut
* for the per clause.
*
* <p>Uses AspectJ 5 AJType reflection API, so is only supported on Java 5.
* Enables us to work with different AspectJ instantiation models such as
* "singleton", "pertarget" and "perthis".
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.aop.aspectj.AspectJExpressionPointcut
*/
public class AspectMetadata {
/**
* AspectJ reflection information (AspectJ 5 / Java 5 specific).
*/
private final AjType ajType;
/**
* Spring AOP pointcut corresponding to the per clause of the
* aspect. Will be the Pointcut.TRUE canonical instance in the
* case of a singleton, otherwise an AspectJExpressionPointcut.
*/
private final Pointcut perClausePointcut;
/**
* The name of this aspect as defined to Spring (the bean name) -
* allows us to determine if two pieces of advice come from the
* same aspect and hence their relative precedence.
*/
private String aspectName;
/**
* Create a new AspectMetadata instance for the given aspect class.
* @param aspectClass the aspect class
* @param aspectName the name of the aspect
*/
public AspectMetadata(Class<?> aspectClass, String aspectName) {
this.aspectName = aspectName;
Class<?> currClass = aspectClass;
AjType ajType = null;
while (!currClass.equals(Object.class)) {
AjType ajTypeToCheck = AjTypeSystem.getAjType(currClass);
if (ajTypeToCheck.isAspect()) {
ajType = ajTypeToCheck;
break;
}
currClass = currClass.getSuperclass();
}
if (ajType == null) {
throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect");
}
this.ajType = ajType;
if (this.ajType.getDeclarePrecedence().length > 0) {
throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP");
}
switch (this.ajType.getPerClause().getKind()) {
case SINGLETON :
this.perClausePointcut = Pointcut.TRUE;
return;
case PERTARGET : case PERTHIS :
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setLocation("@Aspect annotation on " + aspectClass.getName());
ajexp.setExpression(findPerClause(aspectClass));
this.perClausePointcut = ajexp;
return;
case PERTYPEWITHIN :
// Works with a type pattern
this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
return;
default :
throw new AopConfigException(
"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
}
}
/**
* Extract contents from String of form {@code pertarget(contents)}.
*/
private String findPerClause(Class<?> aspectClass) {
// TODO when AspectJ provides this, we can remove this hack. Hence we don't
// bother to make it elegant. Or efficient. Or robust :-)
String str = aspectClass.getAnnotation(Aspect.class).value();
str = str.substring(str.indexOf("(") + 1);
str = str.substring(0, str.length() - 1);
return str;
}
/**
* Return AspectJ reflection information.
*/
public AjType getAjType() {
return this.ajType;
}
/**
* Return the aspect class.
*/
public Class getAspectClass() {
return this.ajType.getJavaClass();
}
/**
* Return the aspect class.
*/
public String getAspectName() {
return this.aspectName;
}
/**
* Return a Spring pointcut expression for a singleton aspect.
* (e.g. {@code Pointcut.TRUE} if it's a singleton).
*/
public Pointcut getPerClausePointcut() {
return this.perClausePointcut;
}
/**
* Return whether the aspect is defined as "perthis" or "pertarget".
*/
public boolean isPerThisOrPerTarget() {
PerClauseKind kind = getAjType().getPerClause().getKind();
return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS);
}
/**
* Return whether the aspect is defined as "pertypewithin".
*/
public boolean isPerTypeWithin() {
PerClauseKind kind = getAjType().getPerClause().getKind();
return (kind == PerClauseKind.PERTYPEWITHIN);
}
/**
* Return whether the aspect needs to be lazily instantiated.
*/
public boolean isLazilyInstantiated() {
return (isPerThisOrPerTarget() || isPerTypeWithin());
}
}
@@ -1,127 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.aspectj.annotation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.util.ClassUtils;
/**
* {@link org.springframework.aop.aspectj.AspectInstanceFactory} implementation
* backed by a Spring {@link org.springframework.beans.factory.BeanFactory}.
*
* <p>Note that this may instantiate multiple times if using a prototype,
* which probably won't give the semantics you expect.
* Use a {@link LazySingletonAspectInstanceFactoryDecorator}
* to wrap this to ensure only one new aspect comes back.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.beans.factory.BeanFactory
* @see LazySingletonAspectInstanceFactoryDecorator
*/
public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInstanceFactory {
private final BeanFactory beanFactory;
private final String name;
private final AspectMetadata aspectMetadata;
/**
* Create a BeanFactoryAspectInstanceFactory. AspectJ will be called to
* introspect to create AJType metadata using the type returned for the
* given bean name from the BeanFactory.
* @param beanFactory BeanFactory to obtain instance(s) from
* @param name name of the bean
*/
public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) {
this(beanFactory, name, beanFactory.getType(name));
}
/**
* Create a BeanFactoryAspectInstanceFactory, providing a type that AspectJ should
* introspect to create AJType metadata. Use if the BeanFactory may consider the type
* to be a subclass (as when using CGLIB), and the information should relate to a superclass.
* @param beanFactory BeanFactory to obtain instance(s) from
* @param name the name of the bean
* @param type the type that should be introspected by AspectJ
*/
public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, Class type) {
this.beanFactory = beanFactory;
this.name = name;
this.aspectMetadata = new AspectMetadata(type, name);
}
@Override
public Object getAspectInstance() {
return this.beanFactory.getBean(this.name);
}
@Override
public ClassLoader getAspectClassLoader() {
if (this.beanFactory instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
}
else {
return ClassUtils.getDefaultClassLoader();
}
}
@Override
public AspectMetadata getAspectMetadata() {
return this.aspectMetadata;
}
/**
* Determine the order for this factory's target aspect, either
* an instance-specific order expressed through implementing the
* {@link org.springframework.core.Ordered} interface (only
* checked for singleton beans), or an order expressed through the
* {@link org.springframework.core.annotation.Order} annotation
* at the class level.
* @see org.springframework.core.Ordered
* @see org.springframework.core.annotation.Order
*/
@Override
public int getOrder() {
Class<?> type = this.beanFactory.getType(this.name);
if (type != null) {
if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) {
return ((Ordered) this.beanFactory.getBean(this.name)).getOrder();
}
Order order = AnnotationUtils.findAnnotation(type, Order.class);
if (order != null) {
return order.value();
}
}
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public String toString() {
return getClass().getSimpleName() + ": bean name '" + this.name + "'";
}
}
@@ -1,162 +0,0 @@
/*
* Copyright 2002-2008 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.aop.aspectj.annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.aspectj.lang.reflect.PerClauseKind;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.util.Assert;
/**
* Helper for retrieving @AspectJ beans from a BeanFactory and building
* Spring Advisors based on them, for use with auto-proxying.
*
* @author Juergen Hoeller
* @since 2.0.2
* @see AnnotationAwareAspectJAutoProxyCreator
*/
public class BeanFactoryAspectJAdvisorsBuilder {
private final ListableBeanFactory beanFactory;
private final AspectJAdvisorFactory advisorFactory;
private List<String> aspectBeanNames;
private final Map<String, List<Advisor>> advisorsCache = new HashMap<String, List<Advisor>>();
private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache =
new HashMap<String, MetadataAwareAspectInstanceFactory>();
/**
* Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory.
* @param beanFactory the ListableBeanFactory to scan
*/
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
this(beanFactory, new ReflectiveAspectJAdvisorFactory());
}
/**
* Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory.
* @param beanFactory the ListableBeanFactory to scan
* @param advisorFactory the AspectJAdvisorFactory to build each Advisor with
*/
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
this.beanFactory = beanFactory;
this.advisorFactory = advisorFactory;
}
/**
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this
// case they would be cached by the Spring container but would not
// have been weaved
Class beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.EMPTY_LIST;
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
/**
* Return whether the aspect bean with the given name is eligible.
* @param beanName the name of the aspect bean
* @return whether the bean is eligible
*/
protected boolean isEligibleBean(String beanName) {
return true;
}
}
@@ -1,269 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.aspectj.lang.reflect.PerClauseKind;
import org.springframework.aop.Pointcut;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJPrecedenceInformation;
import org.springframework.aop.aspectj.InstantiationModelAwarePointcutAdvisor;
import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotation;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
import org.springframework.aop.support.Pointcuts;
/**
* Internal implementation of AspectJPointcutAdvisor.
* Note that there will be one instance of this advisor for each target method.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
class InstantiationModelAwarePointcutAdvisorImpl
implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation {
private final AspectJExpressionPointcut declaredPointcut;
private Pointcut pointcut;
private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;
private final Method method;
private final boolean lazy;
private final AspectJAdvisorFactory atAspectJAdvisorFactory;
private Advice instantiatedAdvice;
private int declarationOrder;
private String aspectName;
private Boolean isBeforeAdvice;
private Boolean isAfterAdvice;
public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {
this.declaredPointcut = ajexp;
this.method = method;
this.atAspectJAdvisorFactory = af;
this.aspectInstanceFactory = aif;
this.declarationOrder = declarationOrderInAspect;
this.aspectName = aspectName;
if (aif.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut =
Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
this.lazy = true;
}
else {
// A singleton aspect.
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
this.pointcut = declaredPointcut;
this.lazy = false;
}
}
/**
* The pointcut for Spring AOP to use. Actual behaviour of the pointcut will change
* depending on the state of the advice.
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
/**
* This is only of interest for Spring AOP: AspectJ instantiation semantics
* are much richer. In AspectJ terminology, all a return of {@code true}
* means here is that the aspect is not a SINGLETON.
*/
@Override
public boolean isPerInstance() {
return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
}
/**
* Return the AspectJ AspectMetadata for this advisor.
*/
public AspectMetadata getAspectMetadata() {
return this.aspectInstanceFactory.getAspectMetadata();
}
/**
* Lazily instantiate advice if necessary.
*/
@Override
public synchronized Advice getAdvice() {
if (this.instantiatedAdvice == null) {
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
return this.instantiatedAdvice;
}
@Override
public boolean isLazy() {
return this.lazy;
}
@Override
public synchronized boolean isAdviceInstantiated() {
return (this.instantiatedAdvice != null);
}
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
return this.atAspectJAdvisorFactory.getAdvice(
this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}
public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() {
return this.aspectInstanceFactory;
}
public AspectJExpressionPointcut getDeclaredPointcut() {
return this.declaredPointcut;
}
@Override
public int getOrder() {
return this.aspectInstanceFactory.getOrder();
}
@Override
public String getAspectName() {
return this.aspectName;
}
@Override
public int getDeclarationOrder() {
return this.declarationOrder;
}
@Override
public boolean isBeforeAdvice() {
if (this.isBeforeAdvice == null) {
determineAdviceType();
}
return this.isBeforeAdvice;
}
@Override
public boolean isAfterAdvice() {
if (this.isAfterAdvice == null) {
determineAdviceType();
}
return this.isAfterAdvice;
}
/**
* Duplicates some logic from getAdvice, but importantly does not force
* creation of the advice.
*/
private void determineAdviceType() {
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.method);
if (aspectJAnnotation == null) {
this.isBeforeAdvice = false;
this.isAfterAdvice = false;
}
else {
switch (aspectJAnnotation.getAnnotationType()) {
case AtAfter:
case AtAfterReturning:
case AtAfterThrowing:
this.isAfterAdvice = true;
this.isBeforeAdvice = false;
break;
case AtAround:
case AtPointcut:
this.isAfterAdvice = false;
this.isBeforeAdvice = false;
break;
case AtBefore:
this.isAfterAdvice = false;
this.isBeforeAdvice = true;
}
}
}
@Override
public String toString() {
return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() +
"]; advice method [" + this.method + "]; perClauseKind=" +
this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind();
}
/**
* Pointcut implementation that changes its behaviour when the advice is instantiated.
* Note that this is a <i>dynamic</i> pointcut. Otherwise it might
* be optimized out if it does not at first match statically.
*/
private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPointcut {
private final AspectJExpressionPointcut declaredPointcut;
private final Pointcut preInstantiationPointcut;
private LazySingletonAspectInstanceFactoryDecorator aspectInstanceFactory;
private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut,
Pointcut preInstantiationPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
this.declaredPointcut = declaredPointcut;
this.preInstantiationPointcut = preInstantiationPointcut;
if (aspectInstanceFactory instanceof LazySingletonAspectInstanceFactoryDecorator) {
this.aspectInstanceFactory = (LazySingletonAspectInstanceFactoryDecorator) aspectInstanceFactory;
}
}
@Override
public boolean matches(Method method, Class targetClass) {
// We're either instantiated and matching on declared pointcut, or uninstantiated matching on either pointcut
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)) ||
this.preInstantiationPointcut.getMethodMatcher().matches(method, targetClass);
}
@Override
public boolean matches(Method method, Class targetClass, Object[] args) {
// This can match only on declared pointcut.
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
}
private boolean isAspectMaterialized() {
return (this.aspectInstanceFactory == null || this.aspectInstanceFactory.isMaterialized());
}
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import org.springframework.util.Assert;
/**
* Decorator to cause a {@link MetadataAwareAspectInstanceFactory} to instantiate only once.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
public class LazySingletonAspectInstanceFactoryDecorator implements MetadataAwareAspectInstanceFactory {
private final MetadataAwareAspectInstanceFactory maaif;
private volatile Object materialized;
/**
* Create a new lazily initializing decorator for the given AspectInstanceFactory.
* @param maaif the MetadataAwareAspectInstanceFactory to decorate
*/
public LazySingletonAspectInstanceFactoryDecorator(MetadataAwareAspectInstanceFactory maaif) {
Assert.notNull(maaif, "AspectInstanceFactory must not be null");
this.maaif = maaif;
}
@Override
public synchronized Object getAspectInstance() {
if (this.materialized == null) {
synchronized (this) {
if (this.materialized == null) {
this.materialized = this.maaif.getAspectInstance();
}
}
}
return this.materialized;
}
public boolean isMaterialized() {
return (this.materialized != null);
}
@Override
public ClassLoader getAspectClassLoader() {
return this.maaif.getAspectClassLoader();
}
@Override
public AspectMetadata getAspectMetadata() {
return this.maaif.getAspectMetadata();
}
@Override
public int getOrder() {
return this.maaif.getOrder();
}
@Override
public String toString() {
return "LazySingletonAspectInstanceFactoryDecorator: decorating " + this.maaif;
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2007 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.aop.aspectj.annotation;
import org.springframework.aop.aspectj.AspectInstanceFactory;
/**
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
*
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
* we need to split out this subinterface.
*
* @author Rod Johnson
* @since 2.0
* @see AspectMetadata
* @see org.aspectj.lang.reflect.AjType
*/
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
/**
* Return the AspectJ AspectMetadata for this factory's aspect.
* @return the aspect metadata
*/
AspectMetadata getAspectMetadata();
}
@@ -1,51 +0,0 @@
/*
* Copyright 2002-2012 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.aop.aspectj.annotation;
import org.springframework.aop.framework.AopConfigException;
/**
* Extension of AopConfigException thrown when trying to perform
* an advisor generation operation on a class that is not an
* AspectJ annotation-style aspect.
*
* @author Rod Johnson
* @since 2.0
*/
@SuppressWarnings("serial")
public class NotAnAtAspectException extends AopConfigException {
private Class<?> nonAspectClass;
/**
* Create a new NotAnAtAspectException for the given class.
* @param nonAspectClass the offending class
*/
public NotAnAtAspectException(Class<?> nonAspectClass) {
super(nonAspectClass.getName() + " is not an @AspectJ aspect");
this.nonAspectClass = nonAspectClass;
}
/**
* Returns the offending class.
*/
public Class<?> getNonAspectClass() {
return this.nonAspectClass;
}
}
@@ -1,52 +0,0 @@
/*
* Copyright 2002-2007 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.aop.aspectj.annotation;
import org.springframework.beans.factory.BeanFactory;
/**
* AspectInstanceFactory backed by a BeanFactory-provided prototype,
* enforcing prototype semantics.
*
* <p>Note that this may instantiate multiple times, which probably won't give the
* semantics you expect. Use a {@link LazySingletonAspectInstanceFactoryDecorator}
* to wrap this to ensure only one new aspect comes back.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.beans.factory.BeanFactory
* @see LazySingletonAspectInstanceFactoryDecorator
*/
public class PrototypeAspectInstanceFactory extends BeanFactoryAspectInstanceFactory {
/**
* Create a PrototypeAspectInstanceFactory. AspectJ will be called to
* introspect to create AJType metadata using the type returned for the
* given bean name from the BeanFactory.
* @param beanFactory the BeanFactory to obtain instance(s) from
* @param name the name of the bean
*/
public PrototypeAspectInstanceFactory(BeanFactory beanFactory, String name) {
super(beanFactory, name);
if (!beanFactory.isPrototype(name)) {
throw new IllegalArgumentException(
"Cannot use PrototypeAspectInstanceFactory with bean named '" + name + "': not a prototype");
}
}
}

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