Skip to content

Commit

Permalink
First drop
Browse files Browse the repository at this point in the history
  • Loading branch information
aclement committed Dec 7, 2012
1 parent deff1cc commit 1bf580f
Show file tree
Hide file tree
Showing 996 changed files with 61,618 additions and 3 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
target/
bin/
subbin/
builds/
superbin/
springloaded-*.jar
.DS_Store
88 changes: 85 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,86 @@
spring-loaded
=============
# Welcome to Spring-Loaded

Java agent that enables class reloading in a running JVM
## What is Spring Loaded?

Spring Loaded is a JVM agent for reloading class file changes whilst a JVM is running. It transforms
classes at loadtime to make them amenable to later reloading. Unlike 'hot code replace' which only allows
simple changes once a JVM is running (e.g. changes to method bodies), Spring Loaded allows you
to add/modify/delete methods/fields/constructors. The annotations on types/methods/fields/constructors
can also be modified and it is possible to add/remove/change values in enum types.

Spring Loaded is usable on any bytecode that may run on a JVM, and is actually the reloading system
used in Grails 2.

# Installation

A dev build of version 1.1.1 is available [here](https://github.com/downloads/SpringSource/spring-loaded/springloaded-1.1.1-dev.jar). The download is the agent jar and needs no further unpacking before use.

# Running with reloading

java -javaagent:<pathTo>/springloaded-{VERSION}.jar -noverify SomeJavaClass

The verifier is being turned off because some of the bytecode rewriting stretches the meaning of
some of the bytecodes - in ways the JVM doesn't mind but the verifier doesn't like. Once up and
running what effectively happens is that any classes loaded from jar files (dependencies) are not
treated as reloadable, whilst anything loaded from .class files on disk is made reloadable. Once
loaded the .class file will be watched (once a second) and should a new version appear
SpringLoaded will pick it up. Any live instances of that class will immediately see the new form
of the object, the instances do not need to be discarded and recreated.

No doubt that raises a lot of questions and hopefully a proper FAQ will appear here shortly! But in
the meantime, here are some basic Qs and As:

Q. Does it reload anything that might change in a class file?
A. No, you can't change the hierarchy of a type. Also there are certain constructor patterns of
usage it can't actually handle right now.

Q. With objects changing shape, what happens with respect to reflection?
A. Reflection results change over time as the objects are reloaded. For example, modifying a class
with a new method and calling getDeclaredMethods() after reloading has occurred will mean you see
the new method in the results. *But* this does mean if you have existing caches in your system
that stash reflective information assuming it never changes, those will need to be cleared
after a reload.

Q. How do I know when a reload has occurred so I can clear my state?
A. You can write a plugin that is called when reloads occur and you can then take the appropriate
action. Create an implementation of `ReloadEventProcessorPlugin` and then register it via
`SpringLoadedPreProcessor.registerGlobalPlugin(plugin)`. (There are other ways to register plugins,
which will hopefully get some documentation!)

Q. What's the state of the codebase?
A. The technology is successfully being used by Grails for reloading. It does need some performance
work and a few smacks with a refactoring hammer. It needs upgrading here and there to tolerate
the invokedynamic instruction and associated new constant pool entries that arrived in Java 7.
It could also use a proper (gradle probably) build process.

# Working with the code

git clone https://github.com/SpringSource/spring-loaded

Once cloned there will be three projects suitable for import into eclipse. The main project and
two containing testdata. One of the test projects is an AspectJ project (containing both Java
and AspectJ code), the other test project is a Groovy project. To compile these test projects
in Eclipse you will need the relevant eclipse plugins:

AJDT: update site: `http://download.eclipse.org/tools/ajdt/42/dev/update`
Groovy-Eclipse: update site: `http://dist.springsource.org/snapshot/GRECLIPSE/e4.2/`

After importing them you can run the tests. There are two kinds of tests, hand crafted and
generated. Running all the tests including the generated ones can take a while.
To run just the hand crafted ones change `GlobalConfiguration.generatedTestsOn=false`. To run the
tests create a Run Configuration of type JUnit for the project org.springsource.loaded and on the
arguments tab set `VM arguments` to `-noverify` - then click run.

To build a new version of the agent jar, just run `ant` in the org.springsource.loaded project,
this will create a new springloaded-dev.jar in the builds folder using the compiled eclipse
output. Note: the jarjar task is used to repackage asm with a prefix (to avoid clashes) -
so you will need to install the jarjar task (from the project lib folder) into your ant lib folder.

# Can I contribute?

Sure! Just press *Fork* at the top of this github page and get coding. Before we accept pull
requests we just need you to sign a simple contributor's agreement - which you can find
[here](https://support.springsource.com/spring_committer_signup). Signing the contributor's
agreement does not grant anyone commit rights to the main repository, but it does mean that we
can accept your contributions, and you will get an author credit if we do. Active contributors
might be asked to join the core team, and given the ability to merge pull requests.
9 changes: 9 additions & 0 deletions org.springsource.loaded.testdata.groovy/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry exported="true" kind="con" path="GROOVY_SUPPORT"/>
<classpathentry kind="lib" path="groovy-1.8.2.jar" sourcepath="groovy-src-1.7.8.zip"/>
<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
<classpathentry kind="output" path="bin"/>
</classpath>
18 changes: 18 additions & 0 deletions org.springsource.loaded.testdata.groovy/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.springsource.loaded.testdata.groovy</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#Fri Dec 03 07:39:45 PST 2010
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
groovy.compiler.level=20
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#Tue Dec 07 13:19:07 PST 2010
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=false
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=false
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_missing_override_annotations_interface_methods=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=true
sp_cleanup.correct_indentation=true
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=false
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=false
sp_cleanup.remove_unused_imports=true
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.use_blocks=true
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
Loading

0 comments on commit 1bf580f

Please sign in to comment.