diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..72bdef3 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,53 @@ +jobs: + build: + docker: + - image: sillelien/circleci-dollar-build-image + + environment: + MAJOR_VERSION: 0.9 + NEXT_MAJOR_VERSION: 0.10 + + MAVEN_OPTS: "-Xmx1g -DbuildNumber=${CIRCLE_BUILD_NUM}" + TZ: "/usr/share/zoneinfo/Europe/London" + + working_directory: ~/java-as-script + + branches: + ignore: + - gh-pages + + steps: + + - checkout + + - restore_cache: + key: dependency-cache + + - run: + name: init + command: /home/circleci/.build-init.sh + + - run: + name: dependencies + command: | + ~/build-utils/maven_make_deps.sh + + - save_cache: + key: dependency-cache + paths: + - ~/.gnupg + - ~/.m2 + - run: + name: test + command: | + mvn -q -T 1C -Drat.skip -Dsource.skip=true -DgenerateReports=false -Dmaven.javadoc.skip=true integration-test + no_output_timeout: 600 + + - deploy: + command: | + if [ "${CIRCLE_BRANCH}" == "staging" ]; then + ~/build-utils/promote_from_staging.sh + fi + if [ "${CIRCLE_BRANCH}" == "master" ]; then + ~/build-utils/maven_deploy.sh + fi diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..986d0b0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=true +indent_style=space +indent_size=4 + +[{*.yml,*.yaml}] +indent_style=space +indent_size=2 + diff --git a/.gitignore b/.gitignore index c245488..7aa3ccc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ /relproxy_dist_tmp/ /build/ +/tmp/ +.idea/workspace.xml +out/ +target/ diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..e2957a4 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..2edacd3 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/hotswap_agent.xml b/.idea/hotswap_agent.xml new file mode 100644 index 0000000..8a869d9 --- /dev/null +++ b/.idea/hotswap_agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..0a5158f --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/libraries/ItsNat_1_3_1.xml b/.idea/libraries/ItsNat_1_3_1.xml new file mode 100644 index 0000000..06d4963 --- /dev/null +++ b/.idea/libraries/ItsNat_1_3_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__junit_junit_4_12.xml b/.idea/libraries/Maven__junit_junit_4_12.xml new file mode 100644 index 0000000..d411041 --- /dev/null +++ b/.idea/libraries/Maven__junit_junit_4_12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 0000000..f58bbc1 --- /dev/null +++ b/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_jetbrains_annotations_15_0.xml b/.idea/libraries/Maven__org_jetbrains_annotations_15_0.xml new file mode 100644 index 0000000..f7a5c64 --- /dev/null +++ b/.idea/libraries/Maven__org_jetbrains_annotations_15_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/annotations.xml b/.idea/libraries/annotations.xml new file mode 100644 index 0000000..03e4ad1 --- /dev/null +++ b/.idea/libraries/annotations.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/maven_ant_tasks_2_1_3.xml b/.idea/libraries/maven_ant_tasks_2_1_3.xml new file mode 100644 index 0000000..c5e5cdf --- /dev/null +++ b/.idea/libraries/maven_ant_tasks_2_1_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a6a1320 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + 1.8 + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c13ca56 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml new file mode 100644 index 0000000..6001478 --- /dev/null +++ b/.mvn/extensions.xml @@ -0,0 +1,8 @@ + + + org.springframework.build + aws-maven + 5.0.0.RELEASE + + diff --git a/.release b/.release new file mode 100644 index 0000000..bf69677 --- /dev/null +++ b/.release @@ -0,0 +1 @@ +0.9.167 diff --git a/.release.details b/.release.details new file mode 100644 index 0000000..a690306 --- /dev/null +++ b/.release.details @@ -0,0 +1 @@ +0.9.167 167 dbe48a61c158379efb5aab38f18ddc2b8a5f9e27 good-equerry-from-coalburns diff --git a/relproxy/LICENSE-2.0.txt b/LICENSE similarity index 100% rename from relproxy/LICENSE-2.0.txt rename to LICENSE diff --git a/README.md b/README.md index 07a9119..0e8c646 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,106 @@ -RelProxy -======== -RelProxy is a simple Java and Groovy hot class reloader for Java and Groovy providing transparent compilation and class reload on the fly, and scripting support and shell of pure Java code. +Build: [![Circle CI](https://circleci.com/gh/sillelien/java-as-script.png?style=badge)](https://circleci.com/gh/sillelien/java-as-script) -News ------- +[ ![Download](https://api.bintray.com/packages/sillelien/maven/java-as-script/images/download.svg) ](https://bintray.com/sillelien/maven/java-as-script/_latestVersion) -- 2016-3-16 v0.8.8 Released. Support of static final fields in reloadable proxies. [Release Notes](https://github.com/jmarranz/relproxy/blob/master/relproxy/CHANGES.txt) -- 2015-10-6 DZone article: [Improve Productivity With RelProxy for Java](https://dzone.com/articles/embedding-relproxy-in-your-java-framework-to-provi). About embedding RelProxy in your own Java framework. -- 2015-10-1 v0.8.7 Released. Some improvement needed when embedding RelProxy Java using the scripting API. [Release Notes](https://github.com/jmarranz/relproxy/blob/master/relproxy/CHANGES.txt) -- 2015-6-12 v0.8.6 Released. Support of class reloading of inner classes including anonymous. [Release Notes](https://github.com/jmarranz/relproxy/blob/master/relproxy/CHANGES.txt) -- 2015-6-12 v0.8.5 Released. Improved performance when checking source changes and no change is detected. [Release Notes](https://github.com/jmarranz/relproxy/blob/master/relproxy/CHANGES.txt) -- 2015-2-17 v0.8.4 Released. Added workaround to support Liferay (6.2 tested) [Release Notes](https://github.com/jmarranz/relproxy/blob/master/relproxy/CHANGES.txt). - New [Liferay 6.2 example](https://github.com/jmarranz/relproxy_examples/tree/master/relproxy_ex_liferay-portlet) using RelProxy. -- 2015-1-23 v0.8.3 Released. First release in JCenter and Maven Central [Release Notes](https://github.com/jmarranz/relproxy/blob/master/relproxy/CHANGES.txt) -- 2015-2-5 [Java Hot Class Reloading with RelProxy in Development Mode, a GWT Example](http://java.dzone.com/articles/java-hot-class-reloading) JavaLobby article -- 2015-1-30 [Slides](http://www.slideshare.net/jmarranz/relproxy-class-reload-and-easy-java-scripting) in English and [video](http://autentia.com/2015/01/30/relproxy-easy-class-reload-and-scripting-with-java-por-jose-maria-arranz/#) in Spanish of the RelProxy presentation in MadridJUG on January 28! +Java-as-Script [![Alpha](https://img.shields.io/badge/Status-Alpha-yellowgreen.svg?style=flat)](http://github.com/sillelien/java-as-script) +============== +Java-as-Script provides a hot reloading JSR-223 implementation for Java. This version is a fork of [the original project](https://github.com/jmarranz/relproxy) specifically it has been reduced in scope to focus entirely on the JSR-223 aspect of the original project. It is also primarily been forked for use in the [Dollar project](https://github.com/sillelien/dollar). +Make sure you have the JCenter repo in your pom.xml -Download Binaries and Docs ------- - -[Download](https://sourceforge.net/projects/relproxy/files/) - -Distribution file includes binaries, examples, manual and javadocs. - -Artefacts are uploaded to [JCenter](https://bintray.com/jmarranz/maven/relproxy/view) ([direct](https://jcenter.bintray.com/com/innowhere/relproxy/)) and [Maven Central](http://mvnrepository.com/artifact/com.innowhere/relproxy) ([direct](https://oss.sonatype.org/content/repositories/releases/com/innowhere/relproxy/)) repositories - -Maven: +```xml + + + + false + + central + bintray + http://jcenter.bintray.com + + +``` + + Then just add the following dependency ```xml -com.innowhere -relproxy -(version) -jar + + com.sillelien + java-as-script + 0.9.135 + ``` -Overview ------- - -RelProxy is a simple Java and Groovy hot class reloader for Java and Groovy providing transparent compilation and class reload on the fly, and scripting support and shell of pure -Java code. - -RelProxy is: - -1) A simple Java and Groovy hot class reloader for Java and Groovy providing transparent compilation and class reload on the fly with no need of a bootstrap class loader agent and -avoiding context reloading. Reloading happens only in memory. Class reloading can be used in development phase and optionally in production (if source code can be uploaded to -production). - -2) A scripting environment to execute Java code snippets the same as a shell script. There is no new language, is Java compiled on the fly, code in the initial archive can call -to other normal Java files. Optionally .class can be saved in a cache to provide the fastest "scripting" language of the world. - -3) Execution of Java code snippets in command line (no need of packaging into an archive). - -4) A simple shell to code, edit and execute code snippets in Java interactively. - -5) JSR 223 Scripting API implementation for "Java" as the target scripting language. You can embed and execute Java code as scripting into your Java program. +[ ![Download](https://api.bintray.com/packages/sillelien/maven/java-as-script/images/download.svg) ](https://bintray.com/sillelien/maven/java-as-script/_latestVersion) +Below is a complete example of using Java-as-Script as a JSR-223 scripting engine, with the language being Java. +```java +package com.sillelien.jas; -In case of Java "scripting", there is no a new language, is pure Java code with compilation on the fly. +import com.sillelien.jas.jproxy.JProxy; +import com.sillelien.jas.jproxy.JProxyConfig; +import com.sillelien.jas.jproxy.JProxyScriptEngine; +import com.sillelien.jas.jproxy.JProxyScriptEngineFactory; -In spite of RelProxy is a general purpose tool it was conceived for [ItsNat web framework](http://www.itsnat.org) to provide hot class reload in development time... -and if you want also in production. RelProxy is standalone and has no dependency on ItsNat. +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.util.Collections; +import java.util.List; -RelProxy Manual explains how to configure and use RelProxy in NetBeans and Eclipse avoiding class reloading (of course other IDEs are possible). +public class Demo { -There are several examples of how to use RelProxy with most popular Java web frameworks ready to run into [RelProxy Examples](https://github.com/jmarranz/relproxy_examples) -repository. Also [ItsNat Experiments](https://github.com/jmarranz/itsnat/tree/master/inexperiments) includes an ItsNat example using NetBeans with Ant, to reload Java and Groovy code. -The most complex example is [relproxy_test_itsnat](https://github.com/jmarranz/relproxy/tree/master/relproxy_test_itsnat) created for internal testing. + public static void main(String[] ignored) throws Exception { -Besides fast and custom Java (and Groovy) class reloading, RelProxy provides a Java shell scripting environment to execute: + //Initializing and configuring the JSR-223 script engine + JProxyConfig jpConfig = JProxy.createJProxyConfig(); + jpConfig.setEnabled(true) + .setRelProxyOnReloadListener((objOld, objNew, proxy, method, args) -> { + //TODO + }) + // .setInputPath(".") + .setScanPeriod(-1) + .setClassFolder("./tmp/classes") + .setCompilationOptions(Collections.emptyList()) + .setJProxyDiagnosticsListener(diagnostics -> { + List> diagnosticList = diagnostics.getDiagnostics(); + diagnosticList.stream() + .filter(diagnostic -> diagnostic.getKind().equals(Diagnostic.Kind.ERROR)) + .forEach(System.err::println); + }); -1) A pure Java archive packaged like a shell script file with no need of previous compilation, compilation is done on the fly and optionally .class can be saved in a -cache to provide the fastest "scripting" language of the world. Code in the initial archive can call to other normal Java files, again with compilation on the fly -and optional compilation caching as .class files. [Example 1](https://github.com/jmarranz/relproxy/blob/master/relproxy/src/test/resources/example_java_shell) -and [example 2](https://github.com/jmarranz/relproxy/blob/master/relproxy/src/test/resources/example_java_shell_complete_class) or just -a conventional [Java source file](https://github.com/jmarranz/relproxy/blob/master/relproxy/src/test/resources/example_normal_class.java) (yes you can execute a conventional -JavaSE application from source code). + JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); -2) Execution of Java code snippets in command line (no need of packaging in an archive). [Example](https://github.com/jmarranz/relproxy/blob/master/relproxy/test_cmd/test_java_shell_snippet_launcher.sh). + ScriptEngineManager manager = new ScriptEngineManager(); + manager.registerEngineName("java", factory); + Bindings bindings = manager.getBindings(); + bindings.put("in", "World"); -3) Interactive console to edit and execute Java code (ever compilation on the fly). [Example of launcher](https://github.com/jmarranz/relproxy/blob/master/relproxy/test_cmd/test_java_shell_interactive_launcher.sh) + ScriptEngine engine = manager.getEngineByName("java"); + JProxyScriptEngine scriptEngine = (JProxyScriptEngine) engine; + scriptEngine.init(jpConfig); -Finally RelProxy implements the official JSR-223 [Java Scripting API](http://docs.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html) as found in Java 1.6 for "Java" language. -By using this API you can embed Java as any other scripting language in your Java code. + //Your code goes here, e.g. -Yes, Java is also a dynamic scripting language with no need of manual compilation, and it is extremely fast and robust. + scriptEngine.eval("System.out.println(\"Hello \"+context.getAttribute(\"in\",javax.script.ScriptContext.ENGINE_SCOPE));return null;\n",bindings); -Online Docs Last Version ------- + } +} -[Manual](http://relproxy.sourceforge.net/docs/manual/manual.html) - -[JavaDocs](http://relproxy.sourceforge.net/docs/javadoc/) - -Examples ------- - -See the GitHub repository [RelProxy Examples](https://github.com/jmarranz/relproxy_examples) - -Questions and discussions ------- - -There is a [Google Group](https://groups.google.com/forum/#!forum/relproxy) for RelProxy. - -Bug Reporting ------- - -Use this GitHub project. - - -Articles/Blogs/Presentations ------- - -- Oct 6,2015 [Improve Productivity With RelProxy for Java](https://dzone.com/articles/embedding-relproxy-in-your-java-framework-to-provi). About embedding RelProxy in your own Java framework. - -- Feb 19,2015 [No longer virgin, uploaded my first jar to Maven Central, and it was not nice](http://jmarranz.blogspot.com.es/2015/02/no-longer-virgin-uploaded-my-first-jar.html). [JavaLobby](http://java.dzone.com/articles/no-longer-virgin-uploaded-my). [Bintray Quote](http://blog.bintray.com/2015/02/19/another-one-bites-the-maven-central-dust-and-saved-by-bintray/). +``` -- Feb 5,2015 [Java Hot Class Reloading with RelProxy in Development Mode, a GWT Example](http://java.dzone.com/articles/java-hot-class-reloading) +Dependencies: [![Dependency Status](https://www.versioneye.com/user/projects/5960064c6725bd0049735d0b/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/5960064c6725bd0049735d0b) -- Jan 28,2015 [Slides](http://www.slideshare.net/jmarranz/relproxy-class-reload-and-easy-java-scripting) in English and [video](http://autentia.com/2015/01/30/relproxy-easy-class-reload-and-scripting-with-java-por-jose-maria-arranz/#) ([youtube](https://www.youtube.com/watch?v=dyUhX6t5t-Y)) in Spanish of the RelProxy presentation in MadridJUG +------- -- Dec 31,2014 [RelProxy v0.8.1 reduce el numero de redeploys en GWT-RPC y otros Java web frameworks](http://www.javahispano.org/portada/2014/12/31/relproxy-v081-reduce-el-numero-de-redeploys-en-gwt-rpc-y-otr.html) Published at javaHispano +** If you use this project please consider giving us a star on [GitHub](http://github.com/sillelien/java-as-script). ** -- Feb 15,2014 [v0.8 announce at JavaLobby](http://java.dzone.com/articles/presenting-relproxy-hot-class) +Please contact me through Gitter (chat) or through GitHub Issues. -- Feb 12,2014 [v0.8 announce at javaHispano](http://www.javahispano.org/portada/2014/2/12/publicado-relproxy-v08-hot-class-reloader-y-scripting-para-j.html) +[![GitHub Issues](https://img.shields.io/github/issues/sillelien/java-as-script.svg)](https://github.com/sillelien/java-as-script/issues) [![Join the chat at https://gitter.im/sillelien/java-as-script](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sillelien/java-as-script?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +For commercial support please contact me directly. +------- diff --git a/README.staged.md b/README.staged.md new file mode 100644 index 0000000..cbc0d85 --- /dev/null +++ b/README.staged.md @@ -0,0 +1,106 @@ +Build: [![Circle CI](https://circleci.com/gh/sillelien/java-as-script.png?style=badge)](https://circleci.com/gh/sillelien/java-as-script) + +[ ![Download](https://api.bintray.com/packages/sillelien/maven/java-as-script/images/download.svg) ](https://bintray.com/sillelien/maven/java-as-script/_latestVersion) + +Java-as-Script [![Alpha](https://img.shields.io/badge/Status-Alpha-yellowgreen.svg?style=flat)](http://github.com/sillelien/java-as-script) +============== +Java-as-Script provides a hot reloading JSR-223 implementation for Java. This version is a fork of [the original project](https://github.com/jmarranz/relproxy) specifically it has been reduced in scope to focus entirely on the JSR-223 aspect of the original project. It is also primarily been forked for use in the [Dollar project](https://github.com/sillelien/dollar). + +Make sure you have the JCenter repo in your pom.xml + +```xml + + + + false + + central + bintray + http://jcenter.bintray.com + + +``` + + Then just add the following dependency + +```xml + + com.sillelien + java-as-script + 0.9.142 + +``` + +[ ![Download](https://api.bintray.com/packages/sillelien/maven/java-as-script/images/download.svg) ](https://bintray.com/sillelien/maven/java-as-script/_latestVersion) + +Below is a complete example of using Java-as-Script as a JSR-223 scripting engine, with the language being Java. + +```java +package com.sillelien.jas; + +import com.sillelien.jas.jproxy.JProxy; +import com.sillelien.jas.jproxy.JProxyConfig; +import com.sillelien.jas.jproxy.JProxyScriptEngine; +import com.sillelien.jas.jproxy.JProxyScriptEngineFactory; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.util.Collections; +import java.util.List; + +public class Demo { + + public static void main(String[] ignored) throws Exception { + + //Initializing and configuring the JSR-223 script engine + JProxyConfig jpConfig = JProxy.createJProxyConfig(); + jpConfig.setEnabled(true) + .setRelProxyOnReloadListener((objOld, objNew, proxy, method, args) -> { + //TODO + }) + // .setInputPath(".") + .setScanPeriod(-1) + .setClassFolder("./tmp/classes") + .setCompilationOptions(Collections.emptyList()) + .setJProxyDiagnosticsListener(diagnostics -> { + List> diagnosticList = diagnostics.getDiagnostics(); + diagnosticList.stream() + .filter(diagnostic -> diagnostic.getKind().equals(Diagnostic.Kind.ERROR)) + .forEach(System.err::println); + }); + + JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); + + ScriptEngineManager manager = new ScriptEngineManager(); + manager.registerEngineName("java", factory); + Bindings bindings = manager.getBindings(); + bindings.put("in", "World"); + + ScriptEngine engine = manager.getEngineByName("java"); + JProxyScriptEngine scriptEngine = (JProxyScriptEngine) engine; + scriptEngine.init(jpConfig); + + //Your code goes here, e.g. + + scriptEngine.eval("System.out.println(\"Hello \"+context.getAttribute(\"in\",javax.script.ScriptContext.ENGINE_SCOPE));return null;\n",bindings); + + } +} + +``` + +Dependencies: [![Dependency Status](https://www.versioneye.com/user/projects/5960064c6725bd0049735d0b/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/5960064c6725bd0049735d0b) + +------- + +** If you use this project please consider giving us a star on [GitHub](http://github.com/sillelien/java-as-script). ** + +Please contact me through Gitter (chat) or through GitHub Issues. + +[![GitHub Issues](https://img.shields.io/github/issues/sillelien/java-as-script.svg)](https://github.com/sillelien/java-as-script/issues) [![Join the chat at https://gitter.im/sillelien/java-as-script](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sillelien/java-as-script?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +For commercial support please contact me directly. +------- diff --git a/annotations/java/io/annotations.xml b/annotations/java/io/annotations.xml new file mode 100644 index 0000000..e658d6e --- /dev/null +++ b/annotations/java/io/annotations.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/annotations/java/lang/annotations.xml b/annotations/java/lang/annotations.xml new file mode 100644 index 0000000..28cc73d --- /dev/null +++ b/annotations/java/lang/annotations.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/annotations/java/lang/reflect/annotations.xml b/annotations/java/lang/reflect/annotations.xml new file mode 100644 index 0000000..7aae6df --- /dev/null +++ b/annotations/java/lang/reflect/annotations.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/annotations/java/net/annotations.xml b/annotations/java/net/annotations.xml new file mode 100644 index 0000000..669663a --- /dev/null +++ b/annotations/java/net/annotations.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/annotations/java/util/annotations.xml b/annotations/java/util/annotations.xml new file mode 100644 index 0000000..4df6ebd --- /dev/null +++ b/annotations/java/util/annotations.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/annotations/java/util/jar/annotations.xml b/annotations/java/util/jar/annotations.xml new file mode 100644 index 0000000..e9bba3d --- /dev/null +++ b/annotations/java/util/jar/annotations.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/annotations/javax/script/annotations.xml b/annotations/javax/script/annotations.xml new file mode 100644 index 0000000..786bc4d --- /dev/null +++ b/annotations/javax/script/annotations.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/annotations/javax/tools/annotations.xml b/annotations/javax/tools/annotations.xml new file mode 100644 index 0000000..0f5f810 --- /dev/null +++ b/annotations/javax/tools/annotations.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/java-as-script.iml b/java-as-script.iml new file mode 100644 index 0000000..860b236 --- /dev/null +++ b/java-as-script.iml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0f504ca --- /dev/null +++ b/pom.xml @@ -0,0 +1,575 @@ + + + 4.0.0 + + com.sillelien + java-as-script + 0 + + Java-as-Script + + Simple Java and Groovy hot class reloader,and Java based shell and scripting used by Dollar project, + original project is https://github.com/jmarranz/relproxy + + + + + 2.0.2 + 1.8 + 3.3.9 + + + + ${maven.version} + + + + https://github.com/sillelien/java-as-script + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Jose Maria Arranz + jmarranz@innowhere.com + Innowhere Software + http://www.innowhere.com + + Original Author + + + + + + + Daniel López (greeneyed) + + + https://github.com/Verdoso + + Original Contributor + + + + Michael Vorburger + mike@vorburger.ch + Vorburger.CH + http://www.vorburger.ch + + Original Contributor + + + + + + scm:git:git@github.com:sillelien/dollar-java-as-script.git + scm:git:git@github.com:sillelien/dollar-java-as-script.git + git@github.com:sillelien/dollar-java-as-script.git + + + + + + + + org.jetbrains + annotations + 15.0 + + + org.slf4j + slf4j-api + 1.7.22 + + + junit + junit + 4.12 + test + + + + + + + + + + org.apache.rat + apache-rat-plugin + 0.12 + + true + true + + src/test/** + src/main/webapp/** + dist/** + dist-skel/** + **/*.markdown + **/*.ds + **/*.md + **/*.sh + **/*.yml + **/*.iml + **/*.html + key.txt + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.4 + + Default + High + true + + target/site + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.9 + + + com.google.code.findbugs + bcel + ${bcel.version} + + + org.apache.maven.shared + maven-shared-jar + 1.2 + + + org.apache.bcel + bcel + + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.6 + + + attach-descriptor + + attach-descriptor + + + + + en + + + + org.apache.maven.doxia + doxia-module-markdown + 1.7 + + + org.apache.maven.skins + maven-fluido-skin + 1.6 + + + + + com.versioneye + versioneye-maven-plugin + 3.11.4 + + 0c797be864338a3649f0 + sillelien + Owners + + + + org.complykit + license-check-maven-plugin + 0.5.3 + + + + + + + + + + + + + + + + + + agpl-3.0 + + gpl-2.0 + + gpl-3.0 + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + enforce-versions + + enforce + + + + + + + + + [${maven.version},] + + + ${jdk.version} + + + unix + + + Best Practice is to always define plugin versions! + true + true + true + clean,deploy,site + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + ${jdk.version} + ${jdk.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + + + + + **/*Test.java + + 4 + true + -Xmx256m + methods + 32 + + + + org.apache.maven.surefire + surefire-junit47 + 2.20 + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + + attach-javadocs + + jar + + + + + + + + maven-scm-plugin + 1.9.5 + + ${project.artifactId}-${project.version} + + + + org.pitest + pitest-maven + 1.2.2 + + + com.sillelien.* + + + com.sillelien.* + + 0 + 0 + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + + + + + + + com.versioneye + versioneye-maven-plugin + + + org.complykit + license-check-maven-plugin + + + org.apache.maven.plugins + maven-clean-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.maven.plugins + maven-install-plugin + + + org.apache.maven.plugins + maven-resources-plugin + + + org.apache.maven.plugins + maven-enforcer-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + org.apache.maven.plugins + maven-site-plugin + + + + + + + + + + + + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.4 + + + + maven-scm-plugin + + + org.codehaus.mojo + findbugs-maven-plugin + + + org.pitest + pitest-maven + + + org.apache.rat + apache-rat-plugin + + + verify + + check + + + + + + org.pitest + pitest-maven + + + + org.apache.maven.plugins + maven-source-plugin + + + + + org.springframework.build + aws-maven + 5.0.0.RELEASE + + + + + + + + s3 + + true + + s3 + + + + + dollar-s3-release-repo + Release Repository for Dollar + s3://dollar-repo/release + + + dollar-s3-snapshot-repo + Snapshot Repository for Dollar + s3://dollar-repo/snapshot + + + + + + + + dollar-s3-release-repo + S3 Release Repository for component1 + s3://dollar-repo/release + + true + + + false + + + + dollar-s3-snapshot-repo + Component1-s3-snapshot-repo + s3://dollar-repo/snapshot + + false + + + true + + + + + + + + bintray + + + http://sillelien.github.io/${project.artifactId} + scm:git:git@github.com:sillelien/${project.artifactId}.git + + + bintray-sillelien-maven + sillelien-maven + https://api.bintray.com/maven/sillelien/maven/${project.artifactId}/;publish=1 + + + + + + + + false + + bintray-sillelien-maven + bintray + http://dl.bintray.com/sillelien/maven + + + + + + + + diff --git a/relproxy/.gitignore b/relproxy/.gitignore deleted file mode 100644 index f394c0e..0000000 --- a/relproxy/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/target/ -/build -/docs_internal -/tmp -.classpath -.project -.settings/ -# .gradle/ - -conf/conf_local.properties diff --git a/relproxy/CHANGES.txt b/relproxy/CHANGES.txt deleted file mode 100644 index b6399a4..0000000 --- a/relproxy/CHANGES.txt +++ /dev/null @@ -1,66 +0,0 @@ - -RELEASE CHANGES - -* 0.8.8 - - - Support of static final fields in reloadable proxies. - -* 0.8.7 - - - Some improvement needed when embedding RelProxy Java using the scripting API (avoiding one unnecessary first class reload). - - Added chapter to Manual: "Embedding RelProxy Java in your Java framework to provide hot reload". - - Added new example in RelProxy Examples repository named relproxy_builtin_ex on how to embed RelProxy. - -* 0.8.6 - - - Support of class reloading in inner classes including anonymous inner classes. Chapter "JProxy or how to be able..." updated accordingly. This feature - is used by ItsNat 1.4, this release uses RelProxy internally for user class reloading (mainly listeners). - -* 0.8.5 - - - Improved performance reducing blocking code when checking source code changes and no change is detected. - -* 0.8.4 - - - Added the method JProxyConfig.setRequiredExtraJarPaths(String[] inputJarPaths) to workaround a problem with Liferay 6.2. - - Added JProxy.create(Object,Class[]), GProxy.create(Object,Class[]),JProxyScriptEngine.create(Object,Class[]) for classes implementing multiple interfaces - - The call proxy.equals(proxy2) returns true if both proxies have associated the same original object and there is no reload. - - Added to Manual "Solving jar manifest configuration problems in JProxy" and "Identity of returned proxies" - -* 0.8.3 - - - First release published on bintray.com/JCenter and Maven Central. In Maven just add to your POM: - - - com.innowhere - relproxy - 0.8.3 - - -* 0.8.2 - - - Optimization: method JProxyInputSourceFileExcludedListener.isExcluded(File file,File) is called being parameter file also a directory, if the directory is fully - excluded, RelProxy doesn't call isExcluded for files into the folder. Useful for big source code bases and used RelProxy for normal source code folders. - - Reload reloadable classes using a new ClassLoader is done only when a exposed method of a singleton registered on JProxy is called. - - Added JProxy.isEnabled() - - API for configuration of JProxyScriptEngineFactory and JProxyScriptEngine has changed. - - JProxyScriptEngine has now the same methods (and same behavior) as JProxy. - - Removed JPROXYSH_SCAN_PERIOD, has no sense in shell scripting. - - Bug fix in case of paths with spaces in JProxy. - - Bug fix in case of ".." in paths in JProxy. - - Manual has been very improved documenting new features and new chapters. - - A lot of examples of using RelProxy with popular Java web frameworks. - -* 0.8.1 - - - Fixed a problem with class localization and loading of javax.* classes not included in Java core (ex javax.servlet classes) - - Support of multiple input folder roots for sources: JProxyConfig.setInputPaths(String[] inputPaths) - - Added listener JProxyConfig.setRelProxyOnReloadListener(RelProxyOnReloadListener) to expecify excluded files - - Added listener JProxyConfig.setJProxyCompilerListener(JProxyCompilerListener) to monitor when files are compiled - - Added JProxyConfig.isRunning() to detect whether JProxy is configured and running - - Added new chapters to manual: - "Setting up a web project based on a Maven POM in NetBeans to use JProxy or GProxy" - "How JProxy can help you only in development time (GWT example)" - -* 0.8 First release - diff --git a/relproxy/ant/maven-ant-tasks-2.1.3.jar b/relproxy/ant/maven-ant-tasks-2.1.3.jar deleted file mode 100644 index bec446f..0000000 Binary files a/relproxy/ant/maven-ant-tasks-2.1.3.jar and /dev/null differ diff --git a/relproxy/bin/jproxysh b/relproxy/bin/jproxysh deleted file mode 100644 index c9863b0..0000000 --- a/relproxy/bin/jproxysh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -if [ ! -z "$JPROXYSH_CACHE_CLASS_FOLDER" ]; then JPROXYSH_CACHE_CLASS_FOLDER_PARAM=-DcacheClassFolder="$JPROXYSH_CACHE_CLASS_FOLDER" -fi - -if [ ! -z "$JPROXYSH_COMPILATION_OPTIONS" ]; then JPROXYSH_COMPILATION_OPTIONS_PARAM=-DcompilationOptions="$JPROXYSH_COMPILATION_OPTIONS" -fi - - -"$JAVA_HOME/bin/java" $JAVA_OPTS com.innowhere.relproxy.jproxy.JProxyShell "$@" \ - "$JPROXYSH_CACHE_CLASS_FOLDER_PARAM" \ - "$JPROXYSH_COMPILATION_OPTIONS_PARAM" diff --git a/relproxy/conf/conf.properties b/relproxy/conf/conf.properties deleted file mode 100644 index 68f9fe3..0000000 --- a/relproxy/conf/conf.properties +++ /dev/null @@ -1,16 +0,0 @@ - -program.name=RelProxy - -# CHANGE ALSO IN RelProxy.getVersion() -program.version=0.8.8 -program.javadoc.prog.version=0.8.8 -program.javadoc.doc.version=1.0 - -# El proyecto al ser Maven no Ant ${program} no est\u00e1 definido, lo definimos -program = ${basedir} - -program.target=${program}/target - -program.src=${program}/src/main/java - - diff --git a/relproxy/distribute.xml b/relproxy/distribute.xml deleted file mode 100644 index afe6c42..0000000 --- a/relproxy/distribute.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/relproxy/pom.xml b/relproxy/pom.xml deleted file mode 100644 index 369a555..0000000 --- a/relproxy/pom.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - 4.0.0 - com.innowhere - relproxy - 0.8.8 - jar - - relproxy - Simple Java and Groovy hot class reloader,and Java based shell and scripting. - https://github.com/jmarranz/relproxy - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - Jose Maria Arranz - jmarranz@innowhere.com - Innowhere Software - http://www.innowhere.com - - - - - - Daniel López (greeneyed) - - - https://github.com/Verdoso - - - Michael Vorburger - mike@vorburger.ch - Vorburger.CH - http://www.vorburger.ch - - - - - scm:git:git@github.com:jmarranz/relproxy.git - scm:git:git@github.com:jmarranz/relproxy.git - git@github.com:jmarranz/relproxy.git - - - - - UTF-8 - 1.6 - 1.6 - - - - - - junit - junit - 4.10 - test - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.0.2 - - 1.6 - 1.6 - ${project.build.sourceEncoding} - - - - - org.apache.maven.plugins - maven-resources-plugin - 2.4.3 - - ${project.build.sourceEncoding} - - - - - org.asciidoctor - asciidoctor-maven-plugin - 1.5.2 - - - output-html - generate-resources - - process-asciidoc - - - highlightjs - html - - - - - - - - - src/main/asciidoc/ - - true - images/ - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.4 - - - attach-sources - - jar - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.1 - - - attach-javadocs - - jar - - - - - true - true - RelProxy v${project.version} - RelProxy v${project.version} - - com.innowhere.relproxy.impl - - - - - - - - diff --git a/relproxy/src/main/asciidoc/css/better_toc.css b/relproxy/src/main/asciidoc/css/better_toc.css deleted file mode 100644 index 6f2c03f..0000000 --- a/relproxy/src/main/asciidoc/css/better_toc.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Truco para añadir al menu lateral izdo una bolita a cada item */ - -ul.sectlevel1 { list-style-type: square !important; } -ul.sectlevel2 { list-style-type: circle !important; } diff --git a/relproxy/src/main/asciidoc/images/browser_1.png b/relproxy/src/main/asciidoc/images/browser_1.png deleted file mode 100644 index 710fdc6..0000000 Binary files a/relproxy/src/main/asciidoc/images/browser_1.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/browser_2.png b/relproxy/src/main/asciidoc/images/browser_2.png deleted file mode 100644 index 0348771..0000000 Binary files a/relproxy/src/main/asciidoc/images/browser_2.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/browser_3.png b/relproxy/src/main/asciidoc/images/browser_3.png deleted file mode 100644 index 6445bde..0000000 Binary files a/relproxy/src/main/asciidoc/images/browser_3.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/edit_web_module.png b/relproxy/src/main/asciidoc/images/edit_web_module.png deleted file mode 100644 index cbebc3b..0000000 Binary files a/relproxy/src/main/asciidoc/images/edit_web_module.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/filesync_conf.png b/relproxy/src/main/asciidoc/images/filesync_conf.png deleted file mode 100644 index 3a51c0d..0000000 Binary files a/relproxy/src/main/asciidoc/images/filesync_conf.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/filesync_force_file_sync.png b/relproxy/src/main/asciidoc/images/filesync_force_file_sync.png deleted file mode 100644 index 3ad13fd..0000000 Binary files a/relproxy/src/main/asciidoc/images/filesync_force_file_sync.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/install_filesync1.png b/relproxy/src/main/asciidoc/images/install_filesync1.png deleted file mode 100644 index ae93bb5..0000000 Binary files a/relproxy/src/main/asciidoc/images/install_filesync1.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/module_conf.png b/relproxy/src/main/asciidoc/images/module_conf.png deleted file mode 100644 index 0069523..0000000 Binary files a/relproxy/src/main/asciidoc/images/module_conf.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/publishing.png b/relproxy/src/main/asciidoc/images/publishing.png deleted file mode 100644 index 3571d76..0000000 Binary files a/relproxy/src/main/asciidoc/images/publishing.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/images/server_options.png b/relproxy/src/main/asciidoc/images/server_options.png deleted file mode 100644 index dd0fd0f..0000000 Binary files a/relproxy/src/main/asciidoc/images/server_options.png and /dev/null differ diff --git a/relproxy/src/main/asciidoc/manual.asciidoc b/relproxy/src/main/asciidoc/manual.asciidoc deleted file mode 100644 index 3552f6a..0000000 --- a/relproxy/src/main/asciidoc/manual.asciidoc +++ /dev/null @@ -1,2654 +0,0 @@ -// :icons: font es necesario para que se considere en la generación de HTML usando Font Awesome en donde palabras especiales son iconos por ej en "NOTE:" "IMPORTANT:" etc -:icons: font -// :linkcss: por defecto está definida por si acaso, para linkar asciidoctor.css -:linkcss: -// :copycss: es para que copie el asciidoctor.css por defecto junto al HTML generado -:copycss: -// :sectanchors: para mostrar un link de "posicionar" arriba cada título -:sectanchors: -:toc2: -// usamos highlightjs o prettify porque coderay falla (aunque está incluido) -// NOTA: se configura en el POM pues aquí parece que no funciona -// :source-highlighter: highlightjs - - -= RelProxy - -++++ - -++++ - -(C) Jose María Arranz Santamaría -v0.8.8 March 16, 2016 - -_Because Java is also a dynamic language and Groovy can still be a dynamic language in a Java based environment._ - -== What is RelProxy - -RelProxy is: - -. A simple Java and Groovy hot class reloader for Java and Groovy providing transparent compilation and class reload on the fly with no need of a bootstrap class loader agent -and avoiding context reloading. Reloading happens only in memory. Class reloading can be used in development phase and optionally in production (if source code can be uploaded -to production). -. A scripting environment to execute Java code snippets the same as a shell script. There is no new language, is Java compiled on the fly, code in the initial archive can call -to other normal Java files. Optionally .class can be saved in a cache to provide *the fastest "scripting" language* of the world. -. Execution of Java code snippets in command line (no need of packaging into an archive). -. A simple shell to code, edit and execute code snippets in Java interactively. -. JSR 223 Scripting API implementation for "Java" as the target scripting language. You can embed and execute Java code as scripting into your Java program. - - - -== The origins of RelProxy - - -RelProxy was born as the result of the intent of providing a way to hot class reloading for the http://www.itsnat.org[ItsNat] Java web framework in development and optionally -in production time. - -ItsNat templates are pure X/HTML/SVG files with no logic executed in server, when a template is changed ItsNat automatically reloads the file and the new version is used -in the next attempt of using the template. View logic is coded in Java by using Java W3C DOM APIs modifying ItsNat templates and finally generating the required markup in load -time or JavaScript code as the result of AJAX events. - -As said before, ItsNat provided hot reloading of markup templates, but view logic, coded in Java, remained too static, if you needed to change the view logic you needed to redeploy -the web application, in the best case only the web application context is reloaded. Providing some hot class reloading of view logic (coded in Java or Groovy as we will see later) with no -need of context reload (context reload can be quick or very slow depending of the size of the application and session can be lost) would dramatically increase -the productivity of ItsNat in development and in an edge case, view logic changes would be possible in production. - -Redeploying is not strange to Java web developers when you leave the world of JSPs, JSPs were designed to be hot reloaded including logic coded in Java, server centric frameworks -usually provides some kind of hot reloaded templating, but in most of them conventional Java code is not hot reloaded unless context reload or redeploy. - -The first attempt to provide hot reload of view logic to ItsNat was trying to use Groovy, many people think Groovy is an "interpreted" language and is not, -Groovy is also a language compiled to bytecode, but compilation happens on demand, generated bytecode is usually more flexible ("dynamic") than conventional Java and hot class reloading is built-in. -The problem of using Groovy in ItsNat was the need of registering Groovy listeners in ItsNat Java listeners (view processors), when registering -on an ItsNat (Java) listener the object registered is "frozen" and cannot be reloaded... - -This was the context to invent RelProxy and is the reason of the name, Reload + Proxy, the first feature of RelProxy was creating Java proxies (`java.lang.reflect.Proxy`) to wrap Groovy objects before registering -into Java methods (ItsNat listeners), RelProxy proxies ever use the last version of the Groovy class by using the hot class loader of Groovy, by this way we can hot class reload classes in ItsNat... coded in Groovy. - -The next step is obvious, Groovy class reloader is also a Groovy "compiler on demand", if we are able to make a "compiler on demand" for Java we can get THE SAME AS Groovy provides... in Java. -As you know, as of Java 1.6 we have a http://docs.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html[Java API] to access the compiler of your JDK. - -If we are able to compile and load on demand Java source files, we can be more ambitious and be able to execute Java programs with no need of a previous compilation, just -put into a file Java code like a UNIX shell script in a similar way http://groovy.codehaus.org/Running[Groovy does], this feature provides maybe the fastest "scripting" language of the world. - -Why are you going to need Java as a scripting/shell language? - -Because: - -. Java is faaaast -. Java is robust: because there is compilation on the fly, syntax errors are detected with no need of executing the offending code. -. Java has an enormous amount of libraries -. class files can be optionally automatically saved to avoid compilation in the next execution -. Java 8 is around the corner providing a less verbose language - -If you can execute uncompiled Java code, can we get something similar to an interactive shell to execute Java code snippets? Yes of course. - -Do not confuse Java scripting provided by RelProxy with http://www.beanshell.org/[BeanShell], BeanShell is NOT Java, is a scripting language heavily inspired on Java < 1.5, -running on the JVM with a nice integration with real Java, for instance in BeanShell there is no support of Java 1.5 features like generics. - -Finally, RelProxy is in some way similar to the famous http://zeroturnaround.com/software/jrebel/[JRebel], but is not so transparent and has more limitations. The only advantage is the price, zero, and maybe lesser memory footprint -and better performance. - -That is all, welcome to dynamic Java, welcome to a more dynamic Groovy in a Java environment. - - - -== RelProxy: a fast alternative to context reloading for Java (and Groovy) - -Most of Java application servers (servlet containers) have the context reloading feature. Context reloading is a great feature of Java servlet/EE application servers, -it reloads the web application usually when a .class is changed in `WEB-INF/classes`. This feature is usually used in development with the help of your favourite IDE, -when you change a Java source file the IDE automatically compiles the file and save the .class to WEB-INF/classes, this change triggers the context reloading loading the new -changed class. - -Context reloading is enough for small web applications in development phase but it becomes a productivity problem when the applications becomes big because any small change -triggers the context reloading, the result is the context reloading being executed most of time making your system slow and producing soon a memory overflow (PermGen exception). -Another caveat because the session is lost you must log again etc. - -RelProxy-Java uses a different approach, it just recompile in memory the modified class but there is no class reload (which requires a new ClassLoader). When a exposed method of -a registered singleton in JProxy (a utility of RelProxy for Java) is called, the class reloading happens. By this way you can make big changes without consuming much memory -and CPU, and when changes are done, just with a simple page reload the registered singleton will be called producing the class reload. - -RelProxy does not want to be a "full solution" for the problem of automatic class loading, with RelProxy you decide what code can be reloaded, this also reduce the impact -of the tool because your are just going to reload just a subset of the source code of your web application (web applications are the target of the class reloading feature -of RelProxy but other type of Java applications, for instance desktop, could be used. - -RelProxy can be used in development only and/or in production, in the case of production, source code going to be reloaded is included into the war file (recommended of course -under `WEB-INF/`), you can modify the Java source file directly in production and automatically changes are detected, recompiled in memory and reloaded when needed, no .class -change is needed, by this way you get the similar experience when you change the source code in production of a PHP, Ruby, JSP etc files without the need of restarting -the application. If you do not want use RelProxy in production just disable it, the performance penalty is zero. - -RelProxy also can be used in development only accessing directly to your source files in `/src` folder (multiple source folders are allowed) with no need of uploading source code to -production. - -RelProxy is an alternative to context reloading, if your application is small and you feel comfortable with context reloading you do not need the class reloading features of RelProxy -(RelProxy also offers Java scripting), otherwise you must disable context reloading when RelProxy is enabled. - - - -== Requisites and installation - -RelProxy requires JDK 1.6 or upper, RelProxy have been tested on Oracle JDK 1.6, 1.7 and OpenJDK 1.7. - -Just uncompress the RelProxy distribution file. - -The distribution file has two important files: - -. `relproxy-X.Y.Z.jar` : needed in classpath to use RelProxy in any form. -. `jproxysh` : needed whether command line scripting capabilities in Java are going to be used. - -The distribution file includes some example scripts into the folder `cmd_examples` to test the shell capabilities or RelProxy Java, executing the script -`sh fixesforunix.sh` is recommended to define the appropriated executable permissions, then define the environment variable `JAVA_HOME` with the location of your 1.6+ JDK installation. - -=== Add RelProxy jar as Maven or Gradle dependency - -As of v0.8.3 RelProxy is registered in https://bintray.com/bintray/jcenter/[Bintray/JCenter] and Maven Central repositories. -Add JCenter repository to your POM or Maven settings if you want to import from JCenter, in Gradle just add `jcenter()` to `repositories`. - -Examples for version 0.8.8: - -Maven POM: - -[source,xml] ----- - - com.innowhere - relproxy - 0.8.8 - ----- - -To add JCenter to your POM: - -[source,xml] ----- - - - jcenter - http://jcenter.bintray.com - - ----- - -Gradle build: - -[source,groovy] ----- -dependencies { - compile 'com.innowhere:relproxy:0.8.8' -} ----- - -To add JCenter: - -[source,groovy] ----- -repositories { - jcenter() -} ----- - -== Disabling automatic context reload - -RelProxy is an alternative to context reloading, use of both has no sense and makes RelProxy useless, therefore we must disable context reloading. - -=== In Eclipse and Tomcat - -In this manual Eclipse 4.4 (Luna) has been used, behaviour of previous and future versions may be the same but not tested. - -In Eclipse the Tomcat associated can have a configuration controlled only by Eclipse (the default mode), this configuration is only valid inside the Eclipse environment and the original configuration -of Tomcat is untouched. By default Eclipse is configured to "Automatically publish when resources changed" for your concrete associated Tomcat, this option is required, to review this option go to -menu `Window / Show View / Servers`, this menu option opens a view listing your servers, double click on the concrete Tomcat to show a configuration panel (if no server is associated -to your Eclipse install a Tomcat back to Eclipse Servers view click the right button and select `New / Server` to associate the Tomcat to Eclipse). - -In the configuration panel click on `Publishing` drop-down and review whether is correct. - -image:publishing.png[Publishing, title="Publishing"] - -Now we are going to disable automatic context reload in a per web application/module basis (we can keep enabled in global configuration in `Server Options`). - -image:server_options.png[Server Options, title="Publishing"] - -Click on `Modules` tab. - -image:module_conf.png[Web Modules, title="Web Modules"] - -Disable the `Auto Reload` feature selecting the required module and clicking `Edit...` - -image:edit_web_module.png[Edit Web Module, title="Edit Web Module"] - -=== In NetBeans and Tomcat - -In this manual NetBeans 8.0.2 has been used, behaviour of previous and future versions may be the same but not tested. - -The author of this manual has not been able to disable context reloading feature of Tomcat in NetBeans environment. The xml archive with the `` descriptor in Tomcat installation is -replaced with the content of `META-INF/contex.xml`, in theory just adding `reloadable=true` to `` in this file would make the job... no success, is ignored. - -We are able of disabling context reload avoiding the automatic synchronization of sources and deployed artifacts, two flags are involved in `Project Properties` dialog: - -`Build / Compile / Compile on Save` - -`Run / Deploy on Save` - -Just disabling `Deploy on Save` makes the job of avoiding .class changes and therefore context reloading. - -This is valid for Maven web projects and Ant based projects generated by NetBeans's wizards. - -The price is the lost of automatic synchronization when single source files are changed in runtime. We will explain later how we can workaround this problem. - - - -== Configuring an Eclipse web application publishing Java and/or Groovy source code on the final target war - -=== Dynamic Web Project - -We are talking about a web application created by `New / Dynamic Web Project` (or `New / Project... / Web / Dynamic Web Project`) in Eclipse with Java source code -going to be published in production usually in a folder under `WEB-INF/` (for obvious privacy reasons). - -Because this folder is also a source code folder, you must add it to the project configuration `Properties / Java Build Path / Source / Add Folder`. - -This only affects to Java source folders, in case of using RelProxy-Groovy (GProxy) there is no need of configuring in Eclipse the folder with Groovy code (Groovy built-in compiler compiles -Groovy files with no need of Eclipse). - -Unfortunately Eclipse avoids publishing Java files and they are automatically filtered -(not the case of .groovy files) in the web application internally deployed, there is no Eclipse configuration to avoid this filtering. - -Installing and configuring the http://www.onehippo.org/library/development/use-filesync-eclipse-plugin-for-faster-turn-around.html[Eclipse Filesync Plugin] resolves our problem. - -image:install_filesync1.png[Filesync Plugin installation, title="Filesync Plugin installation"] - -Configure Filesync in project `Properties`. - -image:filesync_conf.png[Filesync configuration, title="Filesync configuration"] - -In this example the folder `relproxy_ex_itsnat/WebContent/WEB-INF/javaex/code` is a source code folder root containing .java files. - -The tricky part is the `Default target folder`. This folder is the root of the real deployed web application, the final deployed web application is created under your `workspace` folder, you can -get the path of this folder executing in your servlet init method: - -[source,java] ----- - public void init(ServletConfig config) throws ServletException - { - super.init(config); - - ServletContext context = getServletContext(); - String realPath = context.getRealPath("/"); - String inputPath = realPath + "/WEB-INF/javaex/code/"; ----- - -The variable `inputPath` contains the path to be configured as `Default target folder` in this example. - -Sometimes the Filesync Plugin seems to fail, in this case you can need to force synchronization: - -image:filesync_force_file_sync.png[Force File Synchronization, title="Force File Synchronization"] - -An alternative is manual synchronization of files under `/WEB-INF` executing a custom Ant script like this: - -[source,xml] ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ----- - -After any modification of source code execute the synchronization task to be copied to the build directory created by Eclipse under `workspace` folder explained before. - - -=== Maven based Web Project - -We can create a web project based on a Maven POM using the Maven archetype `maven-archetype-webapp` selecting `New / Other / Maven / Maven Project`, set in `Filter` -the value `maven-archetype-webapp`. - -In project `Properties / Java Build Path / Libraries / Add Library` add the server for instance `Apache Tomcat v7`. - -The header of web.xml generated by Maven is a bit old (based on `DOCTYPE`), replace it with something like this (servlet 2.5, 3.0 is also valid for Tomcat v7): - -[source,xml] -.web.xml ----- - - -... - ----- - -Follow the same steps described for an Eclipse Dynamic Web Project, yes in spite of most of things are defined in Maven we must repeat the same on Eclipse `Project Properties` dialogs, -(for instance register the extra source code folders). - -Because we are going to publish source code files (located in some place under `WEB-INF/`) to the final war we need to explain to Maven where are located these extra folders -to be also included in compilation and copied to the final war, this configuration is done adding the extra source code folders as resources. - -At the time of writing ItsNat is not in Maven Central repository, you must manually include it in your dependencies as a `system` dependency, and copy the jar to the `WEB-INF/lib` -folder. - -The following POM is a simple example of a ItsNat web application using RelProxy (v0.8.8) and including a published source code folder, `src/main/webapp/WEB-INF/code`, able to -contain reloadable source code (remember to add `ItsNat-1.3.1.jar` to the `WEB-INF/lib` folder): - -[source,xml] -.pom.xml ----- - - 4.0.0 - - com.innowhere - relproxy_ex_itsnat_maven - war - 0.1-SNAPSHOT - relproxy_ex_itsnat_maven Maven Webapp - https://github.com/jmarranz/relproxy/ - - - UTF-8 - - - - - - javax.servlet - servlet-api - 2.5 - provided - - - - javax.servlet.jsp - jsp-api - 2.1 - provided - - - - - - - com.innowhere - relproxy - 0.8.8 - - - - ItsNat - ItsNat-jar - 1.3.1 - system - ${basedir}/src/main/webapp/WEB-INF/lib/ItsNat-1.3.1.jar - - - - org.apache.xmlgraphics - batik-dom - 1.7 - - - - org.apache.xmlgraphics - batik-xml - 1.7 - - - - org.apache.xmlgraphics - batik-util - 1.7 - - - - net.sourceforge.nekohtml - nekohtml - 1.9.12 - - - - xalan - serializer - 2.7.1 - - - - org.codehaus.groovy - groovy-all - 2.1.6 - - - - - - relproxy_ex_itsnat_maven - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.0.2 - - 1.6 - 1.6 - ${project.build.sourceEncoding} - - - - - org.apache.maven.plugins - maven-resources-plugin - 2.4.3 - - ${project.build.sourceEncoding} - - - - - - - - src/main/webapp/WEB-INF/groovyex/code - - - src/main/webapp/WEB-INF/javaex/code - - - - - - ----- - -In Eclipse there is no need of using org.codehaus.mojo:build-helper-maven-plugin plug-in (Maven support is "especial" in Eclipse), folders specified in are -recognized as source folders. - -If you have several folders with source code (RelProxy supports multiple hot reloadable root folders), add more elements, for instance: - -[source,xml] ----- - - - src/main/webapp/WEB-INF/groovyex/code - - - src/main/webapp/WEB-INF/javaex/code - - - src/main/webapp/WEB-INF/javaex/code2 - - ----- - -Because we need to synchronize source code into `/WEB-INF` Filesync or this Ant file will help us: - -[source,xml] -.sync.xml ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ----- - - -== Configuring a NetBeans web application publishing Java and/or Groovy source code on the final target war - -Remember you must avoid context reloading disabling first `Run / Deploy on Save` in `Project Properties` dialog. - -=== Web Project based on Ant created by NetBeans - -A conventional (not Maven) web project is created in NetBeans selecting the menu `File / New Project / Java Web / Web Application`. - -By default he generated Ant file filters .java files under `WEB-INF/` when deploying, to avoid this filtering just add to the build.xml: - -[source,xml] -.build.xml ----- - - - - - ----- - -Fortunately in this type of Ant based project, if the option `Project Properties / Build / Compile / Compile on Save` is enabled, NetBeans takes (by using Ant) care of automatic -synchronization of resources of `/web/WEB-INF` and the same folder under `/build`, this includes .java files because we have avoided this filtering. There is no need -of explicit synchronization by using Ant tasks. - -=== Web Project based on Maven - -A Maven web project is created in NetBeans selecting the menu `File / New Project / Maven / Web Application`. - -Take a look again to the chapter about Eclipse Maven based web applications, the Maven POM structure is the same for NetBeans, remember you must specify extra source code folders -under `WEB-INF/` adding them as ``s. - -Because the default structure of Maven, on development time when deploying a web application, Maven deploys under the `target/projectname` folder -the final web application. Changed source files under `src/webapp` in runtime are not detected by RelProxy because the real files being used -are really below `target/projectname` unless NetBeans automatically synchronizes files between both file trees. Effectively NetBeans automatically copies the modified file -to the `target/projectname`, but unfortunately excluding .java (and .groovy) files. - -In theory enabling `Project Properties / Run / Deploy on Save` does the job but enables automatic context reloading. - -One simple solution is adding a special Ant task to synchronize the source files to the same files in `target/projectname`. Call this task after hot source code modification. - -For instance: - -[source,xml] -.sync.xml ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - ----- - -== Configuring a NetBeans or Eclipse web application NOT publishing Java and/or Groovy source code on the final target war - -As you have seen adding source code files under `WEB-INF/` is very problematic, we have needed some tricks and workarounds to synchronize these source folders to the final deployed -web application. - -If you are not going to publish source code to production and you just need hot reload source code under your `/src/main/java` folder in Maven or `src/java` in NetBeans or `/src` -in Eclipse, that is only in development phase, you do not need synchronization tricks because you tell RelProxy to directly access to the original source being modified for you -and RelProxy recompiles and loads your changes in memory. - -Both types of web applications, publishing source code to war and direct access to normal source code, are going to be shown you using concrete examples. - - -== GProxy or how to be able to reload Groovy classes on the Java environment - -`com.innowhere.relproxy.gproxy.GProxy` is the main Java class of RelProxy to provide this feature, with `GProxy` you can create Java proxies for Groovy objects because -a `java.lang.reflect.Proxy` wrapper is passed instead of the original Groovy object, the original Groovy object is retained under the hood and method calls to the proxy -are redirected to the real object calling the corresponding method using reflection. When the source code of the Groovy class changes GProxy automatically reloads the Groovy -class and creates a new object to replace the old one, the fields of the original object are got and re-set to the new object to keep the state (number of fields and types -must be the same otherwise reloading is not possible and a redeploy is required). - -The following code is an example of how to use RelProxy along with ItsNat web framework, this code is included in the examples of RelProxy (`relproxy_ex_itsnat` or `relproxy_ex_itsnat_maven`). - -The `servlet` variable is a servlet object containing a just configured `groovy.util.GroovyScriptEngine`, the setting up of this utility object is omitted: - - -[source,groovy] -.groovy_servlet_init.groovy ----- -package example.groovyex; - -import org.itsnat.core.http.ItsNatHttpServlet; -import org.itsnat.core.tmpl.ItsNatDocumentTemplate; -import org.itsnat.core.event.ItsNatServletRequestListener; -import groovy.util.GroovyScriptEngine; -import java.lang.reflect.Method; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.gproxy.GProxy; -import com.innowhere.relproxy.gproxy.GProxyGroovyScriptEngine; -import com.innowhere.relproxy.gproxy.GProxyConfig; - - -GroovyScriptEngine groovyEngine = servlet.getGroovyScriptEngine(); - -def gproxyGroovyEngine = { - String scriptName -> return (java.lang.Class)groovyEngine.loadScriptByName(scriptName) - } as GProxyGroovyScriptEngine; - -def reloadListener = { - Object objOld,Object objNew,Object proxy, Method method, Object[] args -> - println("Reloaded " + objNew + " Calling method: " + method) - } as RelProxyOnReloadListener; - -def gpConfig = GProxy.createGProxyConfig(); -gpConfig.setEnabled(true) - .setRelProxyOnReloadListener(reloadListener) - .setGProxyGroovyScriptEngine(gproxyGroovyEngine); - -GProxy.init(gpConfig); - - -String pathPrefix = context.getRealPath("/") + "/WEB-INF/groovyex/pages/"; - -def docTemplate; -docTemplate = itsNatServlet.registerItsNatDocumentTemplate("groovyex","text/html", pathPrefix + "groovyex.html"); - -def db = new FalseDB(); - -ItsNatServletRequestListener listener = GProxy.create(new example.groovyex.GroovyExampleLoadListener(db), ItsNatServletRequestListener.class); -docTemplate.addItsNatServletRequestListener(listener); - ----- - - -Let's explain the previous code: - -[source,groovy] ----- -def gproxyGroovyEngine = { - String scriptName -> return (java.lang.Class)groovyEngine.loadScriptByName(scriptName) - } as GProxyGroovyScriptEngine; ----- - -Defines a listener needed by `GProxy` to indirectly call the `groovy.util.GroovyScriptEngine` to load classes, take a look to the signature of `GProxyGroovyScriptEngine` -there is no dependency with `groovy.*` packages, this is why you can use RelProxy in pure Java projects with no Groovy dependency in spite of Groovy support. - - -[source,groovy] ----- -def reloadListener = { - Object objOld,Object objNew,Object proxy, Method method, Object[] args -> - println("Reloaded " + objNew + " Calling method: " + method) - } as RelProxyOnReloadListener; ----- - -Defines an optional `RelProxyOnReloadListener` listener to be called when Groovy classes have been reloaded because some change has happened in the source code managed by RelProxy. - -An object implementing this interface may be registered on RelProxy to listen when a method of the proxy object has been called (this example only includes one method -exposed by the interface, but nothing prevents of adding more methods to the interface/implementation) and the class of the original object associated has been reloaded -(a new "original" object based on the new class was created to replace it). - -When you perform a source code change in source code managed by RelProxy the first time this method is called is the signal that changes has been detected and reloaded -accordingly. - -This interface and behavior is not `GProxy` specific and will be also used in `JProxy` for Java. - -[source,groovy] ----- -def gpConfig = GProxy.createGProxyConfig(); -gpConfig.setEnabled(true) - .setRelProxyOnReloadListener(reloadListener) - .setGProxyGroovyScriptEngine(gproxyGroovyEngine); - -GProxy.init(gpConfig); ----- - -Configures `GProxy`, now it is ready to proxy Groovy objects. - -Take a look to the optional `setEnabled(true)` configuration call, `GProxy` is enabled by default, this means proxied Groovy objects are instrumented for hot reload. -Calling `setEnabled(false)` tells `GProxy` to ignore any other configuration, `GProxy` is disabled and no proxy is created calling `GProxy.create`, the original Groovy -objects will be returned with absolutely no performance penalty, this is the preferred configuration in production whether you do not want hot class reload in production. - -The final code: - -[source,groovy] ----- -def db = new FalseDB(); - -ItsNatServletRequestListener listener = GProxy.create(new example.groovyex.GroovyExampleLoadListener(db), ItsNatServletRequestListener.class); -docTemplate.addItsNatServletRequestListener(listener); ----- - -is an example of proxying a `example.groovyex.GroovyExampleLoadListener` object and registering the returned Java proxy into the ItsNat infrastructure. -The class `example.groovyex.GroovyExampleLoadListener` implements the ItsNat standard interface `ItsNatServletRequestListener` implementing the method -`processRequest(ItsNatServletRequest request, ItsNatServletResponse response)` this method is called by ItsNat when a page rendered by the template is loaded, the proxy object receives this call and forwards this call -to the latest class loaded, we are going to see more details later. - -Let's go to take a look to `example.groovyex.GroovyExampleLoadListener`: - -[source,groovy] -.GroovyExampleLoadListener.groovy ----- -package example.groovyex; - -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.ItsNatServletRequest; -import org.itsnat.core.ItsNatServletResponse; -import example.groovyex.FalseDB; - -class GroovyExampleLoadListener implements ItsNatServletRequestListener -{ - def db - - GroovyExampleLoadListener() - { - } - - GroovyExampleLoadListener(FalseDB db) // Explicit type tells Groovy to reload FalseDB class when changed - { - this.db = db; - } - - void processRequest(ItsNatServletRequest request, ItsNatServletResponse response) - { - println("GroovyExampleLoadListener 4 "); - - new example.groovyex.GroovyExampleDocument(request.getItsNatDocument(),db); - } -} ----- - -To understand this code let's to explain how ItsNat works, the method `processRequest` is called every time a page is loaded specifying the same ItsNat template, -because this listener was registered as its load processor. - -When RelProxy (through `groovy.util.GroovyScriptEngine`) detects the source code of the class `GroovyExampleLoadListener` or dependent classes like `GroovyExampleDocument` -have changed, all classes with associated hot reloadable source, are reloaded and a new `ClassLoader` is created for them, next calls to `GroovyExampleLoadListener` proxy -will use the new loaded class and the same with dependent classes. - -However a concrete `GroovyExampleLoadListener` object was used to register, how can we reload a class with one live object already created? - -The `GroovyExampleLoadListener` object was the one proxied, the class of this object is reloaded when a source change is detected (or any related class) because this is the -objective of RelProxy, but this object can have fields pointing to objects usually loaded _before_ registering/proxying the `GroovyExampleLoadListener` object. The classes -of these attribute objects may be also reloaded but the new version is not effective because referenced objects are usually being used in other places, if we re-create these -objects we are creating new instances for instance of objects designed to be singletons. This is the case of the `db` attribute of -class `FalseDB`, this attribute references a concrete `FalseDB` object not able to be automatically reloaded in spite of the Groovy `FalseDB` class could be reloaded. -This is why in case of the proxied object `GroovyExampleLoadListener`, RelProxy recreates the object based on the new loaded class by calling the default constructor and -*re-setting the attributes*, by this way the new object is based on the new class containing the same attribute objects defined before, you cannot add, remove or change -the type of attributes, if you do so RelProxy will not be able to hot reload and a new redeploy is needed. - -The proxied class usually creates new objects based on dependent classes to execute some task, if no object of these dependent classes is "saved" and/or used outside of -proxied environment RelProxy can reload dependent classes with no problem. - -This is the case of the class `GroovyExampleDocument` and dependent classes (see the source code). - -Other classes and interfaces like `ItsNatServletRequest` or `ItsNatServletResponse` are not reloaded in this example because they are ItsNat based and source code is not -present in Groovy environment. `FalseDB` class could be reloaded but reloading will fail because the proxied object (`GroovyExampleLoadListener`) holds an attribute `db` of this -class, RelProxy will say you the reloading process has been failed and a redeploy is recommended to effectively use the new version of the class. - -In summary, in this ItsNat example, when source code of `GroovyExampleLoadListener` or dependent classes with source code controlled by RelProxy change, all of these classes are recompiled -and reloaded by Groovy when changed. When the `processRequest` method of the proxied `GroovyExampleLoadListener` object is called because an end user is reloading the related web page, `GProxy` detects -the singleton has been reloaded and recreates the `GroovyExampleLoadListener` object with the new +Class+ re-setting the fields and finally -the `processRequest` method is called and method processing is done using the new version of dependent classes. - -Finally we have been able to reload Groovy classes mixed in a Java environment. - -== JProxy or how to be able to reload Java classes without using Java agents - -Java hot reloadable proxies are very similar to Groovy support of RelProxy, in this case the task of detecting source changes, recompiling and reloading is fully done by RelProxy -(in case of Groovy provided `groovy.util.GroovyScriptEngine` does most of this work). - -`com.innowhere.relproxy.jproxy.JProxy` is the main Java class of RelProxy for hot reload of pure Java, with `JProxy` you can create Java `java.lang.reflect.Proxy` proxies wrapping -your original objects to be passed to listeners, the original object is retained under the hood and method calls to the proxy are redirected to the real object calling the -corresponding method using reflection. When the source code of a monitored Java file is changed, it is automatically recompiled in memory. When the `processRequest` method of -the proxied `JProxyExampleLoadListener` object is called because an end user is reloading the related web page, `JProxy` detects something has changed and reload all monitored -classes with a new `ClassLoader`, because the singleton class has been reloaded JProxy recreates the `JProxyExampleLoadListener` object with the new +Class+ re-setting the fields -to keep the state (number of fields and types must be the same otherwise reloading is not possible and a redeploy is required) and finally the `processRequest` method is called and method processing -is done using the new version of dependent classes. - -As you can see reloading only happens when hot reloadable classes are going to be used, only recompiling is done when some file is changed, this is a performance and memory -improvement over the typical "context reloading per file save". - -The following code is an example of how to use `JProxy` along with ItsNat web framework, this code is part of the RelProxy examples (`relproxy_ex_itsnat` or `relproxy_ex_itsnat_maven`) -basically doing the same as the Groovy example: - - -[source,java] -.JProxyExampleServlet.java ----- -package example.javaex; - -import java.io.File; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; - -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.http.HttpServletWrapper; -import org.itsnat.core.tmpl.ItsNatDocumentTemplate; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.jproxy.JProxy; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyConfig; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; - - -/** - * - * @author jmarranz - */ -public class JProxyExampleServlet extends HttpServletWrapper -{ - public JProxyExampleServlet() - { - } - - @Override - public void init(ServletConfig config) throws ServletException - { - super.init(config); - - ServletContext context = getServletContext(); - String realPath = context.getRealPath("/"); - String inputPath = realPath + "/WEB-INF/javaex/code/"; - String classFolder = null; // Optional: context.getRealPath("/") + "/WEB-INF/classes"; - Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - long scanPeriod = 300; - - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - @Override - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - - JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() - { - @Override - public boolean isExcluded(File file, File rootFolderOfSources) - { - return false; - } - }; - - JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ - @Override - public void beforeCompile(File file) - { - System.out.println("Before compile: " + file); - } - - @Override - public void afterCompile(File file) - { - System.out.println("After compile: " + file); - } - }; - - JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() - { - @Override - public void onDiagnostics(DiagnosticCollector diagnostics) - { - List> diagList = diagnostics.getDiagnostics(); - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - }; - - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setJProxyInputSourceFileExcludedListener(excludedListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyCompilerListener(compilerListener) - .setJProxyDiagnosticsListener(diagnosticsListener); - - JProxy.init(jpConfig); - - - String pathPrefix = context.getRealPath("/") + "/WEB-INF/javaex/pages/"; - - ItsNatDocumentTemplate docTemplate; - docTemplate = itsNatServlet.registerItsNatDocumentTemplate("javaex","text/html", pathPrefix + "javaex.html"); - - FalseDB db = new FalseDB(); - - ItsNatServletRequestListener listener = JProxy.create(new example.javaex.JProxyExampleLoadListener(db), ItsNatServletRequestListener.class); - docTemplate.addItsNatServletRequestListener(listener); - } - -} ----- - -There is more code than Groovy code because `GroovyScriptEngine` setting up was omitted (not specific of RelProxy) and now some configuration options are shown in spite of they -may be optional. - -Let's explain the previous code: - -[source,java] ----- - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setJProxyInputSourceFileExcludedListener(excludedListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyCompilerListener(compilerListener) - .setJProxyDiagnosticsListener(diagnosticsListener); - - JProxy.init(jpConfig); ----- - -This is an example of JProxy configuration. - -* `setEnabled(boolean)` configuration method is the same as `GProxy`, when setting to false other configuration options are ignored, there is no hot reload and proxying and -performance penalty is zero. - -* `setRelProxyOnReloadListener(proxyListener)` is the same as `GProxy` in fact the same interface `RelProxyOnReloadListener` is shared between `GProxy` and `JProxy`. - -* `setInputPath(inputPath)` defines where the source code files of hot reloadable classes is. The variant method `setInputPaths(String[])` allows registering -several root folders and `setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener)` allows excluding concrete files. - -* `setJProxyInputSourceFileExcludedListener(excludedListener)` optionally defines whether the specified folder or file is excluded in the recompiling detection. In this example nothing is excluded. - -* `setScanPeriod(scanPeriod)` defines the period (in ms) between checks of timestamps of source code files to detect changes. - -* `setClassFolder(classFolder)` optionally defines where to save, as .class files, the bytecode resulting of re-compiling modified source files in runtime. -By this way the next time the application is started .class files are aligned with source files and no runtime compilation is needed (class folder of course must be in -classpath). - -* `setCompilationOptions(compilationOptions)` optionally sets the list of options you want for compiling phase, these are the same kind of options you would provide to the -http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javac.html[javac command], internally the -http://docs.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html[Java compiler API] receives this parameters and the reason of the required format. - -* `setJProxyCompilerListener(compilerListener)` optionally registers a listener to be called when a file is going to be compiled. - -* `setJProxyDiagnosticsListener(diagnosticsListener)` optionally registers the `JProxyDiagnosticsListener` listener to be executed when some warning or error happens compiling -Java code, when providing null or not called RelProxy uses a default listener very similar to this example. - - -The final code: - -[source,java] ----- - FalseDB db = new FalseDB(); - - ItsNatServletRequestListener listener = JProxy.create(new example.javaex.JProxyExampleLoadListener(db), ItsNatServletRequestListener.class); - docTemplate.addItsNatServletRequestListener(listener); ----- - -Is symmetric to Groovy counterpart, it is the same example and the same expected behavior of RelProxy but all in Java. - -Anyway this is the code of `JProxyExampleLoadListener`: - -[source,java] -.JProxyExampleLoadListener.java ----- -package example.javaex; - -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.ItsNatServletRequest; -import org.itsnat.core.ItsNatServletResponse; -import org.itsnat.core.html.ItsNatHTMLDocument; - -public class JProxyExampleLoadListener implements ItsNatServletRequestListener -{ - protected FalseDB db; - - public JProxyExampleLoadListener() - { - } - - public JProxyExampleLoadListener(FalseDB db) - { - this.db = db; - } - - public void processRequest(ItsNatServletRequest request, ItsNatServletResponse response) - { - System.out.println("JProxyExampleLoadListener 4 " + this.getClass().getClassLoader().hashCode()); - - new example.javaex.JProxyExampleDocument(request,(ItsNatHTMLDocument)request.getItsNatDocument(),db); - } -} ----- - -As of version 0.8.6 RelProxy also supports hot reload of inner classes including anonymous inner classes. An example of ItsNat code: - -[source,java] -.JProxyExampleLoadListener.java ----- -EventListener listener = new EventListener() -{ - @Override - public void handleEvent(Event evt) - { - ... - } - }; - -Element buttonElem = doc.getElementById("buttonId"); -((EventTarget)buttonElem).addEventListener("click", JProxy.create(listener, EventListener.class) ,false); ----- - -Obviously the container (enclosing) class of the inner class must be reloadable. - -=== Use of JProxy with multiple servlets - -In the previous JProxy example we have supposed one single servlet requiring class reloading and context reloading disabled. - -If you have more servlets or you are a purist developer, you can use a `ServletContextListener`: - -[source,java] -.JProxyServletContextListener.java ----- -... -public class JProxyServletContextListener implements ServletContextListener -{ - @Override - public void contextInitialized(ServletContextEvent sce) - { - System.out.println("ServletContextListener contextInitialized"); - - ServletContext context = sce.getServletContext(); - ... - JProxy.init(jpConfig); - } - - @Override - public void contextDestroyed(ServletContextEvent sce) - { - System.out.println("ServletContextListener contextDestroyed"); - JProxy.stop(); - } -} ----- - -Registered on your `web.xml`: - -[source,xml] -.web.xml ----- - - - example.javaex.JProxyServletContextListener - - - ----- - -The `stop()` call is used to stop the timer checker of source code changes, useful to avoid memory leaks when the context is reloaded and avoid warnings when the -servlet container is stopping. - -Finally in your servlet classes only register your singletons: - -[source,java] ----- - @Override - public void init(ServletConfig config) throws ServletException - { - super.init(config); - - ServletContext context = config.getServletContext(); - - String pathPrefix = context.getRealPath("/") + "/WEB-INF/javaex/pages/"; - - ItsNatDocumentTemplate docTemplate; - docTemplate = itsNatServlet.registerItsNatDocumentTemplate("javaex","text/html", pathPrefix + "javaex.html"); - - FalseDB db = new FalseDB(); - - ItsNatServletRequestListener listener = JProxy.create(new example.javaex.JProxyExampleLoadListener(db), ItsNatServletRequestListener.class); - docTemplate.addItsNatServletRequestListener(listener); - } ----- - - -=== Starting and stopping class change detection and reloading in JProxy - -You can reduce to zero the footprint of RelProxy in production setting `setEnabled(boolean)` to false, however if you are a brave guy or girl and you want to make also hot changes -in production... `JProxy.start()` and `JProxy.stop()` methods are for you. - -Remember we must to define the period between source files checking for changes calling `setScanPeriod(scanPeriod)`, RelProxy defines behind the scene a `java.util.Timer` for this -task, of course every time source code is checked for changes needs some time and CPU use, because there are synchronizations between source code checking and proxies use, a very small performance penalty -happens when checking source code. This is why we can `stop` source code checking if we are not able to make source code changes anymore reducing performance penalty to minimum, and we can -call `start` again to detect any source change. - -You can call several times to `JProxy.start()` and `JProxy.stop()` methods, if nothing is going to be done nothing is done without errors (both methods return true -when a state change was effective), and they are thread safe. - -Call ever to `stop()` in your `ServletContextListener` in the `contextDestroyed(ServletContextEvent)` method. - - -== Using JProxy only in development time with no need of publishing code (a GWT example) - -In previous chapters we have added new source code folders below `WEB-INF/` folder, this configuration is very useful in production to be able to hot change -your Java code, of course in production time you can remove these folders avoiding publishing source code before packaging to `war` and with a simple call `setEnabled(false)` -disable JProxy with zero performance penalty, this makes JProxy helpful in development only but as you can easily figure out, adding source code under `WEB-INF/` folder -is not a good idea if you are not going to use this code in production. - -With JProxy is not necessary to put the source code going to be reloaded under `WEB-INF/`, you can modify Java source code and reload it located in conventional source code folders. - -Because JProxy is going to directly access to original source code, the folder synchronizing problems are gone in this use case. - -We are going to illustrate this capability with a GWT RPC example using Eclipse. Besides how to use JProxy in a GWT-RPC project, in this chapter we are going to learn -how we can exclude source files from the hot class reload system of RelProxy/JProxy because we are going to need this feature. - -This example is for development phase only, nothing prevents of appliying both strategies because JProxy allows multiple source folders to monitor by using `JProxyConfig.setInputPaths(String[])`. - -In GWT, JProxy only can be used to reload Java code executed in server, this is why we are going to apply JProxy to a GWT-RPC project (that is a client-server web application). - -Install http://eclipse.org[Eclipse] (Eclipse 4.4 Luna was used for this example), install the https://developers.google.com/eclipse/docs/download[Google Plugin for Eclipse] (version 4.4 was used), -only install GWT dependencies if you want (there is no need of Android and Google App Engine parts). - -Download RelProxy distribution file and copy the `relproxy-x.y.z.jar` to `/war/WEB-INF/lib/`. - -Select in Eclipse the menu option `New / Other... / Google/ Web Application Project` to create a GWT-RPC sample project (Google App Engine is not needed). - -There is no need of disabling context reloading, it seems is already disabled in the default configuration of GWT. - -In this example we have created the project with name `relproxy_ex_gwt` and package `com.innowhere.relproxyexgwt`, this is -the structure of the generated source code: - -++++ -
-relproxy_ex_gwt    (root folder of project)
-  src
-    com
-      innowhere
-        relproxyexgwt
-          client
-            GreetingService.java
-            GreetingServiceAsync.java
-            Relproxy_ex_gwt.java
-          server
-            GreetingServiceImpl.java
-          shared
-            FieldVerifier.java
-          Relproxy_ex_gwt.gwt.xml
-
-++++ - - -We are only be able to reload classes executed in server, that is, classes below `server/` folder. This why the class `GreetingServiceImpl.java` is our focus, -this is the generated code: - -[source,java] -.GreetingServiceImpl.java ----- -package com.innowhere.relproxyexgwt.server; - -import com.google.gwt.user.server.rpc.RemoteServiceServlet; -import com.innowhere.relproxyexgwt.client.GreetingService; -import com.innowhere.relproxyexgwt.shared.FieldVerifier; - - -/** - * The server side implementation of the RPC service. - */ -@SuppressWarnings("serial") -public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService { - - public String greetServer(String input) throws IllegalArgumentException { - // Verify that the input is valid. - if (!FieldVerifier.isValidName(input)) { - // If the input is not valid, throw an IllegalArgumentException back to - // the client. - throw new IllegalArgumentException("Name must be at least 4 characters long"); - } - - String serverInfo = getServletContext().getServerInfo(); - String userAgent = getThreadLocalRequest().getHeader("User-Agent"); - - // Escape data from the client to avoid cross-site script vulnerabilities. - input = escapeHtml(input); - userAgent = escapeHtml(userAgent); - - return "Hello, " + input + "!

I am running " + serverInfo + ".

It looks like you are using:
" + userAgent; - } - - /** - * Escape an html string. Escaping data received from the client helps to - * prevent cross-site script vulnerabilities. - * - * @param html the html string to escape - * @return the escaped string - */ - private String escapeHtml(String html) { - if (html == null) { - return null; - } - return html.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); - } -} - ----- - -This class is a servlet created to receive RPC requests from client following the interface pattern of the interface `GreetingService` shared by client and server code. -We are not going to try to reload this servlet because to use JProxy we need a reloadable singleton implementing an interface registered in JProxy, therefore -we are deeply transforming `GreetingServiceImpl`: - -[source,java] -.GreetingServiceImpl.java ----- -package com.innowhere.relproxyexgwt.server; - -import java.io.File; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; - -import com.google.gwt.user.server.rpc.RemoteServiceServlet; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.jproxy.JProxy; -import com.innowhere.relproxy.jproxy.JProxyConfig; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxyexgwt.client.GreetingService; - -/** - * The server-side implementation of the RPC service. - */ -@SuppressWarnings("serial") -public class GreetingServiceImpl extends RemoteServiceServlet implements - GreetingService { - - protected GreetingServiceDelegate delegate; - - public void init(ServletConfig config) throws ServletException { - - super.init(config); - - ServletContext context = config.getServletContext(); - - String inputPath = context.getRealPath("/") + "/../src/"; - if (!new File(inputPath).exists()) - { - System.out.println("RelProxy is disabled, detected production mode "); - return; - } - - JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() - { - @Override - public boolean isExcluded(File file, File rootFolder) { - String absPath = file.getAbsolutePath(); - if (file.isDirectory()) - { - return absPath.endsWith(File.separatorChar + "client") || - absPath.endsWith(File.separatorChar + "shared"); - } - else - { - return absPath.endsWith(GreetingServiceDelegate.class.getSimpleName() + ".java") || - absPath.endsWith(GreetingServiceImpl.class.getSimpleName() + ".java"); - } - } - }; - - String classFolder = null; // Optional: context.getRealPath("/") + "/WEB-INF/classes"; - Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - long scanPeriod = 200; - - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ - @Override - public void beforeCompile(File file) - { - System.out.println("Before compile: " + file); - } - - @Override - public void afterCompile(File file) - { - System.out.println("After compile: " + file); - } - }; - - JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() - { - public void onDiagnostics(DiagnosticCollector diagnostics) - { - List> diagList = diagnostics.getDiagnostics(); - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - }; - - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setJProxyInputSourceFileExcludedListener(excludedListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyCompilerListener(compilerListener) - .setJProxyDiagnosticsListener(diagnosticsListener); - - JProxy.init(jpConfig); - - this.delegate = JProxy.create(new GreetingServiceDelegateImpl(this), GreetingServiceDelegate.class); - - } // init - - public String greetServer(String input) throws IllegalArgumentException - { - try - { - return delegate.greetServer(input); - } - catch(IllegalArgumentException ex) - { - ex.printStackTrace(); - throw ex; - } - catch(Exception ex) - { - ex.printStackTrace(); - throw new RuntimeException(ex); - } - } - - public HttpServletRequest getThreadLocalRequestPublic() - { - return getThreadLocalRequest(); - } -} - ----- - -Let's review this JProxy-ready class. `GreetingServiceImpl` is a singleton in practice because is a servlet, therefore this attribute: - -[source,java] ----- -protected GreetingServiceDelegate delegate; ----- - -which hold the reloadable singleton registered on: - -[source,java] ----- -this.delegate = JProxy.create(new GreetingServiceDelegateImpl(this), GreetingServiceDelegate.class); ----- - -As you can see we have created the Java file `GreetingServiceDelegateImpl.java` the class to hold the singleton going to be reloaded, implementing -the interface `GreetingServiceDelegate`. JProxy returns a proxy object "implementing" `GreetingServiceDelegate` exposed to the non-reloadable world. - -Take a look to this listener, in the previous example it was trivial, in this case is very important: - -[source,java] ----- - JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() - { - @Override - public boolean isExcluded(File file, File rootFolder) { - String absPath = file.getAbsolutePath(); - if (file.isDirectory()) - { - return absPath.endsWith(File.separatorChar + "client") || - absPath.endsWith(File.separatorChar + "shared"); - } - else - { - return absPath.endsWith(GreetingServiceDelegate.class.getSimpleName() + ".java") || - absPath.endsWith(GreetingServiceImpl.class.getSimpleName() + ".java"); - } - } - }; ----- - -Registered on: - -[source,java] ----- - .setJProxyInputSourceFileExcludedListener(excludedListener) ----- - -This listener filters the Java source files that must be ignored by RelProxy/JProxy even when modified. - -Because JProxy creates a new ClassLoader and reloads on it all hot-reloadable classes when someone is modified, classes inside `client/` and `shared/` folders must not be reloadable -because has no sense in GWT. - -When a folder inside a declared source folder of reloadable classes specified in configuration is going to be inspected for changed classes, the method `isExcluded` is called to -check whether the complete folder must be excluded, this is very useful for big projects with a lot of not reloadable files. In this case classes inside `client/` or `shared/` -are excluded. If a folder is not excluded, child files and folders into this folder are asked for excluding calling `isExcluded`, the class `GreetingServiceImpl` cannot be reloaded -because it is a servlet and cannot be registered in JProxy because is already in use by the JavaEE servlet system. Finally `GreetingServiceDelegate.java` cannot be reloaded -because is the interface exposed to the non-reloadable world. - -In summary only server/ classes can be reloaded excluding the servlet class `GreetingServiceImpl.java` and `GreetingServiceDelegate.java`. - -In this example there is only specified a root source code folder, RelProxy allows several root source code folder `JProxyConfig.setInputPahts(String[])` instead of `JProxyConfig.setInputPaht(String)`, -the parameter `File rootFolder` specify the root source code folder containing the folder or file to apply excluding rules, this parameter can help you to define very complex -excluding scenarios with several root source code folders. The web application `relproxy_test_itsnat` used to test RelProxy contains a complex excluding scenario with three root source folders. - -This is the code of `GreetingServiceDelegate` : - -[source,java] -.GreetingServiceDelegate.java ----- -package com.innowhere.relproxyexgwt.server; - -public interface GreetingServiceDelegate { - - public String greetServer(String input) throws IllegalArgumentException; - -} ----- - -And the code of `GreetingServiceDelegateImpl.java`, basically a copy/paste of the original servlet code. - -[source,java] -.GreetingServiceDelegateImpl.java ----- -package com.innowhere.relproxyexgwt.server; - -import com.innowhere.relproxyexgwt.shared.FieldVerifier; - -public class GreetingServiceDelegateImpl implements GreetingServiceDelegate -{ - protected GreetingServiceImpl parent; - - public GreetingServiceDelegateImpl() // needed by JProxy - { - } - - public GreetingServiceDelegateImpl(GreetingServiceImpl parent) - { - this.parent = parent; - } - - public String greetServer(String input) throws IllegalArgumentException { - - // Verify that the input is valid. - if (!FieldVerifier.isValidName(input)) { - // If the input is not valid, throw an IllegalArgumentException back to - // the client. - throw new IllegalArgumentException("Name must be at least 4 characters long"); - } - - String serverInfo = parent.getServletContext().getServerInfo(); - String userAgent = parent.getThreadLocalRequestPublic().getHeader("User-Agent"); - - // Escape data from the client to avoid cross-site script vulnerabilities. - input = escapeHtml(input); - userAgent = escapeHtml(userAgent); - - return "Hello, " + input + "!

I am running " + serverInfo - + ".

It looks like you are using:
" + userAgent; - } - - /** - * Escape an html string. Escaping data received from the client helps to - * prevent cross-site script vulnerabilities. - * - * @param html the html string to escape - * @return the escaped string - */ - private String escapeHtml(String html) { - if (html == null) { - return null; - } - return html.replaceAll("&", "&").replaceAll("<", "<") - .replaceAll(">", ">"); - } -} - ----- - -Run this example (`Run As/Web Application GWT Super Dev Mode`), open this URL http://127.0.0.1:8888/Relproxy_ex_gwt.html[http://127.0.0.1:8888/Relproxy_ex_gwt.html] in your browser and a screen like this is shown: - -image:browser_1.png[Fig 1, title="Fig 1"] - -Click on `Send to Server`: - -image:browser_2.png[Fig 2, title="Fig 2"] - -Click on the `Close` button. - -Now we are going to modify on the fly the Java code of `GreetingServiceDelegateImpl`, just change "Hello" by "Hello BROTHER" and save: - -[source,java] ----- - return "Hello BROTHER, " + input + "!

I am running " + serverInfo - + ".

It looks like you are using:
" + userAgent; ----- - -Back to browser, click again on "Send to Server": - -image:browser_3.png[Fig 3, title="Fig 3"] - -As you can see in this case no page reload has been necessary because the requisite is to call the proxied method to reload classes, this call was made by a AJAX/RPC call. - -In this example we made a very simple method change, adding more methods is not a problem but most of the time you will need to add new fields related to new classes, -because `GreetingServiceDelegateImpl` is a singleton we cannot add, remove or change names and types of the fields of this class, to overcome this severe limitation -create new classes avoiding the singleton pattern and move the code to them. Code something like this: - -[source,java] -.GreetingServiceDelegateImpl ----- - public String greetServer(String input) throws IllegalArgumentException { - return new GreetingServiceProcessor(this).greetServer(input); - } ----- - -Declared fields of `GreetingServiceProcessor` can change with no problem because this class can be reloaded and is instantiated by any call to -`GreetingServiceDelegateImpl.greetServer()` with fresh data. - -== Solving jar manifest configuration problems in JProxy - -When developing a Liferay 6.2 example using RelProxy a weird problem was manifested, RelProxy (JProxy) internally makes a call like this: - -[source,java] ----- - Enumeration res = classLoader.getResources("javax/portlet"); ----- - -This call is expected to return a list of URL-JAR pointing to `.class` files with package `javax.portlet`, in Liferay 6.2 the jar containing these classes is `portlet.jar`. In spite -of this jar is defined in the classpath of the `ClassLoader`, nothing is returned. To fix this problem we just need to decompress this jar and add `Name: javax/portlet` to the file -`META-INF/MANIFEST.MF` and package again. - -As you have realized, this solution is "dirty" and problematic with "jar downloaders" like any build tool downloading Maven artifacts. This is why JProxy provides a workaround -allowing direct specification of the problematic jars (use absolute paths), when necessary RelProxy uses the "brute force" to locate the .class of the required package "manually" inspecting -the provided jars. JProxy adds a new configuration method to specify these jars: - -[source,java] ----- - JProxyConfig.setRequiredExtraJarPaths(String[] inputJarPaths) ----- - -This problem has been found in a jar in Liferay, but nothing prevents similar problematic jars in other tools. To see this problem fixed in Liferay take a look to the `JProxyServletContextListener.java` file of http://github.com/jmarranz/relproxy_examples/tree/master/relproxy_ex_liferay-portlet[Liferay Example] included in http://github.com/jmarranz/relproxy_examples[RelProxy Examples]. - -This problem is uncommon, if JProxy throws an error of a class not found and this class is in a jar known by the `ClassPath` used to load JProxy, -suspect this problem and try to add explicitly the path of the problematic jar. - -== Identity of returned proxies - -A proxy object implements the specified interfaces (in practice only one is important), you can call to any methods of the interface, this call is forwarded by RelProxy -to the original or reloaded object. Because a proxy object is also an `Object`, the methods `equals(Object)` and `hashCode()` may be also called, these calls -are forwarded to the wrapped object of the proxy. - -When calling `equals(Object)` method RelProxy detects the special case of passing a proxy parameter, in this case RelProxy obtains the current associated object to the proxy parameter -and passes this object to the equals method. By this way we are able to check the identity of two objects indirectly using two proxy objects. This feature -is very useful in collections when registering a proxy instead of the original object, if another proxy object associated to the same original object is used for instance to remove -from collection, the `equals(Object)` method is correctly called passing the original object. - -Example: - -[source,java] ----- - ItsNatServletRequestListener original = new example.javaex.JProxyExampleLoadListener(db); - ItsNatServletRequestListener proxy = JProxy.create(original, ItsNatServletRequestListener.class); - ItsNatServletRequestListener proxy2 = JProxy.create(original, ItsNatServletRequestListener.class); - System.out.println("EQUALS TEST (true if not reloaded): " + (proxy.equals(proxy2))); ----- - -== A new shell scripting language named Java - -When we think on a shell scripting language we think on `sh` or `csh`, or maybe on the scripting language of Windows Console (based on the old MSDOS), or maybe you know -your preferred conventional dynamic language usually can be executed like another shell language, for instance http://groovy.codehaus.org/Running[Groovy], -http://www.linuxjournal.com/content/python-scripts-replacement-bash-utility-scripts[Python], http://stackoverflow.com/questions/166347/how-do-i-use-ruby-for-shell-scripting[Ruby] -or http://www.2ality.com/2011/12/nodejs-shell-scripting.html[JavaScript]. - -But when you think Java like a new shell scripting language sure you say "it's impossible". - -No, it is possible, RelProxy includes a tool named *`jproxysh`* to make possible executing pure Java code like another shell scripting language. - -The principle is simple and is very similar to Groovy scripting, Groovy compiles on the fly Groovy code saving in memory the compiled bytecode, by this way developers -think Groovy script is interpreted and is not, the same approach is applied to Java through RelProxy. In the case of RelProxy, bytecode can be optionally saved as .class -files to avoid compiling on the fly every time the script is executed. When the JVM is able to load .class files instead of compiling, code execution maybe extremely faster -than conventional scripting languages interpreted line by line from sources, this is why the affirmation of Java as the fastest scripting language of the world is accurate. - -Because pure Java is used and the standard compiler API, nothing prevents using Java scripting in the less verbose Java 8 (v1.8). - - -=== Defining a Java based shell scripting file - -Let's see the first example (some background of UNIX shell is required): - -[source,java] -.example_java_shell ----- -#!/usr/bin/env jproxysh - -String msg = args[0] + args[1]; -System.out.println(msg); - -System.out.println("example_java_shell 1 "); - -example.javashellex.JProxyShellExample.exec(); ----- - -The best way to think this script is like the content of the standard method `main` of a class with some invented name in the default package (no package), in fact, -this is how it is managed internally by RelProxy. - -We could use `/bin/jproxysh` or `/usr/local/bin/jproxysh` but we are forced to install RelProxy in a concrete fixed place, by using `/usr/bin/env` the command `jproxysh` -will be located using the current PATH. - -Save this file in a folder root of the dependent classes. The dependent class in this example is `JProxyShellExample`. - -The hierarchy is: - -++++ -
-<root_folder>
-  example_java_shell           (file)
-  example                      (folder)
-    javashellex                (folder)
-      JProxyShellExample.java  (file)
-
-++++ - - -{nbsp} + -Yes, you are right, mentally add the .java extension to `example_java_shell` and you get the typical file hierarchy of a JavaSE program, in fact JProxy is ready to execute a conventional -JavaSE program with no explicit compilation, this will be shown later. - -The first requisite is that `jproxysh` must be accessible by the environment variable `PATH`, anyway executing this script is not direct, it requires some previous configuration: - -* First of all the `JAVA_HOME` environment variable is required. -* The `CLASSPATH` environment variable must locate the `relproxy-X.X.jar` file and other folders and jars required by your Java application, conventions are the same than a typical JavaSE program. -* Optionally you may specify `JAVA_OPTS` to provide runtime options for the JVM. - -There are other _optional_ environment variables in this case RelProxy specific: - -* JPROXYSH_CACHE_CLASS_FOLDER : defines where to save the .class files resulting of compiling on the fly the scripting code, this folder is automatically added to the class path, -so when the script is loaded the second time the .class files are used instead of source code according to the typical source-binary timestamp rules (if source code is more recent the -class is ignored and replaced with a new file). -* JPROXYSH_COMPILATION_OPTIONS : compilations passed to the JDK compiler, the format is the same as the command line `javac`. - -The following is an example of shell code (into a script file) to execute the previous `example_java_shell`, this example is included in RelProxy distribution: - -[source,sh] -.ex_java_shell_launcher.sh ----- -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -$PROJECT/cmd_examples/code/example_java_shell "HELLO " "WORLD!" ----- - -Because `example_java_shell` is a `jproxysh` based script, nothing prevents of being executed directly using `jproxysh`: - -[source,sh] ----- -jproxysh $PROJECT/cmd_examples/code/example_java_shell "HELLO " "WORLD!" ----- - - - -=== Defining a complete Java class - -As you have seen in `example_java_shell` example, you can access to other Java "scripting" classes from the initial scripting file, this is really interesting when your scripts -become too large and you need state (attributes) more methods and so on, that is, you need more classes. - -If you need your scripting code more structured, you have the option of defining a conventional class in the scripting main file. - -Take a look to this example also included in RelProxy distribution (slightly modified): - -[source,sh] -.example_java_shell_complete_class ----- -#!/usr/bin/env jproxysh - -import example.javashellex.JProxyShellExample; - -public class example_java_shell_complete_class -{ - public static void main(String[] args) - { - String msg = args[0] + args[1]; - System.out.println(msg); - - System.out.println("example_java_shell_complete_class 1 "); - - JProxyShellExample.exec(); - } -} ----- - -`example_java_shell_complete_class` is a conventional class, you can add methods, attributes and so on, the only limitation is the name of the class, it must be the same as the -container file (in this case the container file has not the .java extension). - -You can execute this script by the same ways we executed `example_java_shell`, directly or as a parameter of `jproxysh`. - -=== Scripting conventional JavaSE source code - -The differences between the `example_java_shell_complete_class` script and a conventional Java source file are just the extension (missing) and the hash bang to execute jproxysh. - -We can remove the hashbang and add the `.java` extension to the main scripting file, in this scenario the source code is the same as a conventional JavaSE application. - -Instead of compiling with `javac` and executing with `java` command, you just must execute it with `jproxysh` - -[source,sh] ----- -jproxysh $PROJECT/cmd_examples/code/example_normal_class.java "HELLO " "WORLD!" ----- - - -=== Executing a Java code snippet - -We have seen how much overcomplex can be our scripting files, what if you just need to execute one, or two or three sentences... -You don't need to create a Java shell scripting file, you can write down your script as a parameter and execute. RelProxy through `jproxy` allows executing Java code snippets -on the fly. - -The following is a shell script included in RelProxy distribution which executes a simple code snippet (the param `-c` indicates you are going to execute inline code): - -[source,sh] -.ex_java_shell_snippet_launcher.sh ----- -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh -c 'System.out.print("This code snippet says: ");' \ - 'System.out.println("Hello World!!");' ----- - -You can execute a single code block (into a string parameter) or several blocks in several lines separated with "\", every block can contain several Java sentences. - -Alternatively you can execute a complete class with a standard `main` method, RelProxy detects when you are specifying a sentence block or a complete class, in this case because there -is no file holding the code, the class name must be known by default and must be `_jproxyMainClass_`. For instance: - -[source,sh] ----- -jproxysh -c 'public class _jproxyMainClass_ { ' \ - ' public static void main(String[] args) { ' \ - ' System.out.print("This code snippet says: ");' \ - ' System.out.println("Hello World!!");' \ - ' }' \ - '}' ----- - - - -=== The interactive Java shell - -When you need something more interactive, just like the Groovy shell, RelProxy provides a simple interactive shell. - -To launch the interactive shell define the required environment variables like the code snippet example and execute `jproxysh` with no parameters: - -[source,sh] ----- -jproxysh ----- - -A message info is shown and a prompt is shown waiting for your commands and or code. Write `help` to know the shell options, if the text written is not recognized like a command -it is interpreted as Java code and saved in a buffer to be executed when you want writing the 'exec' command. - -The interactive Java shell accepts a block of sentences or a complete class with a standard `main` method and name `_jproxyMainClass_`. - -=== How to use RelProxy shell scripting in Windows - -RelProxy does not provide a `jproxysh` version for Windows because you can easily build a mini-Linux/Unix in your Windows box with http://www.mingw.org/[MinGW/MSYS]. - -Install MinGW/MSYS, you must be able to locate the shell launcher `msys.bat` in a folder like `C:\MinGW\msys\1.0\` (exact location may change according to your installation folder). - -Execute `msys.bat` and you will get a simple Linux shell environment, in this environment you can execute your typical Linux commands like `ls`, `ps`, `find` etc and of course -launch the previous script files documented in this manual and included in RelProxy distribution. - -You can go to your required folder in MSYS with a `cd` command like this: - -[source,sh] ----- -cd "C:\Program Files\MyProgram" ----- - -Or using a Unix format: - -[source,sh] ----- -cd "/c/Program Files/MyProgram" ----- - -MSYS console is enough for most of purposes, if you can also install `mintty` using the MinGW GUI or command based installer, calling `mintty&` in MSYS opens an even more sophisticated -Linux console. Mintty has some problem with some keyboard characters editing Java code in the RelProxy interactive console, back to basic MSYS console when necesssary. - -If you need to execute Linux shell scripts (for instance RelProxy based) from Windows without a Linux like interactive console, do something like this in your Windows script or console: - -[source,sh] ----- -set PATH=C:\MinGW\msys\1.0\bin;%PATH% -sh ----- - -Where `` can have Windows or Linux format (e.g. `/c/development/relproxy/cmd_examples/ex_java_shell_launcher.sh`). - -== Java Scripting API implementation - -RelProxy implements the official JSR-223 http://docs.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html[Java Scripting API] as found -in Java 1.6. - -The following Java code shows how to create the Java Scripting factory, get an engine instance and execute some code: - -[source,java] ----- -// ... -JProxyConfig jpConfig = JProxy.createJProxyConfig(); -jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyDiagnosticsListener(diagnosticsListener); - -JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); - -ScriptEngineManager manager = new ScriptEngineManager(); -manager.registerEngineName("Java", factory); - -manager.getBindings().put("msg","HELLO GLOBAL WORLD!"); - -ScriptEngine engine = (JProxyScriptEngine)manager.getEngineByName("Java"); - -((JProxyScriptEngine)engine).init(jpConfig); - -Bindings bindings = engine.createBindings(); -bindings.put("msg","HELLO ENGINE SCOPE WORLD!"); - - -StringBuilder code = new StringBuilder(); -code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); -code.append( " String msg = (String)bindings.get(\"msg\"); \n"); -code.append( " System.out.println(msg); \n"); -code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n"); -code.append( " msg = (String)bindings.get(\"msg\"); \n"); -code.append( " System.out.println(msg); \n"); -code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); -code.append( " return \"SUCCESS\";"); - -String result = (String)engine.eval( code.toString() , bindings); -System.out.println("RETURNED: " + result); - -((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined ----- - - -As you can see initialization code is the same as in `JProxy` examples, returned `ScriptEngine` implements `JProxyScriptEngine`, this interface defines the same methods you are going to find in `JProxy`, -the main difference between `JProxy` and `JProxyScriptEngine` (implementing `ScriptEngine`) is that `JProxy` use is based on static methods and `JProxyScriptEngine` in practice -is a singleton. In theory you get a new `ScriptEngine` instance every time you call `manager.getEngineByName("Java")`, just call once, use the returned object as a singleton -and you will get a similar environment to `JProxy` plus the capability of executing code snipets, otherwise concurrent conflicting can happen when competing several `ScriptEngine` -objects (unless configured folders are different). - -Inside the `eval` method, compilation phase is thread safe but not code execution, you can use several threads to call `eval` and execute concurrent lengthy tasks without -execution blocking. - -The last line: - -[source,java] ----- -((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined ----- - -The interface `JProxyScriptEngine` defines the same methods you are going to find in `JProxy`, for instance -the `stop()` method, this method is necessary whether you define a `scanPeriod` and you want to dispose the `ScriptEngine` (otherwise the `ScriptEngine` is looking for -source changes forever), you can also register reloadable singletons calling `JProxyScriptEngine.create(...)` like in JProxy`. - - -The scripting code can be the content of a `main` method with this signature: - -[source,java] ----- -public static Object main(javax.script.ScriptEngine engine,javax.script.ScriptContext context) ----- - -Or optionally you can define a complete Java class containing the previous `main` method and name `_jproxyMainClass_`, for instance: - -[source,java] ----- -public class _jproxyMainClass_ { - public static Object main(javax.script.ScriptEngine engine,javax.script.ScriptContext context) { - javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); - // ... - } -} ----- - -You can directly call the `JProxyScriptEngineFactory.getScriptEngine()` method without registering on a `ScriptEngineManager`, in this case avoid calling -`ServiceContext.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE)` because the default global scope `Bindings` object is not defined. In fact the method -`ScriptEngineManager.getEngineByName(String)` calls `JProxyScriptEngineFactory.getScriptEngine()` and may return null if the `JProxyScriptEngineFactory.getScriptEngine()` -method throws an exception for instance when some configuration data is wrong, because there is no log info of this exception you have no way to know what is happening, in -this case directly call `JProxyScriptEngineFactory.getScriptEngine()` to know what is happening. - -Example: - -[source,java] ----- -// ... - -JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); - -ScriptEngine engine = factory.getScriptEngine(); - -((JProxyScriptEngine)engine).init(jpConfig); - -Bindings bindings = engine.createBindings(); -bindings.put("msg","HELLO ENGINE SCOPE WORLD!"); - -StringBuilder code = new StringBuilder(); -code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); -code.append( " String msg = (String)bindings.get(\"msg\"); \n"); -code.append( " System.out.println(msg); \n"); -code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); -code.append( " return \"SUCCESS\";"); - -String result = (String)engine.eval( code.toString() , bindings); -System.out.println("RETURNED: " + result); - -((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined ----- - -If you need two or more different configurations, create two or more `JProxyScriptEngine` with different configurations. - - - -== Embedding RelProxy Java in your Java framework to provide hot reload - -RelProxy use requires some RelProxy explicit code to provide hot class reloading. If you are a developer of a Java framework, or an autonomous Java service -module in general, you can built-in RelProxy Java in your framework to transparently provide code autoreload to end user code using your framework without -explicit use of the RelProxy API in your end user code beyond some required configuration. - -There are two kind of APIs for using the Java part of RelProxy: - -. JProxy class and related: mainly static methods -. Java Scripting API: based on interfaces - -The second option is preferred to embed RelProxy Java in your Java framework because it is based on interfaces, no public class of RelProxy is needed to expose -in your API, because bootstrap can be executed into your framework. We are going to use the simplified version of the API using `JProxyScriptEngineFactory.create()`. - - -The `JProxyScriptEngine` has been designed to provided the same functionality than `JProxy`, that is, the same methods, but in this case using a pure interface. - -A simple example is the best way to show how to embed RelProxy, this example, RelProxyBuiltin (project relproxy_builtin_ex), is included in the RelProxy Examples repository. -It defines two listeners to be implemented and registered by end user code, one listener is to show the options and the other to execute the corresponding selected action. - -This mini framework and use example is developed using NetBeans and Maven. - -There are two packages: - -. `com.innowhere.relproxy_builtin_ex` : the mini framework. The subpackage `com.innowhere.relproxy_builtin_ex.impl` contains the only non-public class of the framework. -. `com.innowhere.relproxy_builtin_ex_main` : a simple use example. - -The mini framework (public class and interfaces): - -[source,java] -.RelProxyBuiltinRoot.java ----- -package com.innowhere.relproxy_builtin_ex; - -import com.innowhere.relproxy_builtin_ex.impl.RelProxyBuiltinImpl; - -public class RelProxyBuiltinRoot -{ - private final static RelProxyBuiltinImpl SINGLETON = new RelProxyBuiltinImpl(); - - public static RelProxyBuiltin get() - { - return SINGLETON; - } -} ----- - -[source,java] -.RelProxyBuiltin.java ----- -package com.innowhere.relproxy_builtin_ex; - -import com.innowhere.relproxy.jproxy.JProxyScriptEngine; -import java.io.InputStream; -import java.io.PrintStream; - -public interface RelProxyBuiltin -{ - public JProxyScriptEngine getJProxyScriptEngine(); - - public void addOutputListener(OutputListener listener); - public void removeOutputListener(OutputListener listener); - public int getOutputListenerCount(); - - public void addCommandListener(CommandListener listener); - public void removeCommandListener(CommandListener listener); - public int getCommandListenerCount(); - - public void runLoop(InputStream in,PrintStream out); -} ----- - -[source,java] -.OutputListener.java ----- -package com.innowhere.relproxy_builtin_ex; - -import java.io.PrintStream; - -public interface OutputListener -{ - public void write(PrintStream out); -} ----- - -[source,java] -.CommandListener.java ----- -package com.innowhere.relproxy_builtin_ex; - -import java.io.PrintStream; - -public interface CommandListener -{ - public void execute(String command,String input,PrintStream out); -} ----- - -Now the implementation details, this class shows how simple is to built-in RelProxy: - -[source,java] -.RelProxyBuiltinImpl.java ----- -package com.innowhere.relproxy_builtin_ex.impl; - -import com.innowhere.relproxy.jproxy.JProxyScriptEngine; -import com.innowhere.relproxy.jproxy.JProxyScriptEngineFactory; -import com.innowhere.relproxy_builtin_ex.CommandListener; -import com.innowhere.relproxy_builtin_ex.OutputListener; -import com.innowhere.relproxy_builtin_ex.RelProxyBuiltin; -import java.io.InputStream; -import java.io.PrintStream; -import java.util.LinkedHashSet; -import java.util.Scanner; - -public class RelProxyBuiltinImpl implements RelProxyBuiltin -{ - protected JProxyScriptEngine jProxyEngine = null; - protected LinkedHashSet outListeners = new LinkedHashSet(); - protected LinkedHashSet commandListeners = new LinkedHashSet(); - - @Override - public JProxyScriptEngine getJProxyScriptEngine() - { - if (jProxyEngine == null) jProxyEngine = (JProxyScriptEngine)JProxyScriptEngineFactory.create().getScriptEngine(); - return jProxyEngine; - } - - public JProxyScriptEngine getJProxyScriptEngineIfConfigured() - { - if (jProxyEngine == null || !jProxyEngine.isEnabled()) - return null; - return jProxyEngine; - } - - @Override - public void addOutputListener(OutputListener listener) - { - JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); - if (jProxy != null) - { - listener = jProxy.create(listener,OutputListener.class); - } - outListeners.add(listener); - } - - @Override - public void removeOutputListener(OutputListener listener) - { - JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); - if (jProxy != null) - { - listener = jProxy.create(listener,OutputListener.class); - } - outListeners.remove(listener); - } - - @Override - public int getOutputListenerCount() - { - return outListeners.size(); - } - - @Override - public void addCommandListener(CommandListener listener) - { - JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); - if (jProxy != null) - { - listener = jProxy.create(listener,CommandListener.class); - } - commandListeners.add(listener); - } - - @Override - public void removeCommandListener(CommandListener listener) - { - JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); - if (jProxy != null) - { - listener = jProxy.create(listener,CommandListener.class); - } - commandListeners.remove(listener); - } - - @Override - public int getCommandListenerCount() - { - return commandListeners.size(); - } - - @Override - public void runLoop(InputStream in,PrintStream out) - { - Scanner scanner = new Scanner(in); - - while(true) - { - out.print("Enter phrase:"); - - String input = scanner.nextLine(); - - out.println("Command list:"); - - for(OutputListener listener : outListeners) - listener.write(out); - - out.print("Enter command (or quit):"); - String command = scanner.nextLine(); - if ("quit".equals(command)) - break; - - for(CommandListener listener : commandListeners) - listener.execute(command,input,out); - } - } -} ----- - -These three methods are enough to explain how to bootstrap RelProxy Java engine and how easy is to instrument listener registering hot reloadable: - -[source,java] -.RelProxyBuiltinImpl.java (partial) ----- - @Override - public JProxyScriptEngine getJProxyScriptEngine() - { - if (jProxyEngine == null) jProxyEngine = (JProxyScriptEngine)JProxyScriptEngineFactory.create().getScriptEngine(); - return jProxyEngine; - } - - public JProxyScriptEngine getJProxyScriptEngineIfConfigured() - { - if (jProxyEngine == null || !jProxyEngine.isEnabled()) - return null; - return jProxyEngine; - } - - @Override - public void addOutputListener(OutputListener listener) - { - JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); - if (jProxy != null) - { - listener = jProxy.create(listener,OutputListener.class); - } - outListeners.add(listener); - } ----- - -The public method `RelProxyBuiltin.getJProxyScriptEngine()` must be called in starting time to configure RelProxy. If RelProxy is not configured and enabled there is no performance penalty. - -Remember that the proxy object returned by `create(...)` method correctly calls the method `hashCode()` and `equals(Object)` in the internal true listener object for -identity purposes required by the listener collection/registry. - - -This is the example code of this console based program (names are inspired on JUnit but is not really a JUnit test example): - -[source,java] -.Main.java ----- -package com.innowhere.relproxy_builtin_ex_main; - -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.jproxy.JProxy; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyConfig; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import com.innowhere.relproxy.jproxy.JProxyScriptEngine; -import com.innowhere.relproxy_builtin_ex.CommandListener; -import com.innowhere.relproxy_builtin_ex.RelProxyBuiltin; -import com.innowhere.relproxy_builtin_ex.RelProxyBuiltinRoot; -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.Arrays; -import java.util.List; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; - -public class Main -{ - public static void main(String[] args) throws Exception - { - new Main(); - } - - public Main() - { - // Note: NetBeans Console window works bad (no input) with Maven Test tasks http://stackoverflow.com/questions/3035351/broken-console-in-maven-project-using-netbeans - // this is why is not a really JUnit test. - setUp(); - try - { - mainTest(); - } - finally - { - tearDown(); - } - System.exit(0); - } - - public void setUp() - { - URL res = this.getClass().getResource("/"); // .../target/classes/ - - // Use example of RelProxy in development time: - - String inputPath = res.getFile() + "/../../src/main/java/"; - - if (new File(inputPath).exists()) - { - System.out.println("RelProxy to be enabled, development mode detected"); - } - else - { - System.out.println("RelProxy disabled, production mode detected"); - return; - } - - - JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() - { - @Override - public boolean isExcluded(File file, File rootFolderOfSources) - { - String absPath = file.getAbsolutePath(); - - if (file.isDirectory()) - { - return absPath.endsWith(File.separatorChar + "relproxy_builtin_ex"); - } - else - { - return absPath.endsWith(File.separatorChar + Main.class.getSimpleName() + ".java"); - } - } - }; - - String classFolder = null; // Optional - Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - long scanPeriod = 1000; - - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - @Override - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ - @Override - public void beforeCompile(File file) - { - System.out.println("Before compile: " + file); - } - - @Override - public void afterCompile(File file) - { - System.out.println("After compile: " + file); - } - }; - - JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() - { - @Override - public void onDiagnostics(DiagnosticCollector diagnostics) - { - List> diagList = diagnostics.getDiagnostics(); - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - }; - - RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get(); - JProxyScriptEngine engine = rpbRoot.getJProxyScriptEngine(); - - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setJProxyInputSourceFileExcludedListener(excludedListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyCompilerListener(compilerListener) - .setJProxyDiagnosticsListener(diagnosticsListener); - - engine.init(jpConfig); - - System.out.println("RelProxy running"); - } - - public void tearDown() - { - RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get(); - JProxyScriptEngine engine = rpbRoot.getJProxyScriptEngine(); - engine.stop(); - - System.out.println("RelProxy stopped"); - } - - public void mainTest() - { - RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get(); - - TestListener listener = new TestListener(); - - rpbRoot.addOutputListener(listener); - assertTrue(rpbRoot.getOutputListenerCount() == 1); - rpbRoot.removeOutputListener(listener); - assertTrue(rpbRoot.getOutputListenerCount() == 0); - - rpbRoot.addOutputListener(listener); - - - CommandListener commandListener = listener.getCommandListener(); - - rpbRoot.addCommandListener(commandListener); - assertTrue(rpbRoot.getCommandListenerCount() == 1); - rpbRoot.removeCommandListener(commandListener); - assertTrue(rpbRoot.getCommandListenerCount() == 0); - - rpbRoot.addCommandListener(commandListener); - - rpbRoot.runLoop(System.in,System.out); - } - - - private static void assertTrue(boolean res) - { - if (!res) throw new RuntimeException("Unexpected Error"); - } -} - ----- - -Take a look to this piece of code: - -[source,java] -.Main.java (partial) ----- - URL res = this.getClass().getResource("/"); // .../target/classes/ - - // Use example of RelProxy in development time: - - String inputPath = res.getFile() + "/../../src/main/java/"; - - if (new File(inputPath).exists()) - { - System.out.println("RelProxy to be enabled, development mode detected"); - } - else - { - System.out.println("RelProxy disabled, production mode detected"); - return; - } - - JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() - { - @Override - public boolean isExcluded(File file, File rootFolderOfSources) - { - String absPath = file.getAbsolutePath(); - - if (file.isDirectory()) - { - return absPath.endsWith(File.separatorChar + "relproxy_builtin_ex"); - } - else - { - return absPath.endsWith(File.separatorChar + Main.class.getSimpleName() + ".java"); - } - } - }; ----- - -We get and later register the root folder of the source code of our application, this code "may be" reloadable. - -We need to exclude the source code of the framework because is not obviously end user code (not reloadable), and the `Main.java` file containing -the test code, this class is not reloadable, only the class `TestListener.java` (in the same folder than Main.java) will be (and must be) reloadable. -We could simplify this `isExcluded` method just excluding anything different to the file `TestListener.java`, but excluding whole folders is recommended to -speed the traversing of the source code file tree because otherwise all not reloadable files, into not reloadable folders, are checked. - -Finally the class `TestListener.java` containing both listeners, the `CommandListener` implemented is forced to be an anonymous inner class just for demonstrative purposes: - -[source,java] -.TestListener.java ----- -package com.innowhere.relproxy_builtin_ex_main; - -import com.innowhere.relproxy_builtin_ex.CommandListener; -import com.innowhere.relproxy_builtin_ex.OutputListener; -import java.io.PrintStream; - -public class TestListener implements OutputListener -{ - @Override - public void write(PrintStream out) - { - out.println("uppercase"); - out.println("lowercase"); - } - - public CommandListener getCommandListener() - { - return new CommandListener() - { - @Override - public void execute(String command,String text,PrintStream out) - { - if ("uppercase".equals(command)) - out.println(text.toUpperCase()); - else if ("lowercase".equals(command)) - out.println(text.toLowerCase()); - else - out.println("Unknown command:" + command); - } - }; - } -} - ----- - -Execute the Main class and try the predefined options. To check if RelProxy is working fine try to add a new option "same" without stopping the program: - -[source,java] ----- - - @Override - public void write(PrintStream out) - { - out.println("uppercase"); - out.println("lowercase"); - - out.println("same"); // NEW - } - - public CommandListener getCommandListener() - { - return new CommandListener() - { - @Override - public void execute(String command,String text,PrintStream out) - { - if ("uppercase".equals(command)) - out.println(text.toUpperCase()); - else if ("lowercase".equals(command)) - out.println(text.toLowerCase()); - - else if ("same".equals(command)) // NEW - out.println(text); // NEW - - else - out.println("Unknown command:" + command); - } - }; - } -} - ----- - -The next phrase to process includes now the "same" action with no need of stopping the console application. - -Note: use RelProxy 0.8.7 or upper, this release adds an improvement when embedding by this way. - diff --git a/relproxy/src/main/cmd_examples/README.txt b/relproxy/src/main/cmd_examples/README.txt deleted file mode 100644 index 14c3ec3..0000000 --- a/relproxy/src/main/cmd_examples/README.txt +++ /dev/null @@ -1,2 +0,0 @@ - -These files cannot be executed here, they will be copied to the distribution files able to be executed as examples of the final distribution form of RelProxy. diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_interactive_launcher.sh b/relproxy/src/main/cmd_examples/ex_java_shell_interactive_launcher.sh deleted file mode 100644 index d3231e1..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_interactive_launcher.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh - - diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_launcher.sh b/relproxy/src/main/cmd_examples/ex_java_shell_launcher.sh deleted file mode 100644 index a3b282c..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_launcher.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -$PROJECT/cmd_examples/code/example_java_shell "HELLO " "WORLD!" diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_2.sh b/relproxy/src/main/cmd_examples/ex_java_shell_launcher_2.sh deleted file mode 100644 index 4207ec9..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_2.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh $PROJECT/cmd_examples/code/example_java_shell "HELLO " "WORLD!" diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_complete_class.sh b/relproxy/src/main/cmd_examples/ex_java_shell_launcher_complete_class.sh deleted file mode 100644 index 6c7e2f6..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_complete_class.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -$PROJECT/cmd_examples/code/example_java_shell_complete_class "HELLO " "WORLD!" diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_complete_class_2.sh b/relproxy/src/main/cmd_examples/ex_java_shell_launcher_complete_class_2.sh deleted file mode 100644 index 0e832ed..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_complete_class_2.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh $PROJECT/cmd_examples/code/example_java_shell_complete_class "HELLO " "WORLD!" diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_normal_class.sh b/relproxy/src/main/cmd_examples/ex_java_shell_launcher_normal_class.sh deleted file mode 100644 index 819b73e..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_launcher_normal_class.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh $PROJECT/cmd_examples/code/example_normal_class.java "HELLO " "WORLD!" diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_snippet_launcher.sh b/relproxy/src/main/cmd_examples/ex_java_shell_snippet_launcher.sh deleted file mode 100644 index 4d64334..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_snippet_launcher.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh -c 'System.out.print("This code snippet says: ");' \ - 'System.out.println("Hello World!!");' - diff --git a/relproxy/src/main/cmd_examples/ex_java_shell_snippet_launcher_complete_class.sh b/relproxy/src/main/cmd_examples/ex_java_shell_snippet_launcher_complete_class.sh deleted file mode 100644 index 5940699..0000000 --- a/relproxy/src/main/cmd_examples/ex_java_shell_snippet_launcher_complete_class.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -RELPROXY_JAR=relproxy-0.8.8.jar - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/lib/$RELPROXY_JAR -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - -jproxysh -c 'public class _jproxyMainClass_ { ' \ - ' public static void main(String[] args) { ' \ - ' System.out.print("This code snippet says: ");' \ - ' System.out.println("Hello World!!");' \ - ' }' \ - '}' diff --git a/relproxy/src/main/cmd_examples/fixesforunix.sh b/relproxy/src/main/cmd_examples/fixesforunix.sh deleted file mode 100644 index e146ea3..0000000 --- a/relproxy/src/main/cmd_examples/fixesforunix.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -chmod +x $PROJECT/cmd_examples/*.sh - -chmod +x $PROJECT/cmd_examples/code/example_java_shell -chmod +x $PROJECT/cmd_examples/code/example_java_shell_complete_class - -chmod +x $PROJECT/bin/jproxysh diff --git a/relproxy/src/main/java/com/innowhere/relproxy/RelProxy.java b/relproxy/src/main/java/com/innowhere/relproxy/RelProxy.java deleted file mode 100644 index 8341873..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/RelProxy.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.innowhere.relproxy; - -/** - * Is the root of RelProxy - * - * @author Jose Maria Arranz Santamaria - */ -public class RelProxy -{ - /** - * Returns the version of this RelProxy library. - * - * @return the version of the library. - */ - public static String getVersion() - { - return "0.8.8"; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/RelProxyException.java b/relproxy/src/main/java/com/innowhere/relproxy/RelProxyException.java deleted file mode 100644 index f8e4666..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/RelProxyException.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.innowhere.relproxy; - -/** - * Internal checked exceptions thrown by RelProxy and library specific errors are wrapped into this exception class. - * - * @author Jose Maria Arranz Santamaria - */ -public class RelProxyException extends RuntimeException -{ - /** - * Constructs a new exception with the specified message and cause. - * - *

Parameters are passed to the super constructor.

- * - * @param message the detail message - * @param cause the cause - */ - public RelProxyException(String message, Throwable cause) - { - super(message, cause); - } - - /** - * Constructs a new exception with the specified message. - * - *

Parameter is passed to the super constructor.

- * - * @param message the detail message - */ - public RelProxyException(String message) - { - super(message); - } - - /** - * Constructs a new exception with the specified cause. - * - *

Parameter is passed to the super constructor.

- * - * @param cause the cause - */ - public RelProxyException(Throwable cause) - { - super(cause); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/RelProxyOnReloadListener.java b/relproxy/src/main/java/com/innowhere/relproxy/RelProxyOnReloadListener.java deleted file mode 100644 index 2fc287d..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/RelProxyOnReloadListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.innowhere.relproxy; - -import java.lang.reflect.Method; - -/** - * Is the interface needed to register a class reload listener. - * - *

An object implementing this interface can optionally be registered on RelProxy to listen when the method of a proxy object has been called - * and the class of the original object associated has been reloaded (and a new "original" object based on the new class was created to replace it). - *

- * - * - * @see com.innowhere.relproxy.jproxy.JProxyConfig#setRelProxyOnReloadListener(com.innowhere.relproxy.RelProxyOnReloadListener) - * @see com.innowhere.relproxy.gproxy.GProxyConfig#setRelProxyOnReloadListener(com.innowhere.relproxy.RelProxyOnReloadListener) - * @author Jose Maria Arranz Santamaria - */ -public interface RelProxyOnReloadListener -{ - /** - * Called when some source code change has happened and a new class has been compiled and reloaded. - * - * @param objOld the old object before class reload. - * @param objNew the new object based on the new class loaded by the new class loader. - * @param proxy the proxy object created by {@link java.lang.reflect.Proxy} being used. - * @param method the method being called through the proxy object. - * @param args the parameters being used in the method call. - */ - public void onReload(Object objOld,Object objNew,Object proxy, Method method, Object[] args); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxy.java b/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxy.java deleted file mode 100644 index 1ec80b2..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxy.java +++ /dev/null @@ -1,68 +0,0 @@ - -package com.innowhere.relproxy.gproxy; - -import com.innowhere.relproxy.impl.gproxy.GProxyConfigImpl; -import com.innowhere.relproxy.impl.gproxy.GProxyDefaultImpl; - -/** - * Is the class to create Java proxy objects based on Groovy objects and keep track of Groovy source code changes reloading Groovy classes when detected. - * - * @author Jose Maria Arranz Santamaria - */ -public class GProxy -{ - /** - * Creates a {@link GProxyConfig} object to be used to configure GProxy. - * - * @return a new configuration object. - * @see #init(GProxyConfig) - */ - public static GProxyConfig createGProxyConfig() - { - return GProxyDefaultImpl.createGProxyConfig(); - } - - /** - * Initializes GProxy with the provided configuration object. - * - * @param config - */ - public static void init(GProxyConfig config) - { - GProxyDefaultImpl.initStatic((GProxyConfigImpl)config); - } - - /** - * Creates a proxy object using java.lang.reflect.Proxy based on the provided Groovy object and the class of the implemented Java interface. - * - *

This method is a simplification for a single interface (the most common case) of {@link #create(Object,Class[])} .

- * - * @param the interface implemented by the original object and proxy object returned. - * @param obj the original object to proxy. - * @param clasz the class of the interface implemented by the original object and proxy object returned. - * @return the java.lang.reflect.Proxy object associated or the original object when GProxy is disabled. - */ - public static T create(T obj,Class clasz) - { - return GProxyDefaultImpl.createStatic(obj, clasz); - } - - /** - * Creates a proxy object using java.lang.reflect.Proxy based on the provided Groovy object and the classes of the implemented Java interfaces. - * - *

If GProxy has been configured and is enabled this method returns a java.lang.reflect.Proxy object implementing instead of - * the original object provided. Methods called in proxy object are received by GProxy and forwarded to the original object, if source code - * managed by GProxy has been changed, the class of the original object is reloaded based on the new source and the original object - * is recreated with the new class and fields are re-set in the new object, then the method is called on the new original object.

- * - *

If GProxy is disabled returns the original object provided with no performance penalty.

- * - * @param obj the original object to proxy. - * @param classes the classes of the interfaces implemented by the original object and proxy object returned. - * @return the java.lang.reflect.Proxy object associated or the original object when GProxy is disabled. - */ - public static Object create(Object obj,Class[] classes) - { - return GProxyDefaultImpl.createStatic(obj, classes); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxyConfig.java b/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxyConfig.java deleted file mode 100644 index 0184f5f..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxyConfig.java +++ /dev/null @@ -1,43 +0,0 @@ - -package com.innowhere.relproxy.gproxy; - -import com.innowhere.relproxy.RelProxyOnReloadListener; - -/** - * Interface implemented by the configuration object needed to initialize GProxy. - * - * - * @see GProxy#init(GProxyConfig) - * @author Jose Maria Arranz Santamaria - */ -public interface GProxyConfig -{ - /** - * Sets whether automatic detection of source code changes is enabled. - * - *

If set to false other configuration parameters are ignored, there is no automatic source code change detection/reload and original objects are returned - * instead of proxies, performance penalty is zero. Setting to false is recommended in production whether source code change detection/reload is not required.

- * - * @param enabled whether automatic source code change detection and reload is enabled. By default is true. - * @return this object for flow API use. - */ - public GProxyConfig setEnabled(boolean enabled); - - /** - * Sets the class reload listener. - * - * @param relListener the class reload listener. By default is null. - * @return this object for flow API use. - */ - public GProxyConfig setRelProxyOnReloadListener(RelProxyOnReloadListener relListener); - - /** - * Sets the object implementing the GroovyScriptEngine wrapper used to reload Groovy classes. - * - *

This parameter is required otherwise there is no bridge between RelProxy and Groovy because there is no explicit Groovy dependency in RelProxy. - * - * @param engine the GroovyScriptEngine wrapper. - * @return this object for flow API use. - */ - public GProxyConfig setGProxyGroovyScriptEngine(GProxyGroovyScriptEngine engine); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxyGroovyScriptEngine.java b/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxyGroovyScriptEngine.java deleted file mode 100644 index 7294bcd..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/GProxyGroovyScriptEngine.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.innowhere.relproxy.gproxy; - -/** - * Interface to implement the object implementing the GroovyScriptEngine wrapper used to reload Groovy classes. - * - *

The following is a very simple example of the required implementation, groovyEngine is the groovy.util.GroovyScriptEngine - * object:

- - def gproxyGroovyEngine = { - String scriptName -> return (java.lang.Class)groovyEngine.loadScriptByName(scriptName) - } as GProxyGroovyScriptEngine; - - * - * - * @see GProxyConfig#setGProxyGroovyScriptEngine(GProxyGroovyScriptEngine) - * @author Jose Maria Arranz Santamaria - */ -public interface GProxyGroovyScriptEngine -{ - /** - * The class implementing this method must call the method groovy.util.GroovyScriptEngine.loadScriptByName(String scriptName) passing - * the scriptName. - * - *

This method is called by GProxy when it needs to get the Class associated to the specified Groovy script/class to check if this class - * has changed because Groovy has reloaded the class when a source code has been detected. - * - * @param scriptName the name of the Groovy script/class. - * @return the class associated to the specified Groovy script. - */ - public Class loadScriptByName(String scriptName); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/package.html b/relproxy/src/main/java/com/innowhere/relproxy/gproxy/package.html deleted file mode 100644 index 3cbf0dc..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/gproxy/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains the classes related with Groovy support of RelProxy. - - - diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/FileExt.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/FileExt.java deleted file mode 100644 index 0533f75..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/FileExt.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.innowhere.relproxy.impl; - -import com.innowhere.relproxy.RelProxyException; -import java.io.File; -import java.io.IOException; - -/** - * - * @author jmarranz - */ -public class FileExt -{ - protected final File file; - protected final String cannonicalPath; // El obtener el cannonicalPath exige acceder al sistema de archivos, por eso nos inventamos esta clase, para evitar sucesivas llamadas a File.getCanonicalPath() - - public FileExt(File file) - { - this.file = file; - try { this.cannonicalPath = file.getCanonicalPath(); } - catch (IOException ex) { throw new RelProxyException(ex); } - } - - public File getFile() - { - return file; - } - - public String getCanonicalPath() - { - return cannonicalPath; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyImpl.java deleted file mode 100644 index 7a0afcf..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.innowhere.relproxy.impl; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; - -/** - * - * @author jmarranz - */ -public abstract class GenericProxyImpl -{ - protected RelProxyOnReloadListener reloadListener; - - public GenericProxyImpl() - { - } - - public static void checkSingletonNull(GenericProxyImpl singleton) - { - if (singleton != null) - throw new RelProxyException("Already initialized"); - } - - protected static void checkSingletonExists(GenericProxyImpl singleton) - { - if (singleton == null) - throw new RelProxyException("Execute first the init method"); - } - - protected void init(GenericProxyConfigBaseImpl config) - { - this.reloadListener = config.getRelProxyOnReloadListener(); - } - - public RelProxyOnReloadListener getRelProxyOnReloadListener() - { - return reloadListener; - } - - public T create(T obj,Class clasz) - { - if (obj == null) return null; - - return (T)create(obj,new Class[] { clasz }); - } - - public Object create(Object obj,Class[] classes) - { - if (obj == null) return null; - - InvocationHandler handler = createGenericProxyInvocationHandler(obj); - - Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(),classes, handler); - return proxy; - } - - - public abstract GenericProxyInvocationHandler createGenericProxyInvocationHandler(Object obj); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyInvocationHandler.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyInvocationHandler.java deleted file mode 100644 index e9fba8c..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyInvocationHandler.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.innowhere.relproxy.impl; - -import com.innowhere.relproxy.RelProxyOnReloadListener; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -/** - * - * @author jmarranz - */ -public abstract class GenericProxyInvocationHandler implements InvocationHandler -{ - protected GenericProxyImpl root; - protected GenericProxyVersionedObject verObj; - - public GenericProxyInvocationHandler(GenericProxyImpl root) - { - this.root = root; - } - - private Object getCurrent() - { - return verObj.getCurrent(); - } - - private Object getNewVersion() throws Throwable - { - return verObj.getNewVersion(); - } - - @Override - public synchronized Object invoke(Object proxy, Method method, Object[] args) throws Throwable - { - Object oldObj = getCurrent(); - Object obj = getNewVersion(); - - RelProxyOnReloadListener reloadListener = root.getRelProxyOnReloadListener(); - if (oldObj != obj && reloadListener != null) - reloadListener.onReload(oldObj,obj,proxy,method,args); - - if (args != null && args.length == 1) - { - // Conseguimos que en proxy1.equals(proxy2) se usen los objetos asociados no los propios proxies, para ello obtenemos el objeto asociado al parámetro - // No hace falta que equals forme parte de la interface, pero está ahí implícitamente - // hashCode() como no tiene params es llamado sin problema de conversiones - Object param = args[0]; - if (param instanceof Proxy && // Si es una clase generada com.sun.proxy.$ProxyN (N=1,2...) es también derivada de Proxy - method.getName().equals("equals") && - method.getReturnType().equals(boolean.class)) - { - Class[] paramTypes = method.getParameterTypes(); - if (paramTypes.length == 1 && paramTypes[0].equals(Object.class)) - { - InvocationHandler paramInvHandler = Proxy.getInvocationHandler(param); - if (paramInvHandler instanceof GenericProxyInvocationHandler) - { - args[0] = ((GenericProxyInvocationHandler)paramInvHandler).getCurrent(); // reemplazamos el Proxy por el objeto asociado - } - } - } - } - - return method.invoke(obj, args); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyVersionedObject.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyVersionedObject.java deleted file mode 100644 index 10dc262..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyVersionedObject.java +++ /dev/null @@ -1,167 +0,0 @@ -package com.innowhere.relproxy.impl; - -import com.innowhere.relproxy.RelProxyException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author jmarranz - */ -public abstract class GenericProxyVersionedObject -{ - protected Object obj; - protected GenericProxyInvocationHandler parent; - - public GenericProxyVersionedObject(Object obj,GenericProxyInvocationHandler parent) - { - this.obj = obj; - this.parent = parent; - } - - protected static void getTreeFields(Class clasz,Object obj,ArrayList fieldList,ArrayList valueList) throws IllegalAccessException - { - getFields(clasz,obj,fieldList,valueList); - Class superClass = clasz.getSuperclass(); - if (superClass != null) - getTreeFields(superClass,obj,fieldList,valueList); - } - - protected static void getFields(Class clasz,Object obj,ArrayList fieldList,ArrayList valueList) throws IllegalAccessException - { - Field[] fieldListClass = clasz.getDeclaredFields(); - for(int i = 0; i < fieldListClass.length; i++) - { - Field field = fieldListClass[i]; - fieldList.add(field); - if (valueList != null) - { - field.setAccessible(true); - Object value = field.get(obj); - valueList.add(value); - } - } - } - - public Object getCurrent() - { - return obj; - } - - public Object getNewVersion() throws Throwable - { - Class newClass = reloadClass(); - if (newClass == null) - return obj; - - Class oldClass = obj.getClass(); - if (newClass != oldClass) - { - this.obj = copy(oldClass,obj,newClass); - } - - return obj; - } - - private Object copy(Class oldClass,Object oldObj,Class newClass) throws IllegalAccessException, InstantiationException, IllegalArgumentException, InvocationTargetException - { - Object newObj; - - ArrayList fieldListOld = new ArrayList(); - ArrayList valueListOld = new ArrayList(); - - getTreeFields(oldClass,oldObj,fieldListOld,valueListOld); - - Class enclosingClassNew = newClass.getEnclosingClass(); - if (enclosingClassNew == null) - { - Constructor construc; - try - { - construc = newClass.getConstructor(new Class[0]); - } - catch(NoSuchMethodException ex) - { - throw new RelProxyException("Cannot reload " + newClass.getName() + " a default empty of params constructor is required",ex); - } - newObj = construc.newInstance(); - } - else - { - // En el caso de inner class o anonymous inner class el constructor por defecto se obtiene de forma diferente, útil para los EventListener de ItsNat - Constructor construc; - try - { - construc = newClass.getDeclaredConstructor(new Class[]{enclosingClassNew}); - } - catch(NoSuchMethodException ex) // Yo creo que nunca ocurre al menos no en anonymous inner classes pero por si acaso - { - throw new RelProxyException("Cannot reload " + newClass.getName() + " a default empty of params constructor is required",ex); - } - construc.setAccessible(true); // Necesario - - // http://stackoverflow.com/questions/1816458/getting-hold-of-the-outer-class-object-from-the-inner-class-object - - - Field enclosingFieldOld; - try { enclosingFieldOld = oldClass.getDeclaredField("this$0"); } - catch (NoSuchFieldException ex) { throw new RelProxyException(ex); } - enclosingFieldOld.setAccessible(true); - Object enclosingObjectOld = enclosingFieldOld.get(oldObj); - Object enclosingObjectNew = copy(enclosingObjectOld.getClass(),enclosingObjectOld,enclosingClassNew); - - newObj = construc.newInstance(enclosingObjectNew); - } - - - ArrayList fieldListNew = new ArrayList(); - - getTreeFields(newClass,newObj,fieldListNew,null); - - if (fieldListOld.size() != fieldListNew.size()) throw new RelProxyException("Cannot reload " + newClass.getName() + " number of fields have changed, redeploy"); - - for(int i = 0; i < fieldListOld.size(); i++) - { - Field fieldOld = fieldListOld.get(i); - Field fieldNew = fieldListNew.get(i); - if (enclosingClassNew != null && fieldOld.getName().equals("this$0") && fieldNew.getName().equals("this$0")) - continue; // Ya están correctamente definidos - - if ( (!ignoreField(fieldOld) && !fieldOld.getName().equals(fieldNew.getName())) || - !fieldOld.getType().equals(fieldNew.getType())) - throw new RelProxyException("Cannot reload " + newClass.getName() + " fields have changed, redeploy"); - - Object fieldObj = valueListOld.get(i); - fieldNew.setAccessible(true); - int modifiersNew = fieldNew.getModifiers(); - boolean isStaticFinal = Modifier.isStatic(modifiersNew) && Modifier.isFinal(modifiersNew); - Field modifiersField = null; - if (isStaticFinal) - { - // http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection - try { - modifiersField = Field.class.getDeclaredField("modifiers"); - } - catch (NoSuchFieldException ex) { throw new RelProxyException(ex); } - modifiersField.setAccessible(true); - modifiersField.setInt(fieldNew, fieldNew.getModifiers() & ~Modifier.FINAL); // Quitamos el modifier final - } - - fieldNew.set(newObj, fieldObj); - - if (modifiersField != null) - { - modifiersField.setInt(fieldNew, fieldNew.getModifiers() & ~Modifier.FINAL); // Restauramos el modifier final - } - } - return newObj; - } - - protected abstract Class reloadClass(); - protected abstract boolean ignoreField(Field field); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/GProxyConfigImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/GProxyConfigImpl.java deleted file mode 100644 index 95fa423..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/GProxyConfigImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.innowhere.relproxy.impl.gproxy; - -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.gproxy.GProxyConfig; -import com.innowhere.relproxy.gproxy.GProxyGroovyScriptEngine; -import com.innowhere.relproxy.impl.GenericProxyConfigBaseImpl; - -/** - * - * @author jmarranz - */ -public class GProxyConfigImpl extends GenericProxyConfigBaseImpl implements GProxyConfig -{ - protected GProxyGroovyScriptEngine engine; - - public GProxyConfig setEnabled(boolean enabled) - { - this.enabled = enabled; - return this; - } - - public GProxyConfig setRelProxyOnReloadListener(RelProxyOnReloadListener relListener) - { - this.relListener = relListener; - return this; - } - - public GProxyConfig setGProxyGroovyScriptEngine(GProxyGroovyScriptEngine engine) - { - this.engine = engine; - return this; - } - - public GProxyGroovyScriptEngine getGProxyGroovyScriptEngine() - { - return engine; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/GProxyDefaultImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/GProxyDefaultImpl.java deleted file mode 100644 index f47f734..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/GProxyDefaultImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.innowhere.relproxy.impl.gproxy; - -import com.innowhere.relproxy.gproxy.GProxyConfig; -import com.innowhere.relproxy.impl.gproxy.core.GProxyImpl; - -/** - * - * @author jmarranz - */ -public class GProxyDefaultImpl extends GProxyImpl -{ - public static GProxyConfig createGProxyConfig() - { - return new GProxyConfigImpl(); - } - - public static void initStatic(GProxyConfigImpl config) - { - if (!config.isEnabled()) return; - - checkSingletonNull(SINGLETON); - SINGLETON = new GProxyDefaultImpl(); - SINGLETON.init(config); - } - - public static T createStatic(T obj,Class clasz) - { - if (SINGLETON == null) - return obj; // No se ha llamado al init o enabled = false - - return SINGLETON.create(obj, clasz); - } - - public static Object createStatic(Object obj,Class[] classes) - { - if (SINGLETON == null) - return obj; // No se ha llamado al init o enabled = false - - return SINGLETON.create(obj, classes); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyImpl.java deleted file mode 100644 index 25064d4..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.innowhere.relproxy.impl.gproxy.core; - -import com.innowhere.relproxy.impl.gproxy.GProxyConfigImpl; -import com.innowhere.relproxy.gproxy.GProxyGroovyScriptEngine; -import com.innowhere.relproxy.impl.GenericProxyImpl; -import com.innowhere.relproxy.impl.GenericProxyInvocationHandler; - - -/** - * - * @author jmarranz - */ -public abstract class GProxyImpl extends GenericProxyImpl -{ - public static GProxyImpl SINGLETON; - protected GProxyGroovyScriptEngine engine; - - public void init(GProxyConfigImpl config) - { - super.init(config); - this.engine = config.getGProxyGroovyScriptEngine(); - } - - public GProxyGroovyScriptEngine getGProxyGroovyScriptEngine() - { - return engine; - } - - @Override - public GenericProxyInvocationHandler createGenericProxyInvocationHandler(Object obj) - { - return new GProxyInvocationHandler(obj,this); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyInvocationHandler.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyInvocationHandler.java deleted file mode 100644 index 439a081..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyInvocationHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.innowhere.relproxy.impl.gproxy.core; - -import com.innowhere.relproxy.impl.GenericProxyInvocationHandler; - -/** - * - * @author jmarranz - */ -public class GProxyInvocationHandler extends GenericProxyInvocationHandler -{ - public GProxyInvocationHandler(Object obj,GProxyImpl root) - { - super(root); - this.verObj = new GProxyVersionedObject(obj,this); - } - - public GProxyImpl getGProxyImpl() - { - return (GProxyImpl)root; - } - -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyVersionedObject.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyVersionedObject.java deleted file mode 100644 index d1a8106..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/gproxy/core/GProxyVersionedObject.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.innowhere.relproxy.impl.gproxy.core; - -import com.innowhere.relproxy.gproxy.GProxyGroovyScriptEngine; -import com.innowhere.relproxy.impl.GenericProxyVersionedObject; -import java.lang.reflect.Field; - -/** - * - * @author jmarranz - */ -public class GProxyVersionedObject extends GenericProxyVersionedObject -{ - protected String path; - - public GProxyVersionedObject(Object obj,GProxyInvocationHandler parent) - { - super(obj,parent); - this.path = obj.getClass().getName().replace('.','/'); - } - - - public GProxyInvocationHandler getGProxyInvocationHandler() - { - return (GProxyInvocationHandler)parent; - } - - @Override - protected Class reloadClass() - { - GProxyGroovyScriptEngine engine = getGProxyInvocationHandler().getGProxyImpl().getGProxyGroovyScriptEngine(); - - try - { - return engine.loadScriptByName(path + ".groovy"); //Ej: example/groovyex/GroovyExampleLoadListener.groovy - } - catch(Exception ex) - { - ex.printStackTrace(System.err); - return null; - } - } - - @Override - protected boolean ignoreField(Field field) - { - return field.getName().startsWith("__timeStamp__"); // Este atributo cambia de nombre en cada reload, no lo consideramos - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyConfigImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyConfigImpl.java deleted file mode 100644 index b435a8a..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyConfigImpl.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.impl.GenericProxyConfigBaseImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyConfig; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import java.io.File; - -/** - * - * @author jmarranz - */ -public class JProxyConfigImpl extends GenericProxyConfigBaseImpl implements JProxyConfig -{ - protected File folderSources; - protected FolderSourceList folderSourceList; - protected FolderSourceList requiredExtraJarPaths; - protected JProxyInputSourceFileExcludedListener excludedListener; - protected JProxyCompilerListener compilerListener; - protected String classFolder; - protected long scanPeriod = -1; - protected Iterable compilationOptions; - protected JProxyDiagnosticsListener diagnosticsListener; - protected boolean test = false; - - @Override - public JProxyConfig setEnabled(boolean enabled) - { - this.enabled = enabled; - return this; - } - - @Override - public JProxyConfig setRelProxyOnReloadListener(RelProxyOnReloadListener relListener) - { - this.relListener = relListener; - return this; - } - - @Override - public JProxyConfig setInputPath(String inputPath) - { - setInputPaths(inputPath != null ? new String[]{inputPath} : null); // inputPath es null en el caso de shell interactive - return this; - } - - @Override - public JProxyConfig setInputPaths(String[] inputPaths) - { - this.folderSourceList = new FolderSourceList(inputPaths,true); // inputPaths es null en el caso de shell interactive - return this; - } - - @Override - public JProxyConfig setRequiredExtraJarPaths(String[] inputJarPaths) - { - this.requiredExtraJarPaths = new FolderSourceList(inputJarPaths,false); // inputPaths es null en el caso de shell interactive - return this; - } - - @Override - public JProxyConfig setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener excludedListener) - { - this.excludedListener = excludedListener; - return this; - } - - @Override - public JProxyConfig setJProxyCompilerListener(JProxyCompilerListener compilerListener) - { - this.compilerListener = compilerListener; - return this; - } - - @Override - public JProxyConfig setClassFolder(String classFolder) - { - this.classFolder = classFolder; - return this; - } - - @Override - public JProxyConfig setScanPeriod(long scanPeriod) - { - if (scanPeriod == 0) throw new RelProxyException("scanPeriod cannot be zero"); - this.scanPeriod = scanPeriod; - return this; - } - - @Override - public JProxyConfig setCompilationOptions(Iterable compilationOptions) - { - this.compilationOptions = compilationOptions; - return this; - } - - @Override - public JProxyConfig setJProxyDiagnosticsListener(JProxyDiagnosticsListener diagnosticsListener) - { - this.diagnosticsListener = diagnosticsListener; - return this; - } - - public FolderSourceList getFolderSourceList() - { - return folderSourceList; - } - - public FolderSourceList getRequiredExtraJarPaths() - { - return requiredExtraJarPaths; - } - - public JProxyInputSourceFileExcludedListener getJProxyInputSourceFileExcludedListener() - { - return excludedListener; - } - - public JProxyCompilerListener getJProxyCompilerListener() - { - return compilerListener; - } - - public String getClassFolder() - { - return classFolder; - } - - public long getScanPeriod() - { - return scanPeriod; - } - - public Iterable getCompilationOptions() - { - return compilationOptions; - } - - public JProxyDiagnosticsListener getJProxyDiagnosticsListener() - { - return diagnosticsListener; - } - - public boolean isTest() - { - return test; - } - - public void setTest(boolean test) - { - this.test = test; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyDefaultImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyDefaultImpl.java deleted file mode 100644 index 1fa606d..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyDefaultImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy; - -import com.innowhere.relproxy.impl.jproxy.core.JProxyImpl; -import com.innowhere.relproxy.jproxy.JProxyConfig; - -/** - * - * @author jmarranz - */ -public class JProxyDefaultImpl extends JProxyImpl -{ - public JProxyDefaultImpl() - { - } - - @Override - public Class getMainParamClass() - { - return null; - } - - public static JProxyConfig createJProxyConfig() - { - return new JProxyConfigImpl(); - } - - public static void initStatic(JProxyConfigImpl config) - { - if (!config.isEnabled()) return; - - checkSingletonNull(SINGLETON); - SINGLETON = new JProxyDefaultImpl(); - SINGLETON.init(config); - } - - - public static T createStatic(T obj,Class clasz) - { - if (SINGLETON == null) - return obj; // No se ha llamado al init o enabled = false - - return SINGLETON.create(obj, clasz); - } - - public static Object createStatic(Object obj,Class[] classes) - { - if (SINGLETON == null) - return obj; // No se ha llamado al init o enabled = false - - return SINGLETON.create(obj, classes); - } - - - public static boolean isEnabledStatic() - { - if (SINGLETON == null) - return false; - - return SINGLETON.isEnabled(); - } - - - public static boolean isRunningStatic() - { - if (SINGLETON == null) - return false; - - return SINGLETON.isRunning(); - } - - public static boolean stopStatic() - { - if (SINGLETON == null) - return false; - - return SINGLETON.stop(); - } - - public static boolean startStatic() - { - if (SINGLETON == null) - return false; - - return SINGLETON.start(); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyUtil.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyUtil.java deleted file mode 100644 index cfc9873..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/JProxyUtil.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy; - -import com.innowhere.relproxy.RelProxyException; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.net.URLConnection; - -/** - * - * @author jmarranz - */ -public class JProxyUtil -{ - public static String getCanonicalPath(File file) - { - try - { - return file.getCanonicalPath(); - } - catch (IOException ex) - { - throw new RelProxyException(ex); - } - } - - public static String getFileExtension(File file) - { - String path = file.getAbsolutePath(); - int pos = path.lastIndexOf('.'); - if (pos != -1) - return path.substring(pos + 1); - return ""; - } - - public static File getParentDir(File file) - { - return file.getParentFile(); - } - - public static byte[] readURL(URL url) - { - URLConnection urlCon; - try - { - urlCon = url.openConnection(); - return readInputStream(urlCon.getInputStream()); - } - catch (IOException ex) { throw new RelProxyException(ex); } - } - - public static byte[] readFile(File file) - { - FileInputStream fis = null; - try - { - fis = new FileInputStream(file); - } - catch (FileNotFoundException ex) - { - throw new RelProxyException(ex); - } - - return readInputStream(fis); - } - - public static byte[] readInputStream(InputStream is) - { - return readInputStream(is,50); // 50Kb => unas 100 lecturas 5 Mb - } - - public static byte[] readInputStream(InputStream is,int bufferSizeKb) - { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try - { - byte[] buffer = new byte[bufferSizeKb*1024]; - - int size; - while( (size = is.read(buffer)) != -1 ) - { - out.write(buffer, 0, size); - } - } - catch (IOException ex) - { - throw new RelProxyException(ex); - } - finally - { - try { is.close(); } catch (IOException ex2) { throw new RelProxyException(ex2); } - } - - return out.toByteArray(); - } - - public static void saveFile(File file,byte[] content) - { - File parent = getParentDir(file); - if (parent != null) parent.mkdirs(); - FileOutputStream out = null; - try - { - out = new FileOutputStream(file); - out.write(content, 0, content.length); - } - catch (IOException ex) - { - throw new RelProxyException(ex); - } - finally - { - if (out != null) try { out.close(); } catch (IOException ex2) { throw new RelProxyException(ex2); } - } - } - - public static String readTextFile(File file,String encoding) - { - Reader reader = null; - try - { - reader = new InputStreamReader(new FileInputStream(file),encoding); // FileReader no permite especificar el encoding y total no hace nada que no haga InputStreamReader - } - catch(IOException ex) { throw new RelProxyException(ex); } - - return readTextFile(reader); - } - - public static String readTextFile(Reader reader) - { - BufferedReader br = null; - try - { - br = new BufferedReader(reader); // FileReader no permite especificar el encoding y total no hace nada que no haga InputStreamReader - StringBuilder sb = new StringBuilder(); - String line = br.readLine(); - - while (line != null) - { - sb.append(line); - sb.append('\n'); - line = br.readLine(); - } - return sb.toString(); - } - catch(IOException ex) - { - throw new RelProxyException(ex); - } - finally - { - if (br != null) try { br.close(); } catch (IOException ex) { throw new RelProxyException(ex); } - } - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyImpl.java deleted file mode 100644 index 4d41301..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core; - -import com.innowhere.relproxy.impl.GenericProxyImpl; -import com.innowhere.relproxy.impl.GenericProxyInvocationHandler; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; - -/** - * - * @author jmarranz - */ -public abstract class JProxyImpl extends GenericProxyImpl -{ - public static JProxyImpl SINGLETON; - protected JProxyEngine engine; - - - protected JProxyImpl() - { - } - - public static ClassLoader getDefaultClassLoader() - { - return Thread.currentThread().getContextClassLoader(); - } - - public ClassDescriptorSourceScript init(JProxyConfigImpl config) - { - return init(config,null,null); - } - - public ClassDescriptorSourceScript init(JProxyConfigImpl config,SourceScriptRoot scriptFile,ClassLoader classLoader) - { - super.init(config); - - FolderSourceList folderSourceList = config.getFolderSourceList(); - FolderSourceList requiredExtraJarPaths = config.getRequiredExtraJarPaths(); - JProxyInputSourceFileExcludedListener excludedListener = config.getJProxyInputSourceFileExcludedListener(); - JProxyCompilerListener compilerListener = config.getJProxyCompilerListener(); - String classFolder = config.getClassFolder(); - long scanPeriod = config.getScanPeriod(); - Iterable compilationOptions = config.getCompilationOptions(); - JProxyDiagnosticsListener diagnosticsListener = config.getJProxyDiagnosticsListener(); - boolean enabled = config.isEnabled(); - - classLoader = classLoader != null ? classLoader : getDefaultClassLoader(); - this.engine = new JProxyEngine(this,enabled,scriptFile,classLoader,folderSourceList,requiredExtraJarPaths,classFolder,scanPeriod,excludedListener,compilerListener,compilationOptions,diagnosticsListener); - - return engine.init(); - } - - public JProxyEngine getJProxyEngine() - { - return engine; - } - - public boolean isEnabled() - { - return engine.isEnabled(); - } - - public boolean isRunning() - { - return engine.isRunning(); - } - - public boolean stop() - { - return engine.stop(); - } - - public boolean start() - { - return engine.start(); - } - - @Override - public GenericProxyInvocationHandler createGenericProxyInvocationHandler(Object obj) - { - return new JProxyInvocationHandler(obj,this); - } - - public abstract Class getMainParamClass(); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyInvocationHandler.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyInvocationHandler.java deleted file mode 100644 index 408b746..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyInvocationHandler.java +++ /dev/null @@ -1,23 +0,0 @@ - -package com.innowhere.relproxy.impl.jproxy.core; - -import com.innowhere.relproxy.impl.GenericProxyInvocationHandler; - -/** - * - * @author jmarranz - */ -public class JProxyInvocationHandler extends GenericProxyInvocationHandler -{ - public JProxyInvocationHandler(Object obj,JProxyImpl root) - { - super(root); - this.verObj = new JProxyVersionedObject(obj,this); - } - - public JProxyImpl getJProxyImpl() - { - return (JProxyImpl)root; - } - -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyVersionedObject.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyVersionedObject.java deleted file mode 100644 index ce0c888..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/JProxyVersionedObject.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core; - -import com.innowhere.relproxy.impl.GenericProxyVersionedObject; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import java.lang.reflect.Field; - -/** - * - * @author jmarranz - */ -public class JProxyVersionedObject extends GenericProxyVersionedObject -{ - protected String className; - - public JProxyVersionedObject(Object obj,JProxyInvocationHandler parent) - { - super(obj,parent); - this.className = obj.getClass().getName(); - } - - public JProxyInvocationHandler getJProxyInvocationHandler() - { - return (JProxyInvocationHandler)parent; - } - - @Override - protected Class reloadClass() - { - JProxyEngine engine = getJProxyInvocationHandler().getJProxyImpl().getJProxyEngine(); - engine.reloadWhenChanged(); - return (Class)engine.findClass(className); - } - - @Override - protected boolean ignoreField(Field field) - { - return false; // Todos cuentan (útil en Groovy no en Java) - } -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/FolderSourceList.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/FolderSourceList.java deleted file mode 100644 index 248a714..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/FolderSourceList.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.FileExt; -import java.io.File; - -/** - * - * @author jmarranz - */ -public class FolderSourceList -{ - protected FileExt[] sourceList; - - public FolderSourceList(String[] sourcePathList,boolean expectedDirectory) - { - if (sourcePathList != null) // En el caso de shell interactivo es null - { - // El convertir siempre a File los paths es para normalizar paths - this.sourceList = new FileExt[sourcePathList.length]; - for(int i = 0; i < sourcePathList.length; i++) - { - File folder = new File(sourcePathList[i]); - if (!folder.exists()) - throw new RelProxyException("Source folder does not exist: " + folder.getAbsolutePath()); - boolean isDirectory = folder.isDirectory(); - if (expectedDirectory) - { - if (!isDirectory) - throw new RelProxyException("Source folder is not a directory: " + folder.getAbsolutePath()); - } - else - { - if (isDirectory) - throw new RelProxyException("Expected a file not a directory: " + folder.getAbsolutePath()); - } - sourceList[i] = new FileExt(folder); - } - } - } - - public FileExt[] getArray() - { - return sourceList; - } - - public String buildClassNameFromFile(FileExt sourceFile) - { - for(FileExt rootFolderOfSources : sourceList) - { - String className = buildClassNameFromFile(sourceFile,rootFolderOfSources); - if (className != null) - return className; - } - throw new RelProxyException("File not found in source folders: " + sourceFile.getFile().getAbsolutePath()); - } - - public static String buildClassNameFromFile(FileExt sourceFile,FileExt rootFolderOfSources) - { - String path = sourceFile.getCanonicalPath(); - - String rootFolderOfSourcesAbsPath = rootFolderOfSources.getCanonicalPath(); - int pos = path.indexOf(rootFolderOfSourcesAbsPath); - if (pos == 0) // Está en este source folder - { - path = path.substring(rootFolderOfSourcesAbsPath.length() + 1); // Sumamos +1 para quitar también el / separador del pathInput y el path relativo de la clase - // Puede no tener extensión (script) o bien ser .java o bien ser una inventada (ej .jsh), la quitamos si existe - pos = path.lastIndexOf('.'); - if (pos != -1) - path = path.substring(0, pos); - path = path.replace(File.separatorChar, '.'); // getAbsolutePath() normaliza con el caracter de la plataforma - return path; - } - return null; - } - - - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyEngine.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyEngine.java deleted file mode 100644 index ca7aedc..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyEngine.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorInner; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.JProxyImpl; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import java.util.LinkedList; -import java.util.Timer; -import java.util.TimerTask; - -/** - * - * @author jmarranz - */ -public class JProxyEngine -{ - protected final Object monitor = new Object(); // Podríamos usar este objeto JProxyEngine directamente pero el monitor es mejor para análisis de dependencias - protected final JProxyImpl parent; - protected final JProxyEngineChangeDetectorAndCompiler delegateChangeDetector; - protected final ClassLoader rootClassLoader; - protected JProxyClassLoader customClassLoader; - protected final long scanPeriod; - protected final String sourceEncoding = "UTF-8"; // Por ahora, provisional - public volatile boolean stop = false; - protected TimerTask task; - protected boolean pendingReload = false; - protected final boolean enabled; - - public JProxyEngine(JProxyImpl parent,boolean enabled,SourceScriptRoot scriptFile,ClassLoader rootClassLoader,FolderSourceList folderSourceList,FolderSourceList requiredExtraJarPaths, - String folderClasses,long scanPeriod,JProxyInputSourceFileExcludedListener excludedListener, - JProxyCompilerListener compilerListener,Iterable compilationOptions,JProxyDiagnosticsListener diagnosticsListener) - { - this.parent = parent; - this.enabled = enabled; - this.rootClassLoader = rootClassLoader; - this.scanPeriod = scanPeriod; - this.delegateChangeDetector = new JProxyEngineChangeDetectorAndCompiler(this,scriptFile,folderSourceList,requiredExtraJarPaths,folderClasses,excludedListener,compilationOptions,diagnosticsListener,compilerListener); - this.customClassLoader = null; //new JProxyClassLoader(this); - } - - public Object getMonitor() - { - return monitor; - } - - public JProxyImpl getJProxy() - { - return parent; - } - - public boolean isEnabled() - { - return enabled; - } - - public ClassDescriptorSourceScript init() - { - synchronized(getMonitor()) - { - ClassDescriptorSourceScript scriptFileDesc = detectChangesInSources(); // Primera vez para detectar cambios en los .java respecto a los .class mientras el servidor estaba parado - - reloadWhenChanged(); // La primera vez cargamos pues el código fuente manda sobre los .class - - startScanner(); - - return scriptFileDesc; - } - } - - /* - public JProxyClassLoader getJProxyClassLoader() - { - return customClassLoader; - } - */ - - public ClassLoader getCurrentClassLoader() - { - if (customClassLoader != null) - return customClassLoader; - return rootClassLoader; - } - - private boolean startScanner() - { - if (scanPeriod > 0) // Si es 0 o negativo sólo se recargan una vez (la inicial ya ejecutada) - { - this.task = new TimerTask() - { - @Override - public void run() - { - if (stop) - { - cancel(); - return; - } - - try - { - detectChangesInSources(); // Está sincronizado las partes que lo necesitan - } - catch(Exception ex) - { - ex.printStackTrace(System.err); // Si dejamos subir la excepción se acabó el timer - } - } - }; - - new Timer().schedule(task, scanPeriod, scanPeriod); // Ojo, después de la primera llamada a detectChangesInSources() - return true; - } - else - { - return false; - } - } - - public void setPendingReload() - { - this.pendingReload = true; - } - - - public ClassLoader getRootClassLoader() - { - return rootClassLoader; - } - - public String getSourceEncoding() - { - return sourceEncoding; - } - - public boolean isRunning() - { - synchronized(getMonitor()) - { - return task != null && scanPeriod > 0; - } - } - - public boolean stop() - { - synchronized(getMonitor()) - { - if (task != null) - { - this.stop = true; - task.cancel(); - this.task = null; - return true; - } - else - { - return false; - } - } - } - - public boolean start() - { - synchronized(getMonitor()) - { - if (task == null) - { - this.stop = false; - return startScanner(); - } - else return false; - } - } - - - - public ClassDescriptor getClassDescriptor(String className) - { - synchronized(getMonitor()) - { - return delegateChangeDetector.getClassDescriptor(className); - } - } - - public Class findClass(String className) - { - // Si ya está cargada la devuelve, y si no se cargó por ningún JProxyClassLoader se intenta cargar por el parent ClassLoader, por lo que siempre devolverá distinto de null si la clase está en el classpath, que debería ser lo normal - synchronized(getMonitor()) - { - try - { - if (customClassLoader != null) - return customClassLoader.findClass(className); - else - return rootClassLoader.loadClass(className); - } - catch (ClassNotFoundException ex) - { - return null; - } - } - } - - private void addNewClassLoader() - { - ClassDescriptorSourceFileRegistry sourceRegistry = delegateChangeDetector.getClassDescriptorSourceFileRegistry(); - - for(ClassDescriptorSourceUnit sourceFile : sourceRegistry.getClassDescriptorSourceFileColl()) - { - sourceFile.resetLastLoadedClass(); // resetea también las innerclasses - } - - this.customClassLoader = new JProxyClassLoader(this); - } - - - - private Class reloadSource(ClassDescriptorSourceUnit sourceFile) - { - Class clasz = customClassLoader.loadClass(sourceFile,true); - reloadInnerClassesOnly(sourceFile,clasz); - return clasz; - } - - private void reloadInnerClassesOnly(ClassDescriptorSourceUnit sourceFile,Class classParent) - { - - LinkedList innerClassDescList = sourceFile.getInnerClassDescriptors(); - if (innerClassDescList != null && !innerClassDescList.isEmpty()) - { - // En el caso de una clase que ha sido compilada, las inner classes se descubren todas - for(ClassDescriptorInner innerClassDesc : innerClassDescList) - { - customClassLoader.loadClass(innerClassDesc,true); - } - } - else // Auto-Detección de innerclasses: puede ser un archivo fuente que posiblemente nunca se hayan tocado desde la carga inicial y por tanto quizás se desconocen las innerclasses - { - // Aprovechando la carga de la clase, hacemos el esfuerzo de cargar todas las clases dependientes lo más posible - classParent.getDeclaredClasses(); // Provoca que las inner clases miembro indirectamente se procesen y carguen a través del JProxyClassLoader de la clase padre clasz - - // Ahora bien, lo anterior NO sirve para las anonymous inner classes, afortunadamente en ese caso podemos conocer y cargar por fuerza bruta - // http://stackoverflow.com/questions/1654889/java-reflection-how-can-i-retrieve-anonymous-inner-classes?rq=1 - - for(int i = 1; i < Integer.MAX_VALUE; i++) // No te asustes por el MAX_VALUE, se parará tras unos poquitos ciclos - { - String anonClassName = sourceFile.getClassName() + "$" + i; - Class innerClasz = customClassLoader.loadInnerClass(sourceFile,anonClassName); - if (innerClasz == null) break; // No hay más o no hay ninguna (si i es 1) - } - - // ¿Qué es lo que queda por cargar pero que no podemos hacer explícitamente? - // 1) Las clases privadas autónomas que fueron definidas en el mismo archivo que la clase principal: no las soportamos pues no podemos identificar en el ClassLoader que es una clase "hot reloadable", no son inner classes en el sentido estricto - // 2) Las clases privadas "inner" locales, es decir no anónimas declaradas dentro de un método, se cargarán la primera vez que se usen, no podemos conocerlas a priori - // porque siguen la notación className$NclassName ej. JReloadExampleDocument$1AuxMemberInMethod. No hay problema con que se carguen con un class loader antiguo pues - // el ClassLoader de la clase padre contenedora será el encargado de cargarla en cuanto se pase por el método que la declara. - } - } - - public ClassDescriptorSourceScript detectChangesInSources() - { - return delegateChangeDetector.detectChangesInSources(); - } - - public ClassDescriptorSourceScript detectChangesInSourcesAndReload() - { - ClassDescriptorSourceScript res = detectChangesInSources(); - reloadWhenChanged(); - return res; - } - - public boolean reloadWhenChanged() - { - synchronized(getMonitor()) - { - if (pendingReload) - { - addNewClassLoader(); - - ClassDescriptorSourceFileRegistry sourceRegistry = delegateChangeDetector.getClassDescriptorSourceFileRegistry(); - - for(ClassDescriptorSourceUnit sourceFile : sourceRegistry.getClassDescriptorSourceFileColl()) // sourceRegistry NUNCA es nulo pues se ejecuta una primera vez en tiempo de inicialización - { - // if (sourceFilesCompiled.contains(sourceFile)) - // continue; - // las clases deleted no están en sourceFileMap por lo que no hay que filtrarlas - reloadSource(sourceFile); // Ponemos detectInnerClasses a true porque son archivos fuente que posiblemente nunca se hayan tocado desde la carga inicial y por tanto quizás se desconocen las innerclasses - } - - this.pendingReload = false; - return true; - } - return false; - } - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JavaSourcesSearch.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JavaSourcesSearch.java deleted file mode 100644 index 8c7b3a6..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JavaSourcesSearch.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootFileJavaExt; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileJava; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import java.io.File; -import java.net.URL; -import java.util.LinkedList; - -/** - * - * @author jmarranz - */ -public class JavaSourcesSearch -{ - protected final JProxyEngineChangeDetectorAndCompiler parent; - - public JavaSourcesSearch(JProxyEngineChangeDetectorAndCompiler parent) - { - this.parent = parent; - } - - public JProxyEngineChangeDetectorAndCompiler getJProxyEngineChangeDetectorAndCompiler() - { - return parent; - } - - public ClassDescriptorSourceScript sourceFileSearch(boolean firstTime,SourceScriptRoot scriptFile,ClassDescriptorSourceFileRegistry sourceRegistry,LinkedList updatedSourceFiles,LinkedList newSourceFiles) - { - ClassDescriptorSourceScript scriptFileDesc = (scriptFile == null) ? null : processSourceFileScript(firstTime,scriptFile,sourceRegistry,updatedSourceFiles,newSourceFiles); - FileExt[] folderSourceList = parent.getFolderSourceList().getArray(); - if (folderSourceList == null) // Es el caso de shell interactivo o code snippet - return scriptFileDesc; - - boolean allEmpty = true; - - String scriptFileJavaCannonPath = (scriptFile != null && (scriptFile instanceof SourceScriptRootFileJavaExt)) ? ((SourceScriptRootFileJavaExt)scriptFile).getFileExt().getCanonicalPath() : null; - - for(int i = 0; i < folderSourceList.length; i++) - { - FileExt rootFolderOfSources = folderSourceList[i]; - String[] children = rootFolderOfSources.getFile().list(); - if (children == null) continue; // El que ha configurado los rootFolders es tonto y ha puesto alguno nulo o no es válido el path - if (children.length == 0) continue; // Empty - - if (allEmpty) allEmpty = false; - recursiveSourceFileJavaSearch(firstTime,scriptFileJavaCannonPath, i ,rootFolderOfSources,children,sourceRegistry,updatedSourceFiles,newSourceFiles); - } - - if (allEmpty) - throw new RelProxyException("All specified input source folders are empty"); - - return scriptFileDesc; - } - - private void recursiveSourceFileJavaSearch(boolean firstTime,String scriptFileJavaCannonPath,int rootFolderOfSourcesIndex,FileExt parentPath,String[] relPathList,ClassDescriptorSourceFileRegistry sourceRegistry,LinkedList updatedSourceFiles,LinkedList newSourceFiles) - { - FileExt rootFolderOfSources = parent.getFolderSourceList().getArray()[rootFolderOfSourcesIndex]; - JProxyInputSourceFileExcludedListener listener = parent.getJProxyInputSourceFileExcludedListener(); - - for(String relPath : relPathList) - { - File file = new File(parentPath.getCanonicalPath() + "/" + relPath); - FileExt fileExt = new FileExt(file); - if (file.isDirectory()) - { - if (listener != null && listener.isExcluded(file,rootFolderOfSources.getFile())) - continue; - - String[] children = file.list(); // Si está vacío el array está vacío pero existe - recursiveSourceFileJavaSearch(firstTime,scriptFileJavaCannonPath,rootFolderOfSourcesIndex,fileExt,children,sourceRegistry,updatedSourceFiles,newSourceFiles); - } - else - { - String ext = JProxyUtil.getFileExtension(file); // Si no tiene extensión devuelve "" - if (!"java".equals(ext)) continue; - //if (!"jsh".equals(ext)) continue; - - String cannonPath = JProxyUtil.getCanonicalPath(file); - if (scriptFileJavaCannonPath != null && scriptFileJavaCannonPath.equals(cannonPath)) - continue; // Es el propio archivo script inicial que es .java, así evitamos considerarlo dos veces - - if (listener != null && listener.isExcluded(file,rootFolderOfSources.getFile())) - continue; - - SourceFileJavaNormal sourceFile = new SourceFileJavaNormal(fileExt,rootFolderOfSources); - processSourceFileJava(firstTime,sourceFile,sourceRegistry,updatedSourceFiles,newSourceFiles); - } - } - } - - private ClassDescriptorSourceScript processSourceFileScript(boolean firstTime,SourceScriptRoot file,ClassDescriptorSourceFileRegistry sourceRegistry,LinkedList updatedSourceFiles,LinkedList newSourceFiles) - { - return (ClassDescriptorSourceScript)processSourceFile(firstTime,file,true,sourceRegistry,updatedSourceFiles,newSourceFiles); - } - - private ClassDescriptorSourceFileJava processSourceFileJava(boolean firstTime,SourceFileJavaNormal file,ClassDescriptorSourceFileRegistry sourceRegistry,LinkedList updatedSourceFiles,LinkedList newSourceFiles) - { - return (ClassDescriptorSourceFileJava)processSourceFile(firstTime,file,false,sourceRegistry,updatedSourceFiles,newSourceFiles); - } - - private ClassDescriptorSourceUnit processSourceFile(boolean firstTime,SourceUnit file,boolean script,ClassDescriptorSourceFileRegistry sourceRegistry,LinkedList updatedSourceFiles,LinkedList newSourceFiles) - { - JProxyEngine engine = parent.getJProxyEngine(); - String className = file.getClassName(); - - long timestampSourceFile = file.lastModified(); - ClassDescriptorSourceUnit sourceFile; - if (!firstTime) - { - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - sourceFile = sourceRegistry.getClassDescriptorSourceUnit(className); - } - - if (sourceFile != null) // Cambiado - { - long oldTimestamp = sourceFile.getTimestamp(); - - if (timestampSourceFile > oldTimestamp) - { - synchronized(monitor) - { - sourceFile.updateTimestamp(timestampSourceFile); - } - updatedSourceFiles.add(sourceFile); - } - - sourceFile.setPendingToRemove( false ); // Encontrado, no se elimina porque sigue existiendo - } - else // Clase nueva - { - sourceFile = ClassDescriptorSourceUnit.create(script,engine,className,file,timestampSourceFile); - sourceFile.setPendingToRemove( false ); // Está ya por defecto pero para que quede claro - newSourceFiles.add(sourceFile); - } - } - else // Primera vez, vemos si el código fuente se ha cambiado respecto a los .class en el sistema de archivos - { - String relClassPath = ClassDescriptor.getRelativeClassFilePathFromClassName(className); - ClassLoader parentClassLoader = engine.getRootClassLoader(); - URL urlClass = parentClassLoader.getResource(relClassPath); - if (urlClass != null) - { - String urlClassExt = urlClass.toExternalForm(); - // Si el .class está en un JAR podríamos obtener el timestamp del archivo dentro del jar pero que haya un .java "fuera" reloadable indica que queremos "reemplazar" el del jar por lo que siempre se considerará que el archivo fuente ha sido modificado más reciente - long timestampCompiledClass = urlClassExt.startsWith("file:") ? new File(urlClass.getPath()).lastModified() : 0; // 0 cuando está en un JAR - - if (timestampSourceFile > timestampCompiledClass) - { - sourceFile = ClassDescriptorSourceUnit.create(script,engine,className,file,timestampSourceFile); - updatedSourceFiles.add(sourceFile); // Hay que recompilar -//System.out.println("UPDATED: " + className + " " + urlClass.toExternalForm() + " " + (timestampSourceFile - timestampCompiledClass)); - } - else - { - // Esto es lo normal en carga si no hemos tocado el código tras el deploy, que el .class sea más reciente que el .java - sourceFile = ClassDescriptorSourceUnit.create(script,engine,className,file,timestampCompiledClass); - byte[] classBytes = JProxyUtil.readURL(urlClass); - sourceFile.setClassBytes(classBytes); - // Falta cargar las posibles inner classes, hay que tener en cuenta que este archivo NO se va a compilar porque no ha cambiado respecto a .class conocido -//System.out.println("NOT UPDATED: " + className + " " + urlClass.toExternalForm() + " " + (timestampSourceFile - timestampCompiledClass)); - } - - } - else // No hay .class, es un archivo fuente nuevo creado antes de cargar la app web, hay que compilar si o si - { - sourceFile = ClassDescriptorSourceUnit.create(script,engine,className,file,timestampSourceFile); - newSourceFiles.add(sourceFile); - } - - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - sourceRegistry.addClassDescriptorSourceUnit(sourceFile); // El registro de archivos se hace por primera vez por lo que hay que añadirlos todos inicialmente, updatedSourceFiles y newSourceFiles indicarán en este caso los que hay que recompilar además - } - } - - return sourceFile; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptor.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptor.java deleted file mode 100644 index 233b2c3..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptor.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc; - -import java.io.File; - -/** - * - * @author jmarranz - */ -public abstract class ClassDescriptor -{ - protected final String className; // El nombre basado en puntos pero usando $ en el caso de innerclasses - protected final String simpleClassName; // className sin el package - protected final String packageName; // El package pero acabado en un "." o bien "" si no hay package, el motivo de acabar en un punto es simplemente para poder concatenar ciegamente el package y el simpleClassName - protected byte[] classBytes; - protected Class clasz; - - public ClassDescriptor(String className) - { - this.className = className; - int pos = className.lastIndexOf('.'); - this.simpleClassName = (pos != -1) ? className.substring(pos + 1) : className; - this.packageName = (pos != -1) ? className.substring(0,pos + 1) : ""; // SE INCLUYE EL . en el caso de existir package - } - - public abstract boolean isInnerClass(); - - public String getClassName() - { - return className; - } - - public String getSimpleClassName() - { - return simpleClassName; - } - - public String getPackageName() - { - return packageName; - } - - public byte[] getClassBytes() - { - return classBytes; - } - - public void setClassBytes(byte[] classBytes) - { - this.classBytes = classBytes; - } - - public Class getLastLoadedClass() - { - return clasz; - } - - public void setLastLoadedClass(Class clasz) - { - this.clasz = clasz; - } - - public void resetLastLoadedClass() - { - setLastLoadedClass(null); - } - - /* - public String getClassFileNameFromClassName() - { - return getClassFileNameFromClassName(className); - } - */ - - public static String getClassFileNameFromClassName(String className) - { - // Es válido también para las innerclasses (ej Nombre$Otro => Nombre$Otro.class, Nombre$1 => Nombre$1.class, Nombre$1Nombre => Nombre$1Nombre.class - int pos = className.lastIndexOf("."); - if(pos != -1) className = className.substring(pos + 1); - return className + ".class"; - } - - public static String getRelativeClassFilePathFromClassName(String className) - { - return className.replace('.','/') + ".class"; // alternativa: className.replaceAll("\\.", "/") + ".class" - } - - public static String getRelativePackagePathFromClassName(String className) - { - String packageName = className.replace('.','/'); - int pos = packageName.lastIndexOf('/'); - if (pos == -1) return packageName; - return packageName.substring(0,pos); - } - - public static File getAbsoluteClassFilePathFromClassNameAndClassPath(String className,String classPath) - { - String relativePath = getRelativeClassFilePathFromClassName(className); - classPath = classPath.trim(); - if (!classPath.endsWith("/") && !classPath.endsWith("\\")) classPath += File.separatorChar; - return new File(classPath + relativePath); - } - - public static String getClassNameFromRelativeClassFilePath(String path) - { - // Ej. org/w3c/dom/Element.class => org.w3c.dom.Element - String binaryName = path.replaceAll("/", "."); - return binaryName.replaceAll(".class$", ""); // El $ indica "el .class del final" - } - - public static String getClassNameFromPackageAndClassFileName(String packageName,String fileName) - { - String className = packageName + "." + fileName; - return className.replaceAll(".class$", ""); // El $ indica "el .class del final" - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorInner.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorInner.java deleted file mode 100644 index c1c41ba..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorInner.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc; - -/** - * - * @author jmarranz - */ -public class ClassDescriptorInner extends ClassDescriptor -{ - protected final ClassDescriptorSourceUnit parent; - - public ClassDescriptorInner(String className,ClassDescriptorSourceUnit parent) - { - super(className); - this.parent = parent; - } - - @Override - public boolean isInnerClass() - { - return true; - } - - public ClassDescriptorSourceUnit getClassDescriptorSourceUnit() - { - return parent; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileJava.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileJava.java deleted file mode 100644 index 598d539..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileJava.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc; - -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; - -/** - * - * @author jmarranz - */ -public class ClassDescriptorSourceFileJava extends ClassDescriptorSourceUnit -{ - public ClassDescriptorSourceFileJava(JProxyEngine engine,String className, SourceFileJavaNormal sourceFile, long timestamp) - { - super(engine,className, sourceFile, timestamp); - } - - public SourceFileJavaNormal getSourceFileJavaNormal() - { - return (SourceFileJavaNormal)sourceUnit; - } - - public FileExt getSourceFile() - { - return getSourceFileJavaNormal().getFileExt(); - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileRegistry.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileRegistry.java deleted file mode 100644 index d3108cd..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileRegistry.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; - -/** - * - * @author jmarranz - */ -public class ClassDescriptorSourceFileRegistry -{ - protected final Map sourceUnitMapByClassName; - - public ClassDescriptorSourceFileRegistry() - { - this.sourceUnitMapByClassName = new HashMap(); - } - - public ClassDescriptorSourceFileRegistry(ClassDescriptorSourceFileRegistry origin) - { - this.sourceUnitMapByClassName = new HashMap( origin.sourceUnitMapByClassName ); - } - - public boolean isEmpty() - { - return sourceUnitMapByClassName.isEmpty(); - } - - public Collection getClassDescriptorSourceFileColl() - { - return sourceUnitMapByClassName.values(); - } - - public ClassDescriptorSourceUnit getClassDescriptorSourceUnit(String className) - { - return sourceUnitMapByClassName.get(className); - } - - public ClassDescriptorSourceUnit removeClassDescriptorSourceUnit(String className) - { - return sourceUnitMapByClassName.remove(className); - } - - public void addClassDescriptorSourceUnit(ClassDescriptorSourceUnit sourceFile) - { - sourceUnitMapByClassName.put(sourceFile.getClassName(), sourceFile); - } - - public void setAllClassDescriptorSourceFilesPendingToRemove(boolean pending) - { - for(Map.Entry entries : sourceUnitMapByClassName.entrySet()) - entries.getValue().setPendingToRemove(pending); - } - - public LinkedList getAllClassDescriptorSourceFilesPendingToRemove(LinkedList deletedSourceFiles) - { - for(Map.Entry entries : sourceUnitMapByClassName.entrySet()) - { - ClassDescriptorSourceUnit classDesc = entries.getValue(); - boolean pending = classDesc.isPendingToRemove(); - if (pending) - deletedSourceFiles.add(classDesc); - } - return deletedSourceFiles; - } - - public ClassDescriptor getClassDescriptor(String className) - { - // Puede ser el de una innerclass - // Las innerclasses no están como tales en sourceFileMap pues sólo está la clase contenedora pero también la consideramos hotloadable - String parentClassName; - int pos = className.lastIndexOf('$'); - boolean inner; - if (pos != -1) - { - parentClassName = className.substring(0, pos); - inner = true; - } - else - { - parentClassName = className; - inner = false; - } - ClassDescriptorSourceUnit sourceDesc = sourceUnitMapByClassName.get(parentClassName); - if (sourceDesc == null) - return null; - if (!inner) return sourceDesc; - return sourceDesc.getInnerClassDescriptor(className,true); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceScript.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceScript.java deleted file mode 100644 index e4790e1..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceScript.java +++ /dev/null @@ -1,184 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.core.JProxyImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.LinkedList; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; - -/** - * - * @author jmarranz - */ -public class ClassDescriptorSourceScript extends ClassDescriptorSourceUnit -{ - protected String source; - - public ClassDescriptorSourceScript(JProxyEngine engine,String className,SourceScriptRoot sourceFile,long timestamp) - { - super(engine,className, sourceFile, timestamp); - - generateSourceCode(); - } - - - public SourceScriptRoot getSourceScript() - { - return (SourceScriptRoot)sourceUnit; - } - - private void generateSourceCode() - { - boolean[] hasHashBang = new boolean[1]; - - String scriptCode = getSourceScript().getScriptCode(getEncoding(),hasHashBang); - - boolean completeClass = isCompleteClass(scriptCode); - - StringBuilder finalCode = new StringBuilder(); - if (completeClass) - { - if (hasHashBang[0]) finalCode.append("\n"); // Como hemos quitado la línea #! añadimos una nueva para que los números de línea en caso de error coincidan con el original - finalCode.append(scriptCode); - } - else - { - JProxyImpl jproxy = engine.getJProxy(); - String mainParamsDec = null; - String mainReturnType = null; - - Class mainParamClass = jproxy.getMainParamClass(); - if (mainParamClass.equals(String[].class)) - { - mainParamsDec = "String[] args"; - mainReturnType = "void"; - } - else if (mainParamClass.equals(ScriptContext.class)) - { - mainParamsDec = ScriptEngine.class.getName() + " engine," + ScriptContext.class.getName() + " context"; - mainReturnType = "Object"; - - if (scriptCode.equals("")) scriptCode = "return null;"; - } - - finalCode.append("public class " + className + " { public static " + mainReturnType + " main(" + mainParamsDec + ") {\n"); // Lo ponemos todo en una línea para que en caso de error la línea de error coincida con el script original pues hemos quitado la primera línea #! - finalCode.append(scriptCode); - finalCode.append(" }\n"); - finalCode.append("}\n"); - } - this.source = finalCode.toString(); - } - - private boolean isCompleteClass(String code) - { - // Buscamos si hay un " class ..." o un "import..." al comienzo para soportar la definición de una clase completa como script - int pos = code.indexOf("class"); - if (pos == -1) return false; - // Hay al menos un "class", ojo que puede ser parte de una variable o dentro de un comentario, pero si no existiera desde luego que no es clase completa - - pos = getFirstPosIgnoringCommentsAndSeparators(code); - if (pos == -1) return false; - - // Lo primero que nos tenemos encontrar es un import o una declaración de class - int pos2 = code.indexOf("import",pos); - if (pos2 == pos) - return true; // Si hay un import hay declaración de clase - - // Vemos si es un "public class..." o similar - int posClass = code.indexOf("class", pos); - String visibility = code.substring(pos, posClass); - visibility = visibility.trim(); // No consideramos \n hay que ser retorcido poner un \n entre el public y el class por ejemplo - if (visibility.isEmpty()) return true; // No hay visibilidad, que no compile no es cosa nuestra - return ("private".equals(visibility) || "public".equals(visibility) || "protected".equals(visibility)); - } - - private int getFirstPosIgnoringCommentsAndSeparators(String code) - { - int i = -1; - for(i = 0; i < code.length(); i++) - { - char c = code.charAt(i); - if (c == ' ' || c == '\n' || c == '\t') continue; - else if (c == '/' && i + 1 < code.length()) - { - char c2 = code.charAt(i + 1); - if (c2 == '/') - { - i = getFirstPosIgnoringOneLineComment(code,i); - if (i == -1) return -1; // Comentario mal formado - } - else if (c2 == '*') - { - i = getFirstPosIgnoringMultiLineComment(code,i); - if (i == -1) return -1; // Comentario mal formado - } - } - else break; - } - return i; - } - - private int getFirstPosIgnoringOneLineComment(String code,int start) - { - return code.indexOf('\n',start); - } - - private int getFirstPosIgnoringMultiLineComment(String code,int start) - { - return code.indexOf("*/", start); - } - - @Override - public void updateTimestamp(long timestamp) - { - long oldTimestamp = this.timestamp; - if (oldTimestamp != timestamp) - generateSourceCode(); - super.updateTimestamp(timestamp); - } - - public String getSourceCode() - { - return source; - } - - public void callMainMethod(LinkedList argsToScript) throws Throwable - { - try - { - Class scriptClass = getLastLoadedClass(); - Method method = scriptClass.getDeclaredMethod("main",new Class[]{ String[].class }); - String[] argsToScriptArr = argsToScript.size() > 0 ? argsToScript.toArray(new String[argsToScript.size()]) : new String[0]; - method.invoke(null, new Object[]{ argsToScriptArr }); - } - catch (IllegalAccessException ex) { throw new RelProxyException(ex); } - catch (NoSuchMethodException ex) { throw new RelProxyException(ex); } - catch (SecurityException ex) { throw new RelProxyException(ex); } - catch (IllegalArgumentException ex) { throw new RelProxyException(ex); } - catch (InvocationTargetException ex) { throw ex.getCause(); } // Los errores de ejecución se envuelven en un InvocationTargetException - } - - public Object callMainMethod(ScriptEngine engine,ScriptContext context) throws Throwable - { - Class scriptClass = getLastLoadedClass(); - return callMainMethod(scriptClass,engine,context); - } - - public static Object callMainMethod(Class scriptClass,ScriptEngine engine,ScriptContext context) throws Throwable - { - try - { - Method method = scriptClass.getDeclaredMethod("main",new Class[]{ ScriptEngine.class,ScriptContext.class }); - return method.invoke(null, new Object[]{ engine, context }); - } - catch (IllegalAccessException ex) { throw new RelProxyException(ex); } - catch (NoSuchMethodException ex) { throw new RelProxyException(ex); } - catch (SecurityException ex) { throw new RelProxyException(ex); } - catch (IllegalArgumentException ex) { throw new RelProxyException(ex); } - catch (InvocationTargetException ex) { throw ex.getCause(); } // Los errores de ejecución se envuelven en un InvocationTargetException - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceUnit.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceUnit.java deleted file mode 100644 index f975152..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceUnit.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceUnit; -import java.util.LinkedList; - -/** - * - * @author jmarranz - */ -public abstract class ClassDescriptorSourceUnit extends ClassDescriptor -{ - protected final JProxyEngine engine; - protected volatile long timestamp; - protected final SourceUnit sourceUnit; - protected LinkedList innerClasses; - protected boolean pendingToRemove = false; // Se usa como monohilo, no hay problemas de sincronización - - public ClassDescriptorSourceUnit(JProxyEngine engine,String className,SourceUnit sourceFile, long timestamp) - { - super(className); - this.engine = engine; - this.sourceUnit = sourceFile; - this.timestamp = timestamp; - } - - public static ClassDescriptorSourceUnit create(boolean script,JProxyEngine engine,String className,SourceUnit sourceFile, long timestamp) - { - if (sourceFile instanceof SourceScriptRoot) - return new ClassDescriptorSourceScript(engine,className,(SourceScriptRoot)sourceFile,timestamp); - else if (sourceFile instanceof SourceFileJavaNormal) - return new ClassDescriptorSourceFileJava(engine,className,(SourceFileJavaNormal)sourceFile,timestamp); - else - return null; // WTF!! - } - - public SourceUnit getSourceUnit() - { - return sourceUnit; - } - - public String getEncoding() - { - return engine.getSourceEncoding(); - } - - @Override - public boolean isInnerClass() - { - return false; - } - - public long getTimestamp() - { - return timestamp; - } - - public void updateTimestamp(long timestamp) - { - this.timestamp = timestamp; - } - - public boolean isPendingToRemove() - { - return pendingToRemove; - } - - public void setPendingToRemove(boolean pendingToRemove) - { - this.pendingToRemove = pendingToRemove; - } - - - public void cleanOnSourceCodeChanged() - { - // Como ha cambiado la clase, reseteamos las dependencias - setClassBytes(null); - setLastLoadedClass(null); - clearInnerClassDescriptors(); // El código fuente nuevo puede haber cambiado totalmente las innerclasses antiguas (añadido, eliminado) - } - - public boolean isInnerClass(String className) - { - int pos = className.lastIndexOf('$'); - if (pos == -1) - return false; // No es innerclass - String baseClassName = className.substring(0, pos); - return this.className.equals(baseClassName); // Si es false es que es una innerclass pero de otra clase - } - - public LinkedList getInnerClassDescriptors() - { - return innerClasses; - } - - public void clearInnerClassDescriptors() - { - if (innerClasses != null) - innerClasses.clear(); - } - - public ClassDescriptorInner getInnerClassDescriptor(String className,boolean addWhenMissing) - { - if (innerClasses != null) - { - for(ClassDescriptorInner classDesc : innerClasses) - { - if (classDesc.getClassName().equals(className)) - return classDesc; - } - } - - if (!addWhenMissing) return null; - - return addInnerClassDescriptor(className); - } - - public ClassDescriptorInner addInnerClassDescriptor(String className) - { - if (!isInnerClass(className)) - return null; - - if (innerClasses == null) - innerClasses = new LinkedList(); - - ClassDescriptorInner classDesc = new ClassDescriptorInner(className,this); - innerClasses.add(classDesc); - return classDesc; - } - - @Override - public void resetLastLoadedClass() - { - super.resetLastLoadedClass(); - - LinkedList innerClassDescList = getInnerClassDescriptors(); - if (innerClassDescList != null) - { - for(ClassDescriptorInner innerClassDesc : innerClassDescList) - innerClassDesc.resetLastLoadedClass(); - } - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilationException.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilationException.java deleted file mode 100644 index d72ab12..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilationException.java +++ /dev/null @@ -1,25 +0,0 @@ - -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; - -/** - * - * @author jmarranz - */ -public class JProxyCompilationException extends RelProxyException -{ - protected ClassDescriptorSourceUnit sourceUnit; - - public JProxyCompilationException(ClassDescriptorSourceUnit sourceUnit) - { - super("Compilation error"); - this.sourceUnit = sourceUnit; - } - - public ClassDescriptorSourceUnit getClassDescriptorSourceUnit() - { - return sourceUnit; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilerContext.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilerContext.java deleted file mode 100644 index ebbfc36..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilerContext.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import java.io.IOException; -import java.util.List; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; - -/** - * - * @author jmarranz - */ -public class JProxyCompilerContext -{ - protected StandardJavaFileManager standardFileManager; - protected DiagnosticCollector diagnostics; - protected JProxyDiagnosticsListener diagnosticsListener; - - public JProxyCompilerContext(StandardJavaFileManager standardFileManager,DiagnosticCollector diagnostics,JProxyDiagnosticsListener diagnosticsListener) - { - this.standardFileManager = standardFileManager; - this.diagnostics = diagnostics; - this.diagnosticsListener = diagnosticsListener; - } - - public StandardJavaFileManager getStandardFileManager() - { - return standardFileManager; - } - - public DiagnosticCollector getDiagnosticCollector() - { - return diagnostics; - } - - public void close() - { - try { this.standardFileManager.close(); } - catch (IOException ex) { throw new RelProxyException(ex); } - - List> diagList = diagnostics.getDiagnostics(); - if (!diagList.isEmpty()) - { - if (diagnosticsListener != null) - { - diagnosticsListener.onDiagnostics(diagnostics); - } - else - { - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - } - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilerInMemory.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilerInMemory.java deleted file mode 100644 index b00df77..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JProxyCompilerInMemory.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputSourceInMemory; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectOutputClass; -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorInner; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileJava; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngineChangeDetectorAndCompiler; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import java.io.File; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaCompiler; -import javax.tools.JavaFileManager; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; -import javax.tools.ToolProvider; - -/** - * - * @author jmarranz - */ -public class JProxyCompilerInMemory -{ - protected JProxyEngineChangeDetectorAndCompiler parent; - protected JavaCompiler compiler; - protected Iterable compilationOptions; // puede ser null - protected JProxyDiagnosticsListener diagnosticsListener; // puede ser null - - public JProxyCompilerInMemory(JProxyEngineChangeDetectorAndCompiler engine,Iterable compilationOptions,JProxyDiagnosticsListener diagnosticsListener) - { - this.parent = engine; - this.compilationOptions = compilationOptions; - this.diagnosticsListener = diagnosticsListener; - this.compiler = ToolProvider.getSystemJavaCompiler(); - } - - public JProxyCompilerContext createJProxyCompilerContext() - { - DiagnosticCollector diagnostics = new DiagnosticCollector(); - StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null); - return new JProxyCompilerContext(standardFileManager,diagnostics,diagnosticsListener); - } - - public void compileSourceFile(ClassDescriptorSourceUnit sourceFileDesc,JProxyCompilerContext context,ClassLoader currentClassLoader,ClassDescriptorSourceFileRegistry sourceRegistry) - { - //File sourceFile = sourceFileDesc.getSourceFile(); - LinkedList outClassList = compile(sourceFileDesc,context,currentClassLoader,sourceRegistry); - - if (outClassList == null) - throw new JProxyCompilationException(sourceFileDesc); - - String className = sourceFileDesc.getClassName(); - - // Puede haber más de un resultado cuando hay inner classes y/o clase privada en el mismo archivo o bien simplemente clases dependientes - for(JavaFileObjectOutputClass outClass : outClassList) - { - String currClassName = outClass.binaryName(); - byte[] classBytes = outClass.getBytes(); - if (className.equals(currClassName)) - { - sourceFileDesc.setClassBytes(classBytes); - } - else - { - ClassDescriptorInner innerClass = sourceFileDesc.getInnerClassDescriptor(currClassName,true); - if (innerClass != null) - { - innerClass.setClassBytes(classBytes); - } - else - { - // Lo mismo es un archivo dependiente e incluso una inner class pero de otra clase que está siendo usada en el archivo compilado - ClassDescriptor dependentClass = sourceRegistry.getClassDescriptor(currClassName); - if (dependentClass != null) - { - dependentClass.setClassBytes(classBytes); - } - else - { - // Seguramente es debido a que el archivo java tiene una clase privada autónoma declarada en el mismo archivo .java (las que se ponen después de la clase principal pública normal), no permitimos estas clases porque sólo podemos - // detectarlas cuando cambiamos el código fuente, pero no si el código fuente no se ha tocado, por ejemplo no tenemos - // forma de conseguir que se recarguen de forma determinista y si posteriormente se cargara via ClassLoader al usarse no podemos reconocer que es una clase - // "hot reloadable" (quizás a través del package respecto a las demás clases hot pero no es muy determinista pues nada impide la mezcla de hot y no hot en el mismo package) - // Es una limitación mínima. - - // También puede ser un caso de clase excluida por el listener de exclusión, no debería ocurrir, tengo un caso de test en donde ocurre a posta - // (caso de JProxyExampleAuxIgnored cuando se cambia la JProxyExampleDocument que la usa) pero en programación normal no. - - if (parent.getJProxyInputSourceFileExcludedListener() == null) - throw new RelProxyException("Unexpected class when compiling: " + currClassName + " maybe it is an autonomous private class declared in the same java file of the principal class, this kind of classes are not supported in hot reload"); - else - System.out.println("Unexpected class when compiling: " + currClassName + " maybe it is an excluded class or is an autonomous private class declared in the same java file of the principal class, this kind of classes are not supported in hot reload"); - } - } - } - } - } - - private LinkedList compile(ClassDescriptorSourceUnit sourceFileDesc,JProxyCompilerContext context,ClassLoader currentClassLoader,ClassDescriptorSourceFileRegistry sourceRegistry) - { - // http://stackoverflow.com/questions/12173294/compiling-fully-in-memory-with-javax-tools-javacompiler - // http://www.accordess.com/wpblog/an-overview-of-java-compilation-api-jsr-199/ - // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/com/sun/tools/javac/util/JavacFileManager.java?av=h#JavacFileManager - // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/tools/StandardLocation.java - // http://books.brainysoftware.com/java6_sample/chapter2.pdf - // http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html - // http://www.javablogging.com/dynamic-in-memory-compilation/ Si no queremos generar archivos - // http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html - // http://stackoverflow.com/questions/264828/controlling-the-classpath-in-a-servlet?rq=1 - // http://stackoverflow.com/questions/1563909/how-to-set-classpath-when-i-use-javax-tools-javacompiler-compile-the-source - // http://stackoverflow.com/questions/10767048/javacompiler-with-custom-classloader-and-filemanager - - - StandardJavaFileManager standardFileManager = context.getStandardFileManager(); // recuerda que el StandardJavaFileManager puede reutilizarse entre varias compilaciones consecutivas mientras se cierre al final - - Iterable compilationUnits; - - if (sourceFileDesc instanceof ClassDescriptorSourceFileJava) - { - List sourceFileList = new ArrayList(); - sourceFileList.add(((ClassDescriptorSourceFileJava)sourceFileDesc).getSourceFile().getFile()); - compilationUnits = standardFileManager.getJavaFileObjectsFromFiles(sourceFileList); - } - else if (sourceFileDesc instanceof ClassDescriptorSourceScript) - { - ClassDescriptorSourceScript sourceFileDescScript = (ClassDescriptorSourceScript)sourceFileDesc; - LinkedList compilationUnitsList = new LinkedList(); - String code = sourceFileDescScript.getSourceCode(); - compilationUnitsList.add(new JavaFileObjectInputSourceInMemory(sourceFileDescScript.getClassName(),code,sourceFileDescScript.getEncoding(),sourceFileDescScript.getTimestamp())); - compilationUnits = compilationUnitsList; - } - else - { - throw new RelProxyException("Internal error"); - } - - JavaFileManagerInMemory fileManagerInMemory = new JavaFileManagerInMemory(standardFileManager,currentClassLoader,sourceRegistry,parent.getRequiredExtraJarPaths()); - - boolean success = compile(compilationUnits,fileManagerInMemory,context); - if (!success) return null; - - LinkedList classObj = fileManagerInMemory.getJavaFileObjectOutputClassList(); - return classObj; - } - - private boolean compile(Iterable compilationUnits,JavaFileManager fileManager,JProxyCompilerContext context) - { - /* - String systemClassPath = System.getProperty("java.class.path"); - */ - - LinkedList finalCompilationOptions = new LinkedList(); - if (compilationOptions != null) - for(String option : compilationOptions) finalCompilationOptions.add(option); - - FileExt[] folderSourceList = parent.getFolderSourceList().getArray(); - if (folderSourceList != null) - { - finalCompilationOptions.add("-classpath"); - StringBuilder classPath = new StringBuilder(); - for(int i = 0; i < folderSourceList.length; i++) - { - FileExt folderSources = folderSourceList[i]; - classPath.append(folderSources.getCanonicalPath()); - if (i < folderSourceList.length - 1) - classPath.append(File.pathSeparatorChar); - } - finalCompilationOptions.add(classPath.toString()); - } - - DiagnosticCollector diagnostics = context.getDiagnosticCollector(); - JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, finalCompilationOptions,null, compilationUnits); - boolean success = task.call(); - - return success; - } - - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JavaFileObjectInputClassFinderByClassLoader.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JavaFileObjectInputClassFinderByClassLoader.java deleted file mode 100644 index 3151ba4..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JavaFileObjectInputClassFinderByClassLoader.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFile; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFileSystem; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInJar; -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.JarURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html - * - * @author jmarranz - */ -public class JavaFileObjectInputClassFinderByClassLoader -{ - private static final String CLASS_FILE_EXTENSION = ".class"; - - private final ClassLoader classLoader; - private final FolderSourceList requiredExtraJarPaths; - - public JavaFileObjectInputClassFinderByClassLoader(ClassLoader classLoader,FolderSourceList requiredExtraJarPaths) - { - this.classLoader = classLoader; - this.requiredExtraJarPaths = requiredExtraJarPaths; - } - - public List find(String packageName) throws IOException - { - // http://www.dzone.com/snippets/get-all-classes-within-package - // http://sourceforge.net/p/scannotation/code/HEAD/tree/scannotation/src/main/java/org/scannotation/ClasspathUrlFinder.java#l124 - - String packagePath = packageName.replaceAll("\\.", "/"); - - List result = new ArrayList(); - - Enumeration urlEnumeration = classLoader.getResources(packagePath); - if (urlEnumeration.hasMoreElements()) - { - while (urlEnumeration.hasMoreElements()) - { // one URL for each jar on the classpath that has the given package - URL packageFolderURL = urlEnumeration.nextElement(); - listUnder(packageName,packageFolderURL,result); - } - } - else - { - // Enumeration vacía, chungo, esto nos ha ocurrido con el jar lib/ext/portlet.jar del Tomcat 6.2 del bundle liferay-portal-6.2-ce-ga3 - // daba un error de de javax.portlet.PortletRquest not found. - // En teoría debería responder a la búsqueda classLoader.getResources("javax/portlet") devolviendo el jar, pero devuelve un Enumeration vacío - // quizás es porque el jar no tiene un "Name: javax/portlet" en el META-INF/MANIFEST.MF (otros jar no tienen Name y funcionan no tengo claro el criterio, - // el problema debe estar en una opción del MANIFEST.MF que confunde al compilador), añadiendo el "Name: javax/portlet" funciona - // pero en plan majo permitimos al usuario que nos indique los paths de los jars "conflictivos" obtenemos los .class hijos directos por fuerza bruta del package - // requerido en el parámetro de este método find() y le evitamos modificar un jar de infraestructura que queda muy feo e inmantenible respecto a una solución - // basada en configuración del usuario. - - if (requiredExtraJarPaths != null) - { - FileExt[] jarFileList = requiredExtraJarPaths.getArray(); - if (jarFileList != null) - { - for (FileExt jarFile : jarFileList) - { - listUnderJarCustom(packagePath,jarFile,result); - } - } - } - } - - return result; - } - - - private void listUnder(String packageName, URL packageFolderURL,Collection result) - { - String pkgPath = packageFolderURL.toExternalForm(); //packageFolderURL.getFile(); El problema de getFile es que también está URL-encoded (un espacio es %20) lo cual no es compatible con File - - if (pkgPath.startsWith("file:")) - { - listUnderDir(packageName,pkgPath,result); - } - else - { // browse a jar file - listUnderJar(packageFolderURL,result); - } // maybe there can be something else for more involved class loaders - } - - private void listUnderDir(String packageName,String pkgPath,Collection result) - { - pkgPath = pkgPath.substring("file:".length()); - - try { pkgPath = URLDecoder.decode(pkgPath, "UTF-8"); } // Detecté el problema con Vaadin en un path con "Documents%20and%20Settings" con %20 obviamente no es un path correcto, deben ser espacios - catch (UnsupportedEncodingException ex) { throw new RelProxyException(ex); } - - File directory = new File(pkgPath); - if (!directory.isDirectory()) - throw new RelProxyException("Internal Error:" + pkgPath); - - // browse local .class files - useful for local execution - - File[] childFiles = directory.listFiles(); - for (File childFile : childFiles) - { - if (!childFile.isFile()) continue; - - // We only want the .class files. - String name = childFile.getName(); - if (name.endsWith(CLASS_FILE_EXTENSION)) - { - String binaryName = ClassDescriptor.getClassNameFromPackageAndClassFileName(packageName,name); - result.add(new JavaFileObjectInputClassInFile(childFile,binaryName, childFile.toURI())); - } - } - } - - private void listUnderJar(URL packageFolderURL,Collection result) - { - try - { - String jarUri = packageFolderURL.toExternalForm().split("!")[0]; - - JarURLConnection jarConn = (JarURLConnection) packageFolderURL.openConnection(); - String rootEntryName = jarConn.getEntryName(); - int rootEnd = rootEntryName.length() + 1; - - Enumeration entryEnum = jarConn.getJarFile().entries(); - while (entryEnum.hasMoreElements()) - { - JarEntry jarEntry = entryEnum.nextElement(); - String name = jarEntry.getName(); - // Empieza por packagePath y no hay más folders siguientes, terminando en un .class (una clase concreta) - if (name.startsWith(rootEntryName) && name.indexOf('/', rootEnd) == -1 && name.endsWith(CLASS_FILE_EXTENSION)) - { - URI uri = URI.create(jarUri + "!/" + name); - String binaryName = ClassDescriptor.getClassNameFromRelativeClassFilePath(name); - result.add(new JavaFileObjectInputClassInJar(binaryName, uri,jarEntry.getTime())); - } - } - } - catch (Exception e) - { - throw new RelProxyException("Wasn't able to open " + packageFolderURL + " as a jar file", e); - } - } - - - private void listUnderJarCustom(String packagePath,FileExt jarFile,Collection result) - { - String normalizedPath = jarFile.getCanonicalPath(); - if (normalizedPath.contains("\\")) // Windows - { - // No estoy seguro de que sea necesario normalizar pero por si acaso - normalizedPath = normalizedPath.replace("\\","/"); // "C:/folder" - normalizedPath = "/" + normalizedPath; // "/C:/folder" - } - - String urlPath = "file:" + normalizedPath; - - URL packageFolderURL; - try { packageFolderURL = new URL(urlPath); } - catch (MalformedURLException ex) { throw new RelProxyException(ex); } - - String jarUri = "jar:" + packageFolderURL.toExternalForm(); - - int posEnd = packagePath.length() + 1; - - ZipInputStream zip = null; - - try - { - zip = new ZipInputStream(packageFolderURL.openStream()); - ZipEntry zipEntry = zip.getNextEntry(); - while(zipEntry != null) - { - String name = zipEntry.getName(); - // Empieza por packagePath y no hay más folders siguientes, terminando en un .class (una clase concreta) - if (name.startsWith(packagePath) && name.indexOf('/', posEnd) == -1 && name.endsWith(CLASS_FILE_EXTENSION)) - { - URI uri = URI.create(jarUri + "!/" + name); - String binaryName = ClassDescriptor.getClassNameFromRelativeClassFilePath(name); - result.add(new JavaFileObjectInputClassInJar(binaryName, uri,zipEntry.getTime())); - } - - zipEntry = zip.getNextEntry(); - } - } - catch(IOException ex) - { - throw new RelProxyException(ex); - } - finally - { - if (zip != null) try { zip.close(); } catch (IOException ex) { throw new RelProxyException(ex); } - } - } - - -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JProxyJavaFileObjectInput.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JProxyJavaFileObjectInput.java deleted file mode 100644 index d2db84a..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JProxyJavaFileObjectInput.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -/** - * - * @author jmarranz - */ -public interface JProxyJavaFileObjectInput -{ - public String getBinaryName(); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInJar.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInJar.java deleted file mode 100644 index 2c0dd5c..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInJar.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; - -/** - * - * @author jmarranz - */ -public class JavaFileObjectInputClassInJar extends JavaFileObjectInputClassInFileSystem -{ - protected long timestamp; - - public JavaFileObjectInputClassInJar(String binaryName, URI uri,long timestamp) - { - super(binaryName,uri,uri.getSchemeSpecificPart()); - this.timestamp = timestamp; - } - - @Override - public InputStream openInputStream() throws IOException - { - return uri.toURL().openStream(); // easy way to handle any URI! - } - - @Override - public long getLastModified() - { - return timestamp; - } - - @Override - public String toString() - { - return "JavaFileObjectInputClassInJar{uri=" + uri + '}'; - } -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInMemory.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInMemory.java deleted file mode 100644 index 8bfad82..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInMemory.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import javax.tools.SimpleJavaFileObject; - -/** - * http://www.javablogging.com/dynamic-in-memory-compilation/ - * - * @author jmarranz - */ -public class JavaFileObjectInputClassInMemory extends SimpleJavaFileObject implements JProxyJavaFileObjectInput -{ - protected String binaryName; - protected byte[] byteCode; - protected long timestamp; - - public JavaFileObjectInputClassInMemory(String name,byte[] byteCode,long timestamp) - { - super(URI.create("string:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS); - - this.binaryName = name; - this.byteCode = byteCode; - this.timestamp = timestamp; - } - - public byte[] getBytes() - { - return byteCode; - } - - @Override - public long getLastModified() - { - return timestamp; - } - - @Override - public InputStream openInputStream() throws IOException - { - return new ByteArrayInputStream(getBytes()); - } - - @Override - public OutputStream openOutputStream() throws IOException - { - throw new UnsupportedOperationException(); - } - - @Override - public String getBinaryName() - { - return binaryName; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceBase.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceBase.java deleted file mode 100644 index 2ed0317..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceBase.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import com.innowhere.relproxy.RelProxyException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import javax.tools.SimpleJavaFileObject; - -/** - * http://www.javablogging.com/dynamic-in-memory-compilation/ - * - * @author jmarranz - */ -public abstract class JavaFileObjectInputSourceBase extends SimpleJavaFileObject implements JProxyJavaFileObjectInput -{ - protected String binaryName; - protected String encoding; - - public JavaFileObjectInputSourceBase(String name,String encoding) - { - super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); // La extensión .java es necesaria aunque sea falsa sino da error - - this.binaryName = name; - this.encoding = encoding; - } - - protected abstract String getSource(); - - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException - { - return getSource(); - } - - public byte[] getBytes() - { - try - { - return getSource().getBytes(encoding); - } - catch (UnsupportedEncodingException ex) { throw new RelProxyException(ex); } - } - - @Override - public InputStream openInputStream() throws IOException - { - return new ByteArrayInputStream(getBytes()); - } - - @Override - public OutputStream openOutputStream() throws IOException - { - throw new UnsupportedOperationException(); - } - - public String getBinaryName() - { - return binaryName; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInFile.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInFile.java deleted file mode 100644 index 69abf50..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInFile.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import java.io.File; - -/** - * http://www.javablogging.com/dynamic-in-memory-compilation/ - * - * @author jmarranz - */ -public class JavaFileObjectInputSourceInFile extends JavaFileObjectInputSourceBase -{ - protected File file; - protected String source; - - public JavaFileObjectInputSourceInFile(String name,File file,String encoding) - { - super(name,encoding); - this.file = file; - } - - @Override - protected String getSource() - { - if (source != null) - return source; - this.source = JProxyUtil.readTextFile(file, encoding); - return source; - } - - @Override - public long getLastModified() - { - return file.lastModified(); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInMemory.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInMemory.java deleted file mode 100644 index 95cbf0a..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInMemory.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -/** - * http://www.javablogging.com/dynamic-in-memory-compilation/ - * - * @author jmarranz - */ -public class JavaFileObjectInputSourceInMemory extends JavaFileObjectInputSourceBase -{ - protected String source; - protected long timestamp; - - public JavaFileObjectInputSourceInMemory(String name,String source,String encoding,long timestamp) - { - super(name,encoding); - this.source = source; - this.timestamp = timestamp; - } - - @Override - protected String getSource() - { - return source; - } - - @Override - public long getLastModified() - { - return timestamp; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectOutputClass.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectOutputClass.java deleted file mode 100644 index 187bfb0..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectOutputClass.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import com.innowhere.relproxy.RelProxyException; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; -import javax.tools.SimpleJavaFileObject; - -/** - * http://www.javablogging.com/dynamic-in-memory-compilation/ - * - * @author jmarranz - */ -public class JavaFileObjectOutputClass extends SimpleJavaFileObject { - - /** - * Byte code created by the compiler will be stored in this - * ByteArrayOutputStream so that we can later get the - * byte array out of it - * and put it in the memory as an instance of our class. - */ - protected final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - protected String binaryName; - - /** - * Registers the compiled class object under URI - * containing the class full name - * - * @param name - * Full name of the compiled class - * @param kind - * Kind of the data. It will be CLASS in our case - */ - public JavaFileObjectOutputClass(String name, Kind kind) - { - super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind); - - if (!Kind.CLASS.equals(kind)) throw new RelProxyException("Unexpected"); - this.binaryName = name; - } - - public String binaryName() - { - return binaryName; - } - - public byte[] getBytes() - { - return bos.toByteArray(); - } - - @Override - public OutputStream openOutputStream() throws IOException - { - return bos; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceFileJavaNormal.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceFileJavaNormal.java deleted file mode 100644 index 5842b4b..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceFileJavaNormal.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -import com.innowhere.relproxy.impl.FileExt; - -/** - * - * @author jmarranz - */ -public class SourceFileJavaNormal extends SourceUnit -{ - protected final FileExt sourceFile; - - public SourceFileJavaNormal(FileExt sourceFile,FileExt rootFolderOfSources) - { - super(buildClassNameFromFile(sourceFile,rootFolderOfSources)); - this.sourceFile = sourceFile; - } - - @Override - public long lastModified() - { - return sourceFile.getFile().lastModified(); - } - - public FileExt getFileExt() - { - return sourceFile; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRoot.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRoot.java deleted file mode 100644 index fe03688..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRoot.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -/** - * - * @author jmarranz - */ -public abstract class SourceScriptRoot extends SourceUnit -{ - public SourceScriptRoot(String className) - { - super(className); - } - - public abstract String getScriptCode(String encoding,boolean[] hasHashBang); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFile.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFile.java deleted file mode 100644 index 04c9219..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFile.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; - -/** - * - * @author jmarranz - */ -public abstract class SourceScriptRootFile extends SourceScriptRoot -{ - protected FileExt sourceFile; - - public SourceScriptRootFile(FileExt sourceFile,FolderSourceList folderSourceList) - { - super(buildClassNameFromFile(sourceFile,folderSourceList)); - this.sourceFile = sourceFile; - } - - public static SourceScriptRootFile createSourceScriptRootFile(FileExt sourceFile,FolderSourceList folderSourceList) - { - String ext = JProxyUtil.getFileExtension(sourceFile.getFile()); // Si no tiene extensión devuelve "" - if ("java".equals(ext)) - return new SourceScriptRootFileJavaExt(sourceFile,folderSourceList); - else - return new SourceScriptRootFileOtherExt(sourceFile,folderSourceList); // Caso de archivo script inicial sin extensión .java (puede ser sin extensión) - } - - @Override - public long lastModified() - { - return sourceFile.getFile().lastModified(); - } - - public FileExt getFileExt() - { - return sourceFile; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileJavaExt.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileJavaExt.java deleted file mode 100644 index 263eb0d..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileJavaExt.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; - -/** - * - * @author jmarranz - */ -public class SourceScriptRootFileJavaExt extends SourceScriptRootFile -{ - public SourceScriptRootFileJavaExt(FileExt sourceFile,FolderSourceList folderSourceList) - { - super(sourceFile,folderSourceList); - } - - @Override - public String getScriptCode(String encoding,boolean[] hasHashBang) - { - hasHashBang[0] = false; - return JProxyUtil.readTextFile(sourceFile.getFile(),encoding); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileOtherExt.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileOtherExt.java deleted file mode 100644 index 5437d75..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileOtherExt.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; - -/** - * - * @author jmarranz - */ -public class SourceScriptRootFileOtherExt extends SourceScriptRootFile -{ - public SourceScriptRootFileOtherExt(FileExt sourceFile,FolderSourceList folderSourceList) - { - super(sourceFile,folderSourceList); - } - - @Override - public String getScriptCode(String encoding,boolean[] hasHashBang) - { - String codeBody = JProxyUtil.readTextFile(sourceFile.getFile(),encoding); - // Eliminamos la primera línea #! (debe estar en la primera línea y sin espacios antes) - if (codeBody.startsWith("#!")) - { - hasHashBang[0] = true; - int pos = codeBody.indexOf('\n'); - if (pos != -1) // Rarísimo que sólo esté el hash bang (script vacío) - { - codeBody = codeBody.substring(pos + 1); - } - } - else hasHashBang[0] = false; - return codeBody; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootInMemory.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootInMemory.java deleted file mode 100644 index 973c5cd..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootInMemory.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -/** - * - * @author jmarranz - */ -public class SourceScriptRootInMemory extends SourceScriptRoot -{ - public static final String DEFAULT_CLASS_NAME = "_jproxyMainClass_"; // OJO NO CAMBIAR, está ya documentada - - protected String code; - protected long timestamp; - - private SourceScriptRootInMemory(String className,String code) - { - super(className); - setScriptCode(code,System.currentTimeMillis()); - } - - public static SourceScriptRootInMemory createSourceScriptInMemory(String code) - { - return new SourceScriptRootInMemory(DEFAULT_CLASS_NAME,code); - } - - @Override - public long lastModified() - { - return timestamp; // Siempre ha sido modificado - } - - @Override - public String getScriptCode(String encoding,boolean[] hasHashBang) - { - hasHashBang[0] = false; - return code; - } - - public boolean isEmptyCode() - { - // Si code es "" la clase especial se genera pero no hace nada simplemente devuelve un null. - // Este es el caso en el que utilizamos RelProxy embebido en un framework utilizando la API ScriptEngine pero únicamente porque se usa una API basada - // en interfaces, pero tiene el inconveniente de generarse un SourceScriptRootInMemory inútil que no hace nada - return code.isEmpty(); - } - - public String getScriptCode() - { - return code; - } - - public final void setScriptCode(String code,long timestamp) - { - this.code = code; - this.timestamp = timestamp; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceUnit.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceUnit.java deleted file mode 100644 index bf396c3..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/srcunit/SourceUnit.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit; - -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; - -/** - * - * @author jmarranz - */ -public abstract class SourceUnit -{ - protected final String className; - - public SourceUnit(String className) - { - this.className = className; - } - - public abstract long lastModified(); - - protected static String buildClassNameFromFile(FileExt sourceFile,FolderSourceList sourceList) - { - return sourceList.buildClassNameFromFile(sourceFile); - } - - protected static String buildClassNameFromFile(FileExt sourceFile,FileExt rootFolderOfSources) - { - return FolderSourceList.buildClassNameFromFile(sourceFile,rootFolderOfSources); - } - - public String getClassName() - { - return className; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineDelegateImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineDelegateImpl.java deleted file mode 100644 index d0c3971..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineDelegateImpl.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.screngine; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.core.JProxyImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootInMemory; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.JProxyCompilationException; -import com.innowhere.relproxy.impl.jproxy.shell.JProxyShellClassLoader; -import java.io.File; -import javax.script.ScriptContext; -import javax.script.ScriptException; - -/** - * - * @author jmarranz - */ -public class JProxyScriptEngineDelegateImpl extends JProxyImpl -{ - protected JProxyScriptEngineImpl parent; - protected ClassDescriptorSourceScript classDescSourceScript; - protected long codeBufferModTimestamp = 0; - protected long lastCodeCompiledTimestamp = 0; - - public JProxyScriptEngineDelegateImpl(JProxyScriptEngineImpl parent) - { - this.parent = parent; - } - - @Override - public ClassDescriptorSourceScript init(JProxyConfigImpl config) - { - SourceScriptRoot sourceFileScript = SourceScriptRootInMemory.createSourceScriptInMemory(""); - - JProxyShellClassLoader classLoader = null; - String classFolder = config.getClassFolder(); - if (classFolder != null) - classLoader = new JProxyShellClassLoader(getDefaultClassLoader(),new File(classFolder)); - - this.classDescSourceScript = init(config,sourceFileScript,classLoader); - return classDescSourceScript; - } - - @Override - public Class getMainParamClass() - { - return ScriptContext.class; - } - - private SourceScriptRootInMemory getSourceScriptInMemory() - { - return (SourceScriptRootInMemory)classDescSourceScript.getSourceScript(); - } - - public Object execute(String code,ScriptContext context) throws ScriptException - { - Class scriptClass; - JProxyEngine jproxyEngine = getJProxyEngine(); - Object monitor = jproxyEngine.getMonitor(); - synchronized(monitor) - { - if (!getSourceScriptInMemory().getScriptCode().equals(code)) - { - this.codeBufferModTimestamp = System.currentTimeMillis(); - - getSourceScriptInMemory().setScriptCode(code,codeBufferModTimestamp); - // Recuerda que cada vez que se obtiene el timestamp se llama a System.currentTimeMillis(), es imposible que el usuario haga algo en menos de 1ms - - ClassDescriptorSourceScript classDescSourceScript2 = null; - try - { - classDescSourceScript2 = jproxyEngine.detectChangesInSourcesAndReload(); - } - catch(JProxyCompilationException ex) - { - throw new ScriptException(ex); - } - - if (classDescSourceScript2 != classDescSourceScript) - throw new RelProxyException("Internal Error"); - - this.lastCodeCompiledTimestamp = System.currentTimeMillis(); - if (lastCodeCompiledTimestamp == codeBufferModTimestamp) // Demasiado rápido compilando - { - // Aseguramos que el siguiente código se ejecuta si o si con un codeBufferModTimestamp mayor que el timestamp de la compilación - try { Thread.sleep(1); } catch (InterruptedException ex) { throw new RelProxyException(ex); } - } - } - - scriptClass = classDescSourceScript.getLastLoadedClass(); - } - - try - { - return ClassDescriptorSourceScript.callMainMethod(scriptClass,parent,context); - } - catch(Throwable ex) - { - Exception ex2 = (ex instanceof Exception) ? (Exception)ex : new RelProxyException(ex); - throw new ScriptException(ex2); - } - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineFactoryImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineFactoryImpl.java deleted file mode 100644 index d5deff3..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineFactoryImpl.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.screngine; - -import com.innowhere.relproxy.RelProxy; -import com.innowhere.relproxy.jproxy.JProxyScriptEngineFactory; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.script.ScriptEngine; - -/** - * Ideas: http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy/1.6.0/org/codehaus/groovy/jsr223/GroovyScriptEngineFactory.java - * - * @author jmarranz - */ -public class JProxyScriptEngineFactoryImpl extends JProxyScriptEngineFactory -{ - protected static final String SHORT_NAME = "java"; - protected static final String LANGUAGE_NAME = "Java"; - - protected static final List names; - protected static final List extensions; - protected static final List mimeTypes; - - static - { - ArrayList n; - - n = new ArrayList(2); - n.add(SHORT_NAME); - n.add(LANGUAGE_NAME); - names = Collections.unmodifiableList(n); - - n = new ArrayList(1); - n.add("java"); - extensions = Collections.unmodifiableList(n); - - n = new ArrayList(2); http://reference.sitepoint.com/html/mime-types-full - n.add("text/x-java-source"); - n.add("text/plain"); - mimeTypes = Collections.unmodifiableList(n); - } - - public JProxyScriptEngineFactoryImpl() - { - } - - public static JProxyScriptEngineFactory create() - { - return new JProxyScriptEngineFactoryImpl(); - } - - @Override - public String getEngineName() - { - return "RelProxy Java Script Engine"; - } - - @Override - public String getEngineVersion() - { - return RelProxy.getVersion(); - } - - @Override - public List getExtensions() - { - return extensions; - } - - @Override - public List getMimeTypes() - { - return mimeTypes; - } - - @Override - public List getNames() - { - return names; - } - - @Override - public String getLanguageName() - { - return LANGUAGE_NAME; - } - - @Override - public String getLanguageVersion() - { - return System.getProperty("java.version"); // Ej 1.6.0_18 - } - - @Override - public Object getParameter(String key) - { - if (ScriptEngine.NAME.equals(key)) { - return SHORT_NAME; - } else if (ScriptEngine.ENGINE.equals(key)) { - return getEngineName(); - } else if (ScriptEngine.ENGINE_VERSION.equals(key)) { - return getEngineVersion(); - } else if (ScriptEngine.LANGUAGE.equals(key)) { - return getLanguageName(); - } else if (ScriptEngine.LANGUAGE_VERSION.equals(key)) { - return getLanguageVersion(); - } else if ("THREADING".equals(key)) { - return "MULTITHREADED"; - } else { - throw new IllegalArgumentException("Invalid key"); - } - } - - @Override - public String getMethodCallSyntax(String obj, String method, String... args) - { - StringBuilder ret = new StringBuilder(); - ret.append(obj + "." + method + "("); - int len = args.length; - if (len == 0) { - ret.append(")"); - return ret.toString(); - } - - for (int i = 0; i < len; i++) { - ret.append(args[i]); - if (i != len - 1) { - ret.append(","); - } else { - ret.append(")"); - } - } - return ret.toString(); - } - - @Override - public String getOutputStatement(String toDisplay) - { - StringBuilder buf = new StringBuilder(); - buf.append("System.out.println(\""); - int len = toDisplay.length(); - for (int i = 0; i < len; i++) - { - char ch = toDisplay.charAt(i); - switch (ch) { - case '"': - buf.append("\\\""); - break; - case '\\': - buf.append("\\\\"); - break; - default: - buf.append(ch); - break; - } - } - buf.append("\")"); - return buf.toString(); - } - - @Override - public String getProgram(String... statements) - { - StringBuilder ret = new StringBuilder(); - int len = statements.length; - for (int i = 0; i < len; i++) - { - ret.append(statements[i]); - ret.append('\n'); - } - return ret.toString(); - } - - @Override - public ScriptEngine getScriptEngine() - { - return new JProxyScriptEngineImpl(this); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineImpl.java deleted file mode 100644 index 928d77f..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/JProxyScriptEngineImpl.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.screngine; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.GenericProxyImpl; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.jproxy.JProxyConfig; -import com.innowhere.relproxy.jproxy.JProxyScriptEngine; -import java.io.Reader; -import javax.script.AbstractScriptEngine; -import javax.script.Bindings; -import javax.script.ScriptContext; -import javax.script.ScriptEngineFactory; -import javax.script.ScriptException; - -/** - * Methods of this class are similar to JProxyDefaultImpl - * - * @author jmarranz - */ -public class JProxyScriptEngineImpl extends AbstractScriptEngine implements JProxyScriptEngine -{ - protected JProxyScriptEngineDelegateImpl jproxy; - protected JProxyScriptEngineFactoryImpl factory; - - public JProxyScriptEngineImpl(JProxyScriptEngineFactoryImpl factory) - { - this.factory = factory; - } - - @Override - public void init(JProxyConfig config) - { - JProxyConfigImpl configImpl = (JProxyConfigImpl)config; - if (!configImpl.isEnabled()) return; // jproxy quedará null - - GenericProxyImpl.checkSingletonNull(jproxy); - this.jproxy = new JProxyScriptEngineDelegateImpl(this); - jproxy.init(configImpl); - } - - - @Override - public Object eval(String script, ScriptContext context) throws ScriptException - { - if (jproxy == null) - throw new RelProxyException("Engine is disabled"); - - return jproxy.execute(script,context); - } - - @Override - public Object eval(Reader reader, ScriptContext context) throws ScriptException - { - String script = JProxyUtil.readTextFile(reader); - return eval(script,context); - } - - @Override - public Bindings createBindings() - { - return new BindingsImpl(); - } - - @Override - public ScriptEngineFactory getFactory() - { - return factory; - } - - @Override - public T create(T obj,Class clasz) - { - if (jproxy == null) - return obj; // No se ha llamado al init o enabled = false - return jproxy.create(obj, clasz); - } - - @Override - public Object create(Object obj,Class[] classes) - { - if (jproxy == null) - return obj; // No se ha llamado al init o enabled = false - return jproxy.create(obj, classes); - } - - @Override - public boolean isEnabled() - { - if (jproxy == null) - return false; - - return jproxy.isEnabled(); - } - - @Override - public boolean isRunning() - { - if (jproxy == null) - return false; - - return jproxy.isRunning(); - } - - @Override - public boolean start() - { - if (jproxy == null) - return false; - - return jproxy.start(); - } - - @Override - public boolean stop() - { - if (jproxy == null) - return false; - - return jproxy.stop(); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellClassLoader.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellClassLoader.java deleted file mode 100644 index 53fb839..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellClassLoader.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; - -/** - * - * @author jmarranz - */ -public class JProxyShellClassLoader extends URLClassLoader -{ - public JProxyShellClassLoader(ClassLoader parent,File classFolder) - { - super(toURLArray(classFolder),parent); - } - - private static URL[] toURLArray(File file) - { - try { return new URL[]{file.toURI().toURL()}; } - catch (MalformedURLException ex) { throw new RelProxyException(ex); } - } - - @Override - protected Class findClass(String name) throws ClassNotFoundException - { - return super.findClass(name); - } - - @Override - protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException - { - return super.loadClass(name, resolve); - } - - public synchronized Class defineClass(ClassDescriptor classDesc) - { - String className = classDesc.getClassName(); - byte[] classBytes = classDesc.getClassBytes(); - Class clasz = defineClass(className,classBytes, 0, classBytes.length); - classDesc.setLastLoadedClass(clasz); - return clasz; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellCodeSnippetImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellCodeSnippetImpl.java deleted file mode 100644 index 840f51f..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellCodeSnippetImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootInMemory; -import java.util.LinkedList; - -/** - * - * @author jmarranz - */ -public class JProxyShellCodeSnippetImpl extends JProxyShellImpl -{ - public void init(String[] args) - { - super.init(args, null); - } - - @Override - protected void executeFirstTime(ClassDescriptorSourceScript scriptFileDesc,LinkedList argsToScript,JProxyShellClassLoader classLoader) - { - try - { - scriptFileDesc.callMainMethod(argsToScript); - } - catch(Throwable ex) - { - ex.printStackTrace(System.out); - } - } - - @Override - protected void processConfigParams(String[] args,LinkedList argsToScript,JProxyConfigImpl config) - { - super.processConfigParams(args, argsToScript, config); - - String classFolder = config.getClassFolder(); - if (classFolder != null && !classFolder.trim().isEmpty()) throw new RelProxyException("cacheClassFolder is useless to execute a code snippet"); - } - - @Override - protected SourceScriptRoot createSourceScriptRoot(String[] args,LinkedList argsToScript,FolderSourceList folderSourceList) - { - // En argsToScript no está el args[0] ni falta que hace porque es el flag "-c" - StringBuilder code = new StringBuilder(); - for(String chunk : argsToScript) - code.append(chunk); - return SourceScriptRootInMemory.createSourceScriptInMemory(code.toString()); - } - - @Override - protected JProxyShellClassLoader getJProxyShellClassLoader(JProxyConfigImpl config) - { - // No hay classFolder => no hay necesidad de nuevo ClassLoader - return null; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellImpl.java deleted file mode 100644 index b49c532..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellImpl.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.core.JProxyImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import java.lang.reflect.Method; -import java.util.LinkedList; - -/** - * Inspiraciones: http://groovy.codehaus.org/Running - * - * @author jmarranz - */ -public abstract class JProxyShellImpl extends JProxyImpl -{ - - public static void main(String[] args) - { - if (args[0].isEmpty()) - { - // Esto tiene explicación: cuando invocamos jproxysh sin parámetros (o espacios da igual) invocamos dentro jproxysh con com.innowhere.relproxy.jproxy.JProxyShell "$@" - // el parámetro "$@" se convierte en "" que es un parámetro de verdad que recibimos pero de cadena vacía, lo cual nos viene GENIAL para distinguir el caso shell interactive - SINGLETON = new JProxyShellInteractiveImpl(); - ((JProxyShellInteractiveImpl)SINGLETON).init(args); - } - else - { - if (args[0].equals("-c")) - { - SINGLETON = new JProxyShellCodeSnippetImpl(); - ((JProxyShellCodeSnippetImpl)SINGLETON).init(args); - } - else - { - SINGLETON = new JProxyShellScriptFileImpl(); - ((JProxyShellScriptFileImpl)SINGLETON).init(args); - } - - } - } - - protected ClassDescriptorSourceScript init(String[] args,String inputPath) - { - // Esto quizás necesite una opción en plan "verbose" o "log" para mostrar por pantalla o nada - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - @Override - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - JProxyConfigImpl config = new JProxyConfigImpl(); - config.setEnabled(true); - config.setRelProxyOnReloadListener(proxyListener); - config.setInputPath(inputPath); - config.setJProxyInputSourceFileExcludedListener(null); - config.setJProxyCompilerListener(null); - config.setJProxyDiagnosticsListener(null); // Nos vale el log por defecto y no hay manera de espeficar otra cosa via comando - - LinkedList argsToScript = new LinkedList(); - processConfigParams(args,argsToScript,config); - - SourceScriptRoot sourceFileScript = createSourceScriptRoot(args,argsToScript,config.getFolderSourceList()); - - JProxyShellClassLoader classLoader = getJProxyShellClassLoader(config); - - ClassDescriptorSourceScript scriptFileDesc = init(config,sourceFileScript,classLoader); - - executeFirstTime(scriptFileDesc,argsToScript,classLoader); - - return scriptFileDesc; - } - - @Override - public Class getMainParamClass() - { - return String[].class; - } - - - protected abstract SourceScriptRoot createSourceScriptRoot(String[] args,LinkedList argsToScript,FolderSourceList folderSourceList); - protected abstract JProxyShellClassLoader getJProxyShellClassLoader(JProxyConfigImpl config); - protected abstract void executeFirstTime(ClassDescriptorSourceScript scriptFileDesc,LinkedList argsToScript,JProxyShellClassLoader classLoader); - - private static Iterable parseCompilationOptions(String value) - { - // Ej -source 1.6 -target 1.6 se convertiría en Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - String[] options = value.split(" "); - LinkedList opCol = new LinkedList(); - for (String option : options) - { - String op = option.trim(); // Por si hubiera dos espacios - if (op.isEmpty()) continue; - opCol.add(op); - } - return opCol; - } - - - protected void processConfigParams(String[] args,LinkedList argsToScript,JProxyConfigImpl config) - { - String classFolder = null; - long scanPeriod = -1; - Iterable compilationOptions = null; - - boolean test = false; - - for(int i = 1; i < args.length; i++) - { - String arg = args[i]; - - if (arg.startsWith("-D")) - { - String param = arg.substring(2); - int pos = param.indexOf('='); - if (pos == -1) - throw new RelProxyException("Bad parameter format: " + arg); - String name = param.substring(0,pos); - String value = param.substring(pos + 1); - - if ("cacheClassFolder".equals(name)) - { - classFolder = value; - } - else if ("compilationOptions".equals(name)) - { - compilationOptions = parseCompilationOptions(value); - } - else if ("test".equals(name)) - { - test = Boolean.parseBoolean(value); - } - else throw new RelProxyException("Unknown parameter: " + arg); - } - else - { - argsToScript.add(arg); - } - } - - config.setClassFolder(classFolder); - config.setScanPeriod(scanPeriod); - config.setCompilationOptions(compilationOptions); - config.setTest(test); - } - - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellInteractiveImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellInteractiveImpl.java deleted file mode 100644 index 2434435..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellInteractiveImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootInMemory; -import com.innowhere.relproxy.impl.jproxy.shell.inter.JProxyShellProcessor; -import java.util.LinkedList; - -/** - * Alguna inspiración: http://groovy.codehaus.org/Groovy+Shell - * - * @author jmarranz - */ -public class JProxyShellInteractiveImpl extends JProxyShellImpl -{ - protected boolean test = false; - protected JProxyShellProcessor processor = new JProxyShellProcessor(this); - protected ClassDescriptorSourceScript classDescSourceScript; - - public void init(String[] args) - { - this.classDescSourceScript = super.init(args, null); - - if (test) - { - processor.test(); - return; - } - - processor.loop(); - } - - public ClassDescriptorSourceScript getClassDescriptorSourceScript() - { - return classDescSourceScript; - } - - public SourceScriptRootInMemory getSourceScriptInMemory() - { - return (SourceScriptRootInMemory)classDescSourceScript.getSourceScript(); - } - - @Override - public ClassDescriptorSourceScript init(JProxyConfigImpl config,SourceScriptRoot scriptFile,ClassLoader classLoader) - { - ClassDescriptorSourceScript script = super.init(config,scriptFile, classLoader); - - this.test = config.isTest(); - - return script; - } - - @Override - protected void executeFirstTime(ClassDescriptorSourceScript scriptFileDesc,LinkedList argsToScript,JProxyShellClassLoader classLoader) - { - // La primera vez el script es vacío, no hay nada que ejecutar - } - - @Override - protected void processConfigParams(String[] args,LinkedList argsToScript,JProxyConfigImpl config) - { - super.processConfigParams(args, argsToScript, config); - - String classFolder = config.getClassFolder(); - if (classFolder != null && !classFolder.trim().isEmpty()) throw new RelProxyException("cacheClassFolder is useless to execute in interactive mode"); - } - - @Override - protected SourceScriptRoot createSourceScriptRoot(String[] args,LinkedList argsToScript,FolderSourceList folderSourceList) - { - return SourceScriptRootInMemory.createSourceScriptInMemory(""); // La primera vez no hace nada, sirve para "calentar" la app - } - - @Override - protected JProxyShellClassLoader getJProxyShellClassLoader(JProxyConfigImpl config) - { - // No hay classFolder => no hay necesidad de nuevo ClassLoader - return null; - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellScriptFileImpl.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellScriptFileImpl.java deleted file mode 100644 index 8f96551..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/JProxyShellScriptFileImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell; - -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.FileExt; -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootFile; -import java.io.File; -import java.util.LinkedList; - -/** - * - * @author jmarranz - */ -public class JProxyShellScriptFileImpl extends JProxyShellImpl -{ - protected FileExt scriptFile; - - public void init(String[] args) - { - File scriptFile = new File(args[0]); - if (!scriptFile.exists()) - throw new RelProxyException("File " + args[0] + " does not exist"); - - this.scriptFile = new FileExt(scriptFile); - - File parentDir = JProxyUtil.getParentDir(scriptFile); - String inputPath = parentDir.getAbsolutePath(); - super.init(args, inputPath); - } - - @Override - protected void executeFirstTime(ClassDescriptorSourceScript scriptFileDesc,LinkedList argsToScript,JProxyShellClassLoader classLoader) - { - fixLastLoadedClass(scriptFileDesc,classLoader); - - try - { - scriptFileDesc.callMainMethod(argsToScript); - } - catch(Throwable ex) - { - ex.printStackTrace(System.out); - } - } - - @Override - protected SourceScriptRoot createSourceScriptRoot(String[] args,LinkedList argsToScript,FolderSourceList folderSourceList) - { - return SourceScriptRootFile.createSourceScriptRootFile(scriptFile,folderSourceList); - } - - @Override - protected JProxyShellClassLoader getJProxyShellClassLoader(JProxyConfigImpl config) - { - String classFolder = config.getClassFolder(); - if (classFolder != null) - return new JProxyShellClassLoader(getDefaultClassLoader(),new File(classFolder)); - else - return null; - } - - protected void fixLastLoadedClass(ClassDescriptorSourceScript scriptFileDesc,JProxyShellClassLoader classLoader) - { - Class scriptClass = scriptFileDesc.getLastLoadedClass(); - if (scriptClass != null) return; - - // Esto es esperable cuando especificamos un classFolder en donde está ya compilado el script lanzador y es más actual que el fuente - // no ha habido necesidad de crear un class loader "reloader" ni de recargar todos los archivos fuente con él - if (classLoader == null) throw new RelProxyException("INTERNAL ERROR"); - if (scriptFileDesc.getClassBytes() == null) throw new RelProxyException("INTERNAL ERROR"); - scriptClass = classLoader.defineClass(scriptFileDesc); - scriptFileDesc.setLastLoadedClass(scriptClass); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/Command.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/Command.java deleted file mode 100644 index 893cf28..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/Command.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -/** - * - * @author jmarranz - */ -public abstract class Command -{ - protected JProxyShellProcessor parent; - protected String name; - - public Command(JProxyShellProcessor parent,String name) - { - this.parent = parent; - this.name = name; - } - - public static Command createCommand(JProxyShellProcessor parent,String cmd) - { - cmd = cmd.trim(); - if (cmd.equals("clear")) - { - return new CommandOther(parent,cmd); - } - else if (cmd.startsWith("delete")) - { - CommandDelete command = CommandDelete.createCommandDelete(parent,cmd); - if (command != null) - return command; - else - return new CommandError(parent); - } - else if (cmd.equals("display")) - { - return new CommandOther(parent,cmd); - } - else if (cmd.startsWith("edit")) - { - CommandEdit command = CommandEdit.createCommandEdit(parent,cmd); - if (command != null) - return command; - else - return new CommandError(parent); - } - else if (cmd.equals("exec")) - { - return new CommandOther(parent,cmd); - } - else if (cmd.equals("exit")) - { - return new CommandOther(parent,cmd); - } - else if (cmd.equals("help")) - { - return new CommandOther(parent,cmd); - } - else if (cmd.startsWith("insert")) - { - CommandInsert command = CommandInsert.createCommandInsert(parent,cmd); - if (command != null) - return command; - else - return new CommandError(parent); - } - else if (cmd.startsWith("load")) - { - CommandLoad command = CommandLoad.createCommandLoad(parent,cmd); - if (command != null) - return command; - else - return new CommandError(parent); - } - else if (cmd.equals("quit")) - { - return new CommandOther(parent,cmd); - } - else if (cmd.startsWith("save")) - { - CommandSave command = CommandSave.createCommandSave(parent,cmd); - if (command != null) - return command; - else - return new CommandError(parent); - } - - return null; // No es un comando - } - - protected static String getParameter(String cmdName,String cmd) - { - int pos = cmd.indexOf(cmdName + " "); - if (pos != 0) - return null; - pos = cmd.indexOf(' '); - String param = cmd.substring(pos + 1); - param = param.trim(); - return param; - } - - public abstract boolean run(); - - public abstract void runPostCommand(); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandCodeChangerBase.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandCodeChangerBase.java deleted file mode 100644 index 941ce04..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandCodeChangerBase.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - - -/** - * - * @author jmarranz - */ -public abstract class CommandCodeChangerBase extends Command -{ - public static final int ERROR_LAST_REQUIRED = -1; - public static final int ERROR_NO_LAST_LINE = -2; - public static final int ERROR_NOT_A_NUMBER = -3; - public static final int ERROR_VALUE_NOT_0_OR_NEGATIVE = -4; - public static final int ERROR_LINE_1_NOT_VALID = -5; - public static final int ERROR_OUT_OF_RANGE = -6; - - - protected int line; - - public CommandCodeChangerBase(JProxyShellProcessor parent,String name,int line) - { - super(parent,name); - this.line = line; - } - - public static int getLineFromParam(JProxyShellProcessor parent,String name,String cmd) - { - String param = getParameter(name,cmd); - if (param == null) - { - return ERROR_LAST_REQUIRED; - } - - int line; - if (param.equals("last")) - { - int lastLine = parent.getLastLine(); - if (lastLine == -1) - { - return ERROR_NO_LAST_LINE; - } - line = lastLine; - } - else - { - try - { - line = Integer.parseInt(param); - } - catch(NumberFormatException ex) - { - return ERROR_NOT_A_NUMBER; - } - // Ojo es el valor dado por el usuario (empezando en 1 y con línea vacía) - if (line <= 0) - { - return ERROR_VALUE_NOT_0_OR_NEGATIVE; - } - else if (line == 1) - { - return ERROR_LINE_1_NOT_VALID; - } - line -= JProxyShellProcessor.LINE_OFFSET; - - if (line >= parent.getCodeBuffer().size()) - { - return ERROR_OUT_OF_RANGE; - } - } - return line; - } - - - @Override - public boolean run() - { - return true; - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandDelete.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandDelete.java deleted file mode 100644 index e67d86d..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandDelete.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - - -/** - * - * @author jmarranz - */ -public class CommandDelete extends CommandCodeChangerBase -{ - public static final String NAME = "delete"; - - public CommandDelete(JProxyShellProcessor parent,int line) - { - super(parent,NAME,line); - } - - public static CommandDelete createCommandDelete(JProxyShellProcessor parent,String cmd) - { - int line = getLineFromParam(parent,NAME,cmd); - if (line < 0) - { - switch(line) - { - case ERROR_LAST_REQUIRED: - System.out.println("Command error: parameter \"last\" or a line number is required"); - break; - case ERROR_NO_LAST_LINE: - System.out.println("Command error: no new or edited line code has been introduced"); - break; - case ERROR_NOT_A_NUMBER: - System.out.println("Command error: line value is not a number"); - break; - case ERROR_VALUE_NOT_0_OR_NEGATIVE: - System.out.println("Command error: line value cannot be 0 or negative"); - break; - case ERROR_LINE_1_NOT_VALID: - System.out.println("Command error: line 1 is ever empty and cannot be deleted"); - break; - case ERROR_OUT_OF_RANGE: - System.out.println("Command error: line number out of range"); - break; - default: - // Para que se calle el FindBugs - } - return null; - } - - return new CommandDelete(parent,line); - } - - @Override - public void runPostCommand() - { - parent.removeCodeBuffer(line); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandEdit.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandEdit.java deleted file mode 100644 index ce85371..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandEdit.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - - -/** - * - * @author jmarranz - */ -public class CommandEdit extends CommandCodeChangerBase -{ - public static final String NAME = "edit"; - protected String codeLine; - - public CommandEdit(JProxyShellProcessor parent,int line,String codeLine) - { - super(parent,NAME,line); - this.codeLine = codeLine; - } - - public static CommandEdit createCommandEdit(JProxyShellProcessor parent,String cmd) - { - int line = getLineFromParam(parent,NAME,cmd); - if (line < 0) - { - switch(line) - { - case ERROR_LAST_REQUIRED: - System.out.println("Command error: parameter \"last\" or a line number is required"); - break; - case ERROR_NO_LAST_LINE: - System.out.println("Command error: no new or edited line code has been introduced"); - break; - case ERROR_NOT_A_NUMBER: - System.out.println("Command error: line value is not a number"); - break; - case ERROR_VALUE_NOT_0_OR_NEGATIVE: - System.out.println("Command error: line value cannot be 0 or negative"); - break; - case ERROR_LINE_1_NOT_VALID: - System.out.println("Command error: line 1 is ever empty and cannot be edited"); - break; - case ERROR_OUT_OF_RANGE: - System.out.println("Command error: line number out of range"); - break; - default: - // Para que se calle el FindBugs - } - return null; - } - - String codeLine = parent.getCodeBuffer().get(line); - return new CommandEdit(parent,line,codeLine); - } - - @Override - public void runPostCommand() - { - parent.getKeyboard().type(codeLine); - parent.setLineEditing(line); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandError.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandError.java deleted file mode 100644 index 0465597..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandError.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -/** - * - * @author jmarranz - */ -public class CommandError extends Command -{ - public CommandError(JProxyShellProcessor parent) - { - super(parent, "ERROR"); - } - - @Override - public boolean run() - { - return false; - } - - @Override - public void runPostCommand() - { - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandInsert.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandInsert.java deleted file mode 100644 index cad94fd..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandInsert.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - - -/** - * - * @author jmarranz - */ -public class CommandInsert extends CommandCodeChangerBase -{ - public static final String NAME = "insert"; - - public CommandInsert(JProxyShellProcessor parent,int line) - { - super(parent,NAME,line); - } - - public static CommandInsert createCommandInsert(JProxyShellProcessor parent,String cmd) - { - int line = getLineFromParam(parent,NAME,cmd); - if (line < 0) - { - switch(line) - { - case ERROR_LAST_REQUIRED: - System.out.println("Command error: parameter \"last\" or a line number is required"); - break; - case ERROR_NO_LAST_LINE: - System.out.println("Command error: no new or edited line code has been introduced"); - break; - case ERROR_NOT_A_NUMBER: - System.out.println("Command error: line value is not a number"); - break; - case ERROR_VALUE_NOT_0_OR_NEGATIVE: - System.out.println("Command error: line value cannot be 0 or negative"); - break; - case ERROR_LINE_1_NOT_VALID: - System.out.println("Command error: line 1 is ever empty and no code can be inserted before"); - break; - case ERROR_OUT_OF_RANGE: - System.out.println("Command error: line number out of range"); - break; - default: - // Para que se calle el FindBugs - } - return null; - } - - return new CommandInsert(parent,line); - } - - @Override - public void runPostCommand() - { - parent.insertCodeBuffer(line,""); - parent.setLineEditing(line); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandLoad.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandLoad.java deleted file mode 100644 index b30910d..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandLoad.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import java.io.File; -import java.net.URI; -import java.net.URL; -import java.util.LinkedList; -import java.util.Scanner; - -/** - * - * @author jmarranz - */ -public class CommandLoad extends Command -{ - public static final String NAME = "load"; - protected String url; - - public CommandLoad(JProxyShellProcessor parent,String url) - { - super(parent,NAME); - this.url = url; - } - - public static CommandLoad createCommandLoad(JProxyShellProcessor parent,String cmd) - { - String url = getParameter(NAME,cmd); - if (url == null) - { - System.out.println("Command error: parameter is required"); - return null; - } - - return new CommandLoad(parent,url); - } - - @Override - public boolean run() - { - try - { - byte[] content; - URI uri = new URI(url); - if (uri.getScheme() == null) // Archivo - { - File file = new File(url); - content = JProxyUtil.readFile(file); - } - else // URL (incluyendo file:///...) - { - URL urlObj = new URL(url); - content = JProxyUtil.readURL(urlObj); // Como no conocemos encoding... - } - - String code = new String(content,parent.getEncoding()); // Como no conocemos encoding... - LinkedList lines = new LinkedList(); - Scanner scanner = new Scanner(code); - while (scanner.hasNextLine()) - { - String line = scanner.nextLine(); - lines.add(line); - } - - parent.setCodeBuffer(lines); - return true; - } - catch (Exception ex) - { - ex.printStackTrace(); - return false; - } - } - - @Override - public void runPostCommand() - { - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandOther.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandOther.java deleted file mode 100644 index e39c52c..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandOther.java +++ /dev/null @@ -1,110 +0,0 @@ - -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import com.innowhere.relproxy.RelProxyException; - -/** - * - * @author jmarranz - */ -public class CommandOther extends Command -{ - public CommandOther(JProxyShellProcessor parent,String name) - { - super(parent,name); - } - - @Override - public boolean run() - { - if (name.equals("clear")) - { - commandClear(); - } - else if (name.equals("display")) - { - commandDisplay(); - } - else if (name.equals("exec")) - { - commandExec(); - } - else if (name.equals("exit")) - { - commandExit(); - } - else if (name.equals("help")) - { - commandHelp(); - } - else if (name.equals("quit")) - { - commandExit(); - } - else throw new RelProxyException("Internal Error"); - - return true; - } - - @Override - public void runPostCommand() - { - } - - private void commandClear() - { - parent.clearCodeBuffer(); - } - - private void commandExit() - { - System.exit(0); - } - - private void commandDisplay() - { - System.out.println("001>"); // La primera línea es siempre vacía porque en ella es donde ponemos el "public class /_jproxyShellInMemoryClass_ { " que el usuario ignora, así al dar error el número de línea será correcto respecto al "display" - - int i = 2; - for(String line : parent.getCodeBuffer()) - { - for(int j = 0; j < 3 - String.valueOf(i).length(); j++) System.out.print("0"); - System.out.print(i + ">"); - System.out.print(line); - System.out.println(); - i++; - } - } - - private void commandExec() - { - parent.executeCodeBuffer(); - } - - private void commandHelp() - { - System.out.println("Everything you write in the prompt is added to a code buffer, code buffer is compiled on the fly and executed by exec command, unless a command is detected"); - System.out.println(""); - System.out.println("Available commands:"); - System.out.println(" clear"); - System.out.println(" Clears the buffer"); - System.out.println(" display"); - System.out.println(" Shows the buffer content"); - System.out.println(" edit last | "); - System.out.println(" Edits the last introduced line code or the specified line number"); - System.out.println(" exec"); - System.out.println(" Compile and execute the buffer content"); - System.out.println(" exit"); - System.out.println(" Exits shell"); - System.out.println(" help"); - System.out.println(" This command"); - System.out.println(" insert last | "); - System.out.println(" Insert the next line of code before the last introduced line or the specified line number"); - System.out.println(" load | "); - System.out.println(" Load a file or URL into the buffer"); - System.out.println(" quit"); - System.out.println(" Same as exit"); - System.out.println(" save "); - System.out.println(" Save the current buffer to a file"); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandSave.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandSave.java deleted file mode 100644 index 472d5f5..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/CommandSave.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import java.io.File; -import java.util.List; - -/** - * - * @author jmarranz - */ -public class CommandSave extends Command -{ - public static final String NAME = "save"; - protected String path; - - public CommandSave(JProxyShellProcessor parent,String url) - { - super(parent,NAME); - this.path = url; - } - - public static CommandSave createCommandSave(JProxyShellProcessor parent,String cmd) - { - String url = getParameter(NAME,cmd); - if (url == null) - { - System.out.println("Command error: parameter is required"); - return null; - } - - return new CommandSave(parent,url); - } - - @Override - public boolean run() - { - try - { - List codeBuffer = parent.getCodeBuffer(); - StringBuilder code = new StringBuilder(); - for(String line : codeBuffer) - { - code.append(line); - code.append("\n"); - } - byte[] content = code.toString().getBytes(parent.getEncoding()); // Como no conocemos encoding... - JProxyUtil.saveFile(new File(path),content); - return true; - } - catch (Exception ex) - { - ex.printStackTrace(); - return false; - } - } - - @Override - public void runPostCommand() - { - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/JProxyShellProcessor.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/JProxyShellProcessor.java deleted file mode 100644 index 4e80200..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/JProxyShellProcessor.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import com.innowhere.relproxy.RelProxy; -import com.innowhere.relproxy.RelProxyException; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.JProxyEngine; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.JProxyCompilationException; -import com.innowhere.relproxy.impl.jproxy.shell.JProxyShellInteractiveImpl; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Scanner; - -/** - * - * @author jmarranz - */ -public class JProxyShellProcessor -{ - public static final int LINE_OFFSET = 2; // El índice en codeBuffer + este valor = al valor de la línea que se muestra al usuario, hay que tener en cuenta que contamos desde uno y la primera línea es siempre vacía - - protected JProxyShellInteractiveImpl parent; - protected Charset encoding = Charset.defaultCharset(); - protected ArrayList codeBuffer = new ArrayList(20); - protected Keyboard keyboard = KeyboardNotUsingClipboard.create(encoding); - protected int lastLine = -1; // Indice respecto a codeBuffer - protected int lineEditing = -1; // Indice respecto a codeBuffer - protected long codeBufferModTimestamp = 0; - protected long lastCodeCompiledTimestamp = 0; - - public JProxyShellProcessor(JProxyShellInteractiveImpl parent) - { - this.parent = parent; - } - - public Keyboard getKeyboard() - { - return keyboard; - } - - public Charset getEncoding() - { - return encoding; - } - - public int getLastLine() - { - return lastLine; - } - - public void setLineEditing(int lineEditing) - { - this.lineEditing = lineEditing; - } - - public void test() - { - try { Thread.sleep(2); } catch (InterruptedException ex){ } - execute("System.out.println(\"Hello World\");"); // "Object o = null; o.equals(null);" - } - - public void loop() - { - System.out.println("RelProxy Java Shell v" + RelProxy.getVersion()); - System.out.println("Write help for help"); - - Scanner sc = new Scanner(System.in,encoding.name()); // No encuentro nada interesante en http://docs.oracle.com/javase/6/docs/api/java/io/Console.html - System.out.print(">"); - while(true) - { - String line = sc.nextLine(); - Command command = Command.createCommand(this,line); - if (command != null) - { - if (command instanceof CommandError) // Era un comando pero con params erróneos - { - // Nada que hacer - System.out.print(">"); - } - else - { - boolean success = command.run(); - - System.out.print(">"); - - if (success) - { - command.runPostCommand(); // Lo normal es que no haga nada - } - } - } - else - { - if (lineEditing != -1) - { - setCodeBuffer(lineEditing, line); - this.lineEditing = -1; - } - else - { - addCodeBuffer(line); - } - - System.out.print(">"); - } - } - } - - public List getCodeBuffer() - { - return Collections.unmodifiableList(codeBuffer); - } - - public void setCodeBuffer(int index,String line) - { - codeBuffer.set(index, line); - this.codeBufferModTimestamp = System.currentTimeMillis(); - this.lastLine = index; - } - - public void setCodeBuffer(LinkedList codeBuffer) - { - codeBuffer.clear(); - this.codeBuffer.addAll(codeBuffer); - this.codeBufferModTimestamp = System.currentTimeMillis(); - this.lastLine = codeBuffer.size() - 1; - } - - public void insertCodeBuffer(int index,String line) - { - codeBuffer.add(index, line); - this.codeBufferModTimestamp = System.currentTimeMillis(); - this.lastLine = index; - } - - public void addCodeBuffer(String line) - { - codeBuffer.add(line); - this.codeBufferModTimestamp = System.currentTimeMillis(); - this.lastLine = codeBuffer.size() - 1; - } - - public void removeCodeBuffer(int index) - { - codeBuffer.remove(index); - this.codeBufferModTimestamp = System.currentTimeMillis(); - this.lastLine = -1; // La hemos eliminado, no existe ya - } - - public void clearCodeBuffer() - { - codeBuffer.clear(); - this.codeBufferModTimestamp = System.currentTimeMillis(); - this.lastLine = - 1; - } - - public void executeCodeBuffer() - { - StringBuilder code = new StringBuilder(); - for(String line : codeBuffer) - { - code.append(line); - code.append("\n"); - } - execute(code.toString()); - } - - private void execute(String code) - { - // Este código no es thread safe ni falta que hace. - - ClassDescriptorSourceScript classDescSourceScript = parent.getClassDescriptorSourceScript(); - - if (codeBufferModTimestamp > lastCodeCompiledTimestamp) // Incluimos el = por si acaso va todo muy seguido - { - parent.getSourceScriptInMemory().setScriptCode(code,codeBufferModTimestamp); - // Recuerda que cada vez que se obtiene el timestamp se llama a System.currentTimeMillis(), es imposible que el usuario haga algo en menos de 1ms - - JProxyEngine engine = parent.getJProxyEngine(); - - ClassDescriptorSourceScript classDescSourceScript2 = null; - try - { - classDescSourceScript2 = engine.detectChangesInSourcesAndReload(); - } - catch(JProxyCompilationException ex) - { - System.out.println("Compilation error"); - return; - } - - if (classDescSourceScript2 != classDescSourceScript) - throw new RelProxyException("Internal Error"); - - this.lastCodeCompiledTimestamp = System.currentTimeMillis(); - if (lastCodeCompiledTimestamp == codeBufferModTimestamp) // Demasiado rápido compilando - { - // El ser humano es muy raro y es posible que a alguien se le ocurra usar el shell de forma automatizada y se genere un siguiente cambio en el - // código fuente tan rápido que no cambie el ms, así nos aseguramos con total rotundidad que la modificación posterior de código fuente su timestamp es MAYOR que el de compilación último - try { Thread.sleep(1); } catch (InterruptedException ex) { throw new RelProxyException(ex); } - } - } - - try - { - classDescSourceScript.callMainMethod(new LinkedList()); - } - catch(Throwable ex) - { - ex.printStackTrace(System.out); - } - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/Keyboard.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/Keyboard.java deleted file mode 100644 index 6401125..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/Keyboard.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import java.nio.charset.Charset; - -/** - * - * @author jmarranz - */ -public abstract class Keyboard -{ - public abstract void type(CharSequence characters); - - public static Keyboard create(Charset cs) - { - return KeyboardUsingClipboard.create(cs); - //return KeyboardNotUsingClipboard.create(cs); - } - - public static void test(String arg) - { - Charset charset = Charset.defaultCharset(); - Keyboard kbd = create(charset); - kbd.type(arg); - } -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/KeyboardNotUsingClipboard.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/KeyboardNotUsingClipboard.java deleted file mode 100644 index 3a21838..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/KeyboardNotUsingClipboard.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - - -import com.innowhere.relproxy.RelProxyException; -import java.awt.AWTException; -import java.awt.Robot; -import static java.awt.event.KeyEvent.VK_0; -import static java.awt.event.KeyEvent.VK_1; -import static java.awt.event.KeyEvent.VK_2; -import static java.awt.event.KeyEvent.VK_3; -import static java.awt.event.KeyEvent.VK_4; -import static java.awt.event.KeyEvent.VK_5; -import static java.awt.event.KeyEvent.VK_6; -import static java.awt.event.KeyEvent.VK_7; -import static java.awt.event.KeyEvent.VK_8; -import static java.awt.event.KeyEvent.VK_9; -import static java.awt.event.KeyEvent.VK_A; -import static java.awt.event.KeyEvent.VK_B; -import static java.awt.event.KeyEvent.VK_C; -import static java.awt.event.KeyEvent.VK_D; -import static java.awt.event.KeyEvent.VK_E; -import static java.awt.event.KeyEvent.VK_F; -import static java.awt.event.KeyEvent.VK_G; -import static java.awt.event.KeyEvent.VK_H; -import static java.awt.event.KeyEvent.VK_I; -import static java.awt.event.KeyEvent.VK_J; -import static java.awt.event.KeyEvent.VK_K; -import static java.awt.event.KeyEvent.VK_L; -import static java.awt.event.KeyEvent.VK_M; -import static java.awt.event.KeyEvent.VK_N; -import static java.awt.event.KeyEvent.VK_NUMPAD0; -import static java.awt.event.KeyEvent.VK_NUMPAD1; -import static java.awt.event.KeyEvent.VK_NUMPAD2; -import static java.awt.event.KeyEvent.VK_NUMPAD3; -import static java.awt.event.KeyEvent.VK_NUMPAD4; -import static java.awt.event.KeyEvent.VK_NUMPAD5; -import static java.awt.event.KeyEvent.VK_NUMPAD6; -import static java.awt.event.KeyEvent.VK_NUMPAD7; -import static java.awt.event.KeyEvent.VK_NUMPAD8; -import static java.awt.event.KeyEvent.VK_NUMPAD9; -import static java.awt.event.KeyEvent.VK_O; -import static java.awt.event.KeyEvent.VK_P; -import static java.awt.event.KeyEvent.VK_Q; -import static java.awt.event.KeyEvent.VK_R; -import static java.awt.event.KeyEvent.VK_S; -import static java.awt.event.KeyEvent.VK_SHIFT; -import static java.awt.event.KeyEvent.VK_SPACE; -import static java.awt.event.KeyEvent.VK_T; -import static java.awt.event.KeyEvent.VK_U; -import static java.awt.event.KeyEvent.VK_V; -import static java.awt.event.KeyEvent.VK_W; -import static java.awt.event.KeyEvent.VK_X; -import static java.awt.event.KeyEvent.VK_Y; -import static java.awt.event.KeyEvent.VK_Z; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; - -/** - * http://stackoverflow.com/questions/1248510/convert-string-to-keyevents - * http://en.wikipedia.org/wiki/Unicode_input#Hexadecimal_code_input - * http://stackoverflow.com/questions/9814701/accent-with-robot-keypress - * - * @author jmarranz - */ -public abstract class KeyboardNotUsingClipboard extends Keyboard -{ - protected final Robot robot; - protected Charset cs; - - - - public KeyboardNotUsingClipboard(Charset cs) - { - this.cs = cs; - try - { - this.robot = new Robot(); - } - catch (AWTException ex) - { - throw new RelProxyException(ex); - } - } - - public static KeyboardNotUsingClipboard create(Charset cs) - { - String osName = System.getProperty("os.name").toLowerCase(); - if (osName.contains("windows")) return new WindowUnicodeKeyboard(cs); - else if (osName.contains("os x")) return new MacOSXUnicodeKeyboard(cs);// https://developer.apple.com/library/mac/technotes/tn2002/tn2110.html - else return new LinuxUnicodeKeyboard(cs); - } - - private int[] getUnicodeInt(char character) - { - if (isUseCodePoint()) - { - char[] charArray = new char[]{character}; - int count = Character.codePointCount(charArray, 0,charArray.length); - int[] res = new int[count]; - for(int i = 0; i < count; i++) - res[i] = Character.codePointAt(charArray,i); - return res; - } - else - { - ByteBuffer buffer = cs.encode("" + character); - int size = buffer.limit(); - int[] res = new int[size]; - for(int i = 0; i < size; i++) - { - byte b = buffer.get(); - int bi = b & 0x000000FF; - res[i] = bi; - } - return res; - } - } - - protected String getUnicodeDigits(char character,int radix) - { - int[] uds = getUnicodeInt(character); - StringBuilder res = new StringBuilder(); - for(int i = 0; i < uds.length; i++) - { - int ud = uds[i]; - String digits; - if (radix == 10) - digits = Integer.toString(ud); - else - digits = Integer.toString(ud,radix); // Si es 16 es hexadecimal - res.append(digits); - } - return res.toString(); - } - - protected void typeNumPad(int digit) { - switch (digit) { - case 0: doType(VK_NUMPAD0); break; - case 1: doType(VK_NUMPAD1); break; - case 2: doType(VK_NUMPAD2); break; - case 3: doType(VK_NUMPAD3); break; - case 4: doType(VK_NUMPAD4); break; - case 5: doType(VK_NUMPAD5); break; - case 6: doType(VK_NUMPAD6); break; - case 7: doType(VK_NUMPAD7); break; - case 8: doType(VK_NUMPAD8); break; - case 9: doType(VK_NUMPAD9); break; - default: // Para que se calle el FindBugs - } - } - - - public void type(CharSequence characters) { - int length = characters.length(); - for (int i = 0; i < length; i++) { - char character = characters.charAt(i); - type(character); - } - } - - public boolean type(char character) { - - // He quitado todos los símbolos que son susceptibles de cambiar según el teclado pues hay que acertar exactamente la combinación de teclas del teclado - // concreto o da error, ej no vale emitir VK_COLON únicamente si en el teclado concreto es necesario un SHIFT - // En la clase derivada se procesan los caracteres no contemplados aquí - - switch (character) { - case 'a': doType(VK_A); break; - case 'b': doType(VK_B); break; - case 'c': doType(VK_C); break; - case 'd': doType(VK_D); break; - case 'e': doType(VK_E); break; - case 'f': doType(VK_F); break; - case 'g': doType(VK_G); break; - case 'h': doType(VK_H); break; - case 'i': doType(VK_I); break; - case 'j': doType(VK_J); break; - case 'k': doType(VK_K); break; - case 'l': doType(VK_L); break; - case 'm': doType(VK_M); break; - case 'n': doType(VK_N); break; - case 'o': doType(VK_O); break; - case 'p': doType(VK_P); break; - case 'q': doType(VK_Q); break; - case 'r': doType(VK_R); break; - case 's': doType(VK_S); break; - case 't': doType(VK_T); break; - case 'u': doType(VK_U); break; - case 'v': doType(VK_V); break; - case 'w': doType(VK_W); break; - case 'x': doType(VK_X); break; - case 'y': doType(VK_Y); break; - case 'z': doType(VK_Z); break; - case 'A': doType(VK_SHIFT, VK_A); break; - case 'B': doType(VK_SHIFT, VK_B); break; - case 'C': doType(VK_SHIFT, VK_C); break; - case 'D': doType(VK_SHIFT, VK_D); break; - case 'E': doType(VK_SHIFT, VK_E); break; - case 'F': doType(VK_SHIFT, VK_F); break; - case 'G': doType(VK_SHIFT, VK_G); break; - case 'H': doType(VK_SHIFT, VK_H); break; - case 'I': doType(VK_SHIFT, VK_I); break; - case 'J': doType(VK_SHIFT, VK_J); break; - case 'K': doType(VK_SHIFT, VK_K); break; - case 'L': doType(VK_SHIFT, VK_L); break; - case 'M': doType(VK_SHIFT, VK_M); break; - case 'N': doType(VK_SHIFT, VK_N); break; - case 'O': doType(VK_SHIFT, VK_O); break; - case 'P': doType(VK_SHIFT, VK_P); break; - case 'Q': doType(VK_SHIFT, VK_Q); break; - case 'R': doType(VK_SHIFT, VK_R); break; - case 'S': doType(VK_SHIFT, VK_S); break; - case 'T': doType(VK_SHIFT, VK_T); break; - case 'U': doType(VK_SHIFT, VK_U); break; - case 'V': doType(VK_SHIFT, VK_V); break; - case 'W': doType(VK_SHIFT, VK_W); break; - case 'X': doType(VK_SHIFT, VK_X); break; - case 'Y': doType(VK_SHIFT, VK_Y); break; - case 'Z': doType(VK_SHIFT, VK_Z); break; - // case '`': doType(VK_BACK_QUOTE); break; - case '0': doType(VK_0); break; - case '1': doType(VK_1); break; - case '2': doType(VK_2); break; - case '3': doType(VK_3); break; - case '4': doType(VK_4); break; - case '5': doType(VK_5); break; - case '6': doType(VK_6); break; - case '7': doType(VK_7); break; - case '8': doType(VK_8); break; - case '9': doType(VK_9); break; - /* - case '-': doType(VK_MINUS); break; - case '=': doType(VK_EQUALS); break; - case '~': doType(VK_SHIFT, VK_BACK_QUOTE); break; - case '!': doType(VK_EXCLAMATION_MARK); break; - case '@': doType(VK_AT); break; - case '#': doType(VK_NUMBER_SIGN); break; - case '$': doType(VK_DOLLAR); break; - case '%': doType(VK_SHIFT, VK_5); break; - case '^': doType(VK_CIRCUMFLEX); break; - case '&': doType(VK_AMPERSAND); break; - case '*': doType(VK_ASTERISK); break; - case '(': doType(VK_LEFT_PARENTHESIS); break; - case ')': doType(VK_RIGHT_PARENTHESIS); break; - case '_': doType(VK_UNDERSCORE); break; - case '+': doType(VK_PLUS); break; - case '\t': doType(VK_TAB); break; - case '\n': doType(VK_ENTER); break; - case '[': doType(VK_OPEN_BRACKET); break; - case ']': doType(VK_CLOSE_BRACKET); break; - case '\\': doType(VK_BACK_SLASH); break; - case '{': doType(VK_SHIFT, VK_OPEN_BRACKET); break; - case '}': doType(VK_SHIFT, VK_CLOSE_BRACKET); break; - case '|': doType(VK_SHIFT, VK_BACK_SLASH); break; - case ';': doType(VK_SEMICOLON); break; - case ':': doType(VK_COLON); break; - case '\'': doType(VK_QUOTE); break; - case '"': doType(VK_QUOTEDBL); break; - case ',': doType(VK_COMMA); break; - case '<': doType(VK_LESS); break; - case '.': doType(VK_PERIOD); break; - case '>': doType(VK_GREATER); break; - case '/': doType(VK_SLASH); break; - case '?': doType(VK_SHIFT, VK_SLASH); break; - */ - case ' ': doType(VK_SPACE); break; - default: - return false; - } - - return true; - } - - protected void doType(int... keyCodes) { - doTypeArr(keyCodes); - } - - private void doTypeArr(int[] keyCodes) { - int length = keyCodes.length; - if (length == 1) - { - doType(keyCodes[0]); - } - else // 2 - { - robot.keyPress(keyCodes[0]); - robot.keyPress(keyCodes[1]); - - robot.keyRelease(keyCodes[1]); - robot.keyRelease(keyCodes[0]); - } - } - - protected void doType(int keyCode) - { - robot.keyPress(keyCode); - robot.keyRelease(keyCode); - } - - public abstract boolean isUseCodePoint(); - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/KeyboardUsingClipboard.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/KeyboardUsingClipboard.java deleted file mode 100644 index b319695..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/KeyboardUsingClipboard.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - - -import com.innowhere.relproxy.RelProxyException; -import java.awt.AWTException; -import java.awt.Robot; -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.ClipboardOwner; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.event.KeyEvent; -import java.nio.charset.Charset; - -/** - * http://stackoverflow.com/questions/1248510/convert-string-to-keyevents - * http://en.wikipedia.org/wiki/Unicode_input#Hexadecimal_code_input - * http://stackoverflow.com/questions/9814701/accent-with-robot-keypress - * - * @author jmarranz - */ -public class KeyboardUsingClipboard extends Keyboard implements ClipboardOwner -{ - protected final Robot robot; - protected Charset cs; - - - public KeyboardUsingClipboard(Charset cs) - { - this.cs = cs; - try - { - this.robot = new Robot(); - } - catch (AWTException ex) - { - throw new RelProxyException(ex); - } - } - - public static KeyboardUsingClipboard create(Charset cs) - { - return new KeyboardUsingClipboard(cs); - } - - - @Override - public void type(CharSequence characters) - { - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - StringSelection stringSelection = new StringSelection( characters.toString() ); - clipboard.setContents(stringSelection, this); - - robot.keyPress(KeyEvent.VK_CONTROL); - robot.keyPress(KeyEvent.VK_V); - robot.keyRelease(KeyEvent.VK_V); - robot.keyRelease(KeyEvent.VK_CONTROL); - } - - @Override - public void lostOwnership(Clipboard clipboard, Transferable contents) - { - - } - -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/LinuxUnicodeKeyboard.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/LinuxUnicodeKeyboard.java deleted file mode 100644 index 0957db5..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/LinuxUnicodeKeyboard.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import static java.awt.event.KeyEvent.VK_CONTROL; -import static java.awt.event.KeyEvent.VK_SHIFT; -import static java.awt.event.KeyEvent.VK_U; -import java.nio.charset.Charset; - -/** - * - * http://superuser.com/questions/59418/how-to-type-special-characters-in-linux - * - * @author jmarranz - */ -public class LinuxUnicodeKeyboard extends KeyboardNotUsingClipboard -{ - public LinuxUnicodeKeyboard(Charset cs) - { - super(cs); - } - - @Override - public boolean isUseCodePoint() - { - return true; - } - - @Override - public boolean type(char character) - { - if (super.type(character)) - return true; - - String unicodeDigits = getUnicodeDigits(character,16); // En hexadecimal - - robot.keyPress(VK_CONTROL); - robot.keyPress(VK_SHIFT); - - doType(VK_U); // 'u' indica que después viene un valor unicode hexadecimal - - // Pero dejamos pulsadas CTRL y SHIFT mientras - // Ejemplo: 266A es una nota de solfeo - try - { - for (int i = 0; i < unicodeDigits.length(); i++) - { - char c = unicodeDigits.charAt(i); - if (Character.isDigit(c)) - typeNumPad(Integer.parseInt(unicodeDigits.substring(i, i + 1))); - else - type(c); - } - } - finally - { - robot.keyRelease(VK_CONTROL); - robot.keyRelease(VK_SHIFT); - } - - return true; - } - -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/MacOSXUnicodeKeyboard.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/MacOSXUnicodeKeyboard.java deleted file mode 100644 index f0ee4aa..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/MacOSXUnicodeKeyboard.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import static java.awt.Event.ALT_MASK; -import java.nio.charset.Charset; - -/** - * - * https://discussions.apple.com/thread/1899290 - * http://superuser.com/questions/13086/how-do-you-type-unicode-characters-using-hexadecimal-codes (buscar OS X) - * http://controlyourmac.com/2012/05/understanding-mac-keyboard.html - * - * @author jmarranz - */ -public class MacOSXUnicodeKeyboard extends KeyboardNotUsingClipboard -{ - public MacOSXUnicodeKeyboard(Charset cs) - { - super(cs); - } - - @Override - public boolean isUseCodePoint() - { - return true; - } - - @Override - public boolean type(char character) - { - if (super.type(character)) - return true; - - String unicodeDigits = getUnicodeDigits(character,16); // En hexadecimal - - robot.keyPress(ALT_MASK); // "Since the ALT_MASK modifier is the Option key in OS X" https://developer.apple.com/library/mac/documentation/java/conceptual/java14development/07-NativePlatformIntegration/NativePlatformIntegration.html - - try - { - for (int i = 0; i < unicodeDigits.length(); i++) { - type(unicodeDigits.charAt(i)); - } - } - finally - { - robot.keyRelease(ALT_MASK); - } - - return true; - } - -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/WindowUnicodeKeyboard.java b/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/WindowUnicodeKeyboard.java deleted file mode 100644 index 1deeb94..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/shell/inter/WindowUnicodeKeyboard.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.innowhere.relproxy.impl.jproxy.shell.inter; - -import static java.awt.event.KeyEvent.VK_ALT; -import java.nio.charset.Charset; - -/** - * http://stackoverflow.com/questions/1248510/convert-string-to-keyevents - * - * @author jmarranz - */ -public class WindowUnicodeKeyboard extends KeyboardNotUsingClipboard -{ - public WindowUnicodeKeyboard(Charset cs) - { - super(cs); - } - - @Override - public boolean isUseCodePoint() - { - return false; - } - - @Override - public boolean type(char character) - { - if (super.type(character)) - return true; - - // En Windows usar mintty porque usando la consola de MSYS por sí misma, que es realmente la de Windows, hay problemas con el set de caracteres, pues sería Cp1252 para Java pero Cp850 para la consola y salen mal por tanto los caracteres no ASCII - - - String unicodeDigits = getUnicodeDigits(character,10); // En DECIMAL - - robot.keyPress(VK_ALT); - try - { - for (int i = 0; i < unicodeDigits.length(); i++) { - typeNumPad(Integer.parseInt(unicodeDigits.substring(i, i + 1))); - } - } - finally - { - robot.keyRelease(VK_ALT); - } - - return true; - } - - -} \ No newline at end of file diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyCompilerListener.java b/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyCompilerListener.java deleted file mode 100644 index 397a907..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyCompilerListener.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import java.io.File; - -/** - * Is the interface to monitor the files being compiled. - * - * @see JProxyConfig#setJProxyCompilerListener(JProxyCompilerListener) - * @author Jose Maria Arranz Santamaria - */ -public interface JProxyCompilerListener -{ - public void beforeCompile(File file); - public void afterCompile(File file); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyDiagnosticsListener.java b/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyDiagnosticsListener.java deleted file mode 100644 index 9b0ea1f..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyDiagnosticsListener.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; - -/** - * Is the interface to implement diagnostic listeners to capture compilation errors and warnings. - * - * @see JProxyConfig#setJProxyDiagnosticsListener(JProxyDiagnosticsListener) - * @author Jose Maria Arranz Santamaria - */ -public interface JProxyDiagnosticsListener -{ - /** - * This method is called when compilation Java code has generated diagnostics. - * - * @param diagnostics the list of diagnostics. - */ - public void onDiagnostics(DiagnosticCollector diagnostics); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyInputSourceFileExcludedListener.java b/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyInputSourceFileExcludedListener.java deleted file mode 100644 index 196bfb9..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyInputSourceFileExcludedListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import java.io.File; - -/** - * This interface is provided to developers to implement excluding rules to filter source files not to be part of the hot reloading system in spite of included in input paths - * - * @see JProxyConfig#setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener) - * @author Jose Maria Arranz Santamaria - */ -public interface JProxyInputSourceFileExcludedListener -{ - /** - * This method is called per file when going to be managed by the hot reloading system. - * - * @param file the file to be managed. - * @param rootFolderOfSources the folder root of sources where this file is located. - * @return true whether the file must be ignored. - */ - public boolean isExcluded(File file,File rootFolderOfSources); -} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyShell.java b/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyShell.java deleted file mode 100644 index ec1ee09..0000000 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyShell.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.impl.jproxy.shell.JProxyShellImpl; - -/** - * Is the main class to execute shell scripting based on Java. - * - *

You are not going to use directly this class, use instead jproxysh command line.

- * - * @author Jose Maria Arranz Santamaria - */ -public class JProxyShell -{ - /** - * The main method. - * - * @param args arguments with the necessary data to initialize and executing the provided script. - */ - public static void main(String[] args) - { - JProxyShellImpl.main(args); - } -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyCodeSnippetCompleteClassTest.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyCodeSnippetCompleteClassTest.java deleted file mode 100644 index 2dd7a06..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyCodeSnippetCompleteClassTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package com.innowhere.relproxy.jproxy; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -/** - * - * @author jmarranz - */ -public class JProxyCodeSnippetCompleteClassTest -{ - public static boolean RESULT; - - public JProxyCodeSnippetCompleteClassTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - RESULT = false; - } - - @After - public void tearDown() - { - RESULT = false; - } - - @Test - public void test_code_snippet_complete_class() - { - String compilationOptions = "-source 1.6 -target 1.6"; - - String[] args = new String[] - { - "-c", - "public class _jproxyMainClass_ { ", - " public static void main(String[] args) { ", - " System.out.print(\"This code snippet says: \");", - " System.out.println(\"Hello World!!\");", - JProxyCodeSnippetCompleteClassTest.class.getName() + ".RESULT = true;", - " }", - "}", - "-DcompilationOptions=" + compilationOptions - }; - - JProxyShell.main(args); - - assertTrue(RESULT); - - } -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyCodeSnippetTest.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyCodeSnippetTest.java deleted file mode 100644 index b692a63..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyCodeSnippetTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package com.innowhere.relproxy.jproxy; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -/** - * - * @author jmarranz - */ -public class JProxyCodeSnippetTest -{ - public static boolean RESULT; - - public JProxyCodeSnippetTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - RESULT = false; - } - - @After - public void tearDown() - { - RESULT = false; - } - - @Test - public void test_code_snippet() - { - String compilationOptions = "-source 1.6 -target 1.6"; - - String[] args = new String[] - { - "-c", - "System.out.print(\"This code snippet says: \");", - "System.out.println(\"Hello World!!\");", - JProxyCodeSnippetTest.class.getName() + ".RESULT = true;", - "-DcompilationOptions=" + compilationOptions - }; - - JProxyShell.main(args); - - assertTrue(RESULT); - } -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellCompleteClassTest.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellCompleteClassTest.java deleted file mode 100644 index 91fdbbe..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellCompleteClassTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.jproxy.util.JProxyTestUtil; -import java.io.File; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * - * @author jmarranz - */ -public class JProxyJavaShellCompleteClassTest -{ - - - - public JProxyJavaShellCompleteClassTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - - } - - @After - public void tearDown() - { - - } - - - @Test - public void test_java_shell() - { - File projectFolder = JProxyTestUtil.getProjectFolder(); - File inputFolderFile = new File(projectFolder,JProxyTestUtil.RESOURCES_FOLDER); - File cacheClassFolderFile = new File(projectFolder,JProxyTestUtil.CACHE_CLASS_FOLDER); - - String inputPath = inputFolderFile.getAbsolutePath(); - String cacheClassFolder = cacheClassFolderFile.getAbsolutePath(); - String compilationOptions = "-source 1.6 -target 1.6"; - - String[] args = new String[] - { - inputPath + "/example_java_shell_complete_class", - "HELLO ", - "WORLD!", - "-DcacheClassFolder=" + cacheClassFolder, - "-DcompilationOptions=" + compilationOptions, - }; - - JProxyShell.main(args); - } - - -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellInteractiveTest.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellInteractiveTest.java deleted file mode 100644 index 92fc5d9..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellInteractiveTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package com.innowhere.relproxy.jproxy; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -/** - * - * @author jmarranz - */ -public class JProxyJavaShellInteractiveTest -{ - public JProxyJavaShellInteractiveTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - - } - - @After - public void tearDown() - { - - } - - @Test - public void test_java_shell_interactive() - { - String compilationOptions = "-source 1.6 -target 1.6"; - - String[] args = new String[] - { - "", // El args[0] esperado - "-DcompilationOptions=" + compilationOptions, - "-Dtest=true" - }; - - JProxyShell.main(args); - } -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellNormalClassTest.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellNormalClassTest.java deleted file mode 100644 index 31c819c..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellNormalClassTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.jproxy.util.JProxyTestUtil; -import java.io.File; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * - * @author jmarranz - */ -public class JProxyJavaShellNormalClassTest -{ - public JProxyJavaShellNormalClassTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - - } - - @After - public void tearDown() - { - - } - - - @Test - public void test_java_shell() - { - File projectFolder = JProxyTestUtil.getProjectFolder(); - File inputFolderFile = new File(projectFolder,JProxyTestUtil.RESOURCES_FOLDER); - File cacheClassFolderFile = new File(projectFolder,JProxyTestUtil.CACHE_CLASS_FOLDER); - - String inputPath = inputFolderFile.getAbsolutePath(); - String cacheClassFolder = cacheClassFolderFile.getAbsolutePath(); - String compilationOptions = "-source 1.6 -target 1.6"; - - String[] args = new String[] - { - inputPath + "/example_normal_class.java", - "HELLO ", - "WORLD!", - "-DcacheClassFolder=" + cacheClassFolder, - "-DcompilationOptions=" + compilationOptions - }; - - JProxyShell.main(args); - } - - -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellTest.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellTest.java deleted file mode 100644 index 05b8f11..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaShellTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.jproxy.util.JProxyTestUtil; -import java.io.File; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * - * @author jmarranz - */ -public class JProxyJavaShellTest -{ - - - - public JProxyJavaShellTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - - } - - @After - public void tearDown() - { - - } - - - @Test - public void test_java_shell() - { - File projectFolder = JProxyTestUtil.getProjectFolder(); - File inputFolderFile = new File(projectFolder,JProxyTestUtil.RESOURCES_FOLDER); - File cacheClassFolderFile = new File(projectFolder,JProxyTestUtil.CACHE_CLASS_FOLDER); - - String inputPath = inputFolderFile.getAbsolutePath(); - String cacheClassFolder = cacheClassFolderFile.getAbsolutePath(); - String compilationOptions = "-source 1.6 -target 1.6"; - - - String[] args = new String[] - { - inputPath + "/example_java_shell", - "HELLO ", - "WORLD!", - "-DcacheClassFolder=" + cacheClassFolder, - "-DcompilationOptions=" + compilationOptions - }; - - JProxyShell.main(args); - } - - -} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/util/JProxyTestUtil.java b/relproxy/src/test/java/com/innowhere/relproxy/jproxy/util/JProxyTestUtil.java deleted file mode 100644 index 51dc64d..0000000 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/util/JProxyTestUtil.java +++ /dev/null @@ -1,26 +0,0 @@ - -package com.innowhere.relproxy.jproxy.util; - -import java.io.File; -import java.net.URL; - -/** - * - * @author jmarranz - */ -public class JProxyTestUtil -{ - public static final String RESOURCES_FOLDER = "src/test/resources"; - public static final String CACHE_CLASS_FOLDER = "tmp/java_shell_test_classes"; - - public static File getProjectFolder() - { - String className = JProxyTestUtil.class.getName(); // com.innowhere.relproxy.jproxy.util.JProxyTestUtil - URL urlClass = JProxyTestUtil.class.getClassLoader().getResource(className.replace('.','/') + ".class"); - File fileClass = new File(urlClass.getPath()); - File projectFolder = fileClass.getParentFile(); - for(int i = 0; i < 7; i++) - projectFolder = projectFolder.getParentFile(); - return projectFolder; - } -} diff --git a/relproxy/test_cmd/_shared_test_java_shell_inter_and_snippet_launcher.sh b/relproxy/test_cmd/_shared_test_java_shell_inter_and_snippet_launcher.sh deleted file mode 100644 index 76419b9..0000000 --- a/relproxy/test_cmd/_shared_test_java_shell_inter_and_snippet_launcher.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -# restoring: -cd $TMP_PWD - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/target/classes -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - - - diff --git a/relproxy/test_cmd/_shared_test_java_shell_launcher.sh b/relproxy/test_cmd/_shared_test_java_shell_launcher.sh deleted file mode 100644 index 4d78f66..0000000 --- a/relproxy/test_cmd/_shared_test_java_shell_launcher.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - - -if [ -z "$JAVA_HOME" ]; then - echo Missing JAVA_HOME environment variable, exiting... - exit 1 -fi - -export PATH=$PATH:$PROJECT/bin -export CLASSPATH=$PROJECT/target/classes -export JAVA_OPTS="-client -Xmx100m" -# Nothing really required in JAVA_OPTS, just to test - -export JPROXYSH_CACHE_CLASS_FOLDER="$PROJECT/tmp/java_shell_test_classes" -export JPROXYSH_COMPILATION_OPTIONS="-source 1.6 -target 1.6" - - diff --git a/relproxy/test_cmd/fixesforunix.sh b/relproxy/test_cmd/fixesforunix.sh deleted file mode 100644 index 4b416b8..0000000 --- a/relproxy/test_cmd/fixesforunix.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -PROJECT=`dirname $0`/.. - -# set PROJECT env as absolute path -TMP_PWD=`pwd` -cd $PROJECT -PROJECT=`pwd` -cd $TMP_PWD - -dos2unix $PROJECT/test_cmd/*.sh -chmod +x $PROJECT/test_cmd/*.sh - -dos2unix $PROJECT/bin/jproxysh -chmod +x $PROJECT/bin/jproxysh - -dos2unix $PROJECT/src/test/resources/example_java_shell -chmod +x $PROJECT/src/test/resources/example_java_shell - -dos2unix $PROJECT/src/test/resources/example_java_shell -chmod +x $PROJECT/src/test/resources/example_java_shell_complete_class - diff --git a/relproxy/test_cmd/setupjava.sh b/relproxy/test_cmd/setupjava.sh deleted file mode 100644 index 33e3d0d..0000000 --- a/relproxy/test_cmd/setupjava.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -echo Note: execute this script with source cmd: source setupjava.sh - -# Change JAVA_HOME to your own installation folder - -export JAVA_HOME="/c/Archivos de programa/Java/jdk1.7.0_45/" - -echo Defined JAVA_HOME as $JAVA_HOME diff --git a/relproxy/test_cmd/test_java_shell_interactive_launcher.sh b/relproxy/test_cmd/test_java_shell_interactive_launcher.sh deleted file mode 100644 index b71016c..0000000 --- a/relproxy/test_cmd/test_java_shell_interactive_launcher.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_inter_and_snippet_launcher.sh - -jproxysh - diff --git a/relproxy/test_cmd/test_java_shell_launcher.sh b/relproxy/test_cmd/test_java_shell_launcher.sh deleted file mode 100644 index 23ec4f5..0000000 --- a/relproxy/test_cmd/test_java_shell_launcher.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_launcher.sh - -$PROJECT/src/test/resources/example_java_shell "HELLO " "WORLD!" - diff --git a/relproxy/test_cmd/test_java_shell_launcher_2.sh b/relproxy/test_cmd/test_java_shell_launcher_2.sh deleted file mode 100644 index d07a7cc..0000000 --- a/relproxy/test_cmd/test_java_shell_launcher_2.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_launcher.sh - -jproxysh $PROJECT/src/test/resources/example_java_shell "HELLO " "WORLD!" - diff --git a/relproxy/test_cmd/test_java_shell_launcher_complete_class.sh b/relproxy/test_cmd/test_java_shell_launcher_complete_class.sh deleted file mode 100644 index 983b86f..0000000 --- a/relproxy/test_cmd/test_java_shell_launcher_complete_class.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_launcher.sh - -$PROJECT/src/test/resources/example_java_shell_complete_class "HELLO " "WORLD!" - diff --git a/relproxy/test_cmd/test_java_shell_launcher_complete_class_2.sh b/relproxy/test_cmd/test_java_shell_launcher_complete_class_2.sh deleted file mode 100644 index 5536bfb..0000000 --- a/relproxy/test_cmd/test_java_shell_launcher_complete_class_2.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_launcher.sh - -jproxysh $PROJECT/src/test/resources/example_java_shell_complete_class "HELLO " "WORLD!" - diff --git a/relproxy/test_cmd/test_java_shell_launcher_normal_class.sh b/relproxy/test_cmd/test_java_shell_launcher_normal_class.sh deleted file mode 100644 index 645c5f1..0000000 --- a/relproxy/test_cmd/test_java_shell_launcher_normal_class.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_launcher.sh - -jproxysh $PROJECT/src/test/resources/example_normal_class.java "HELLO " "WORLD!" - diff --git a/relproxy/test_cmd/test_java_shell_snippet_launcher.sh b/relproxy/test_cmd/test_java_shell_snippet_launcher.sh deleted file mode 100644 index d13738e..0000000 --- a/relproxy/test_cmd/test_java_shell_snippet_launcher.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_inter_and_snippet_launcher.sh - -jproxysh -c 'System.out.print("This code snippet says: ");' \ - 'System.out.println("Hello World!!");' - diff --git a/relproxy/test_cmd/test_java_shell_snippet_launcher_complete_class.sh b/relproxy/test_cmd/test_java_shell_snippet_launcher_complete_class.sh deleted file mode 100644 index 80a3825..0000000 --- a/relproxy/test_cmd/test_java_shell_snippet_launcher_complete_class.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -source _shared_test_java_shell_inter_and_snippet_launcher.sh - -jproxysh -c 'public class _jproxyMainClass_ { ' \ - ' public static void main(String[] args) { ' \ - ' System.out.print("This code snippet says: ");' \ - ' System.out.println("Hello World!!");' \ - ' }' \ - '}' diff --git a/relproxy_test_itsnat/.gitignore b/relproxy_test_itsnat/.gitignore deleted file mode 100644 index 8dddca2..0000000 --- a/relproxy_test_itsnat/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/target/ -/tmp -.gradle/ - -conf/conf_local.properties diff --git a/relproxy_test_itsnat/build.gradle b/relproxy_test_itsnat/build.gradle deleted file mode 100644 index ea572ec..0000000 --- a/relproxy_test_itsnat/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -import java.util.jar.Attributes.Name; - -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'war' - -repositories { - mavenCentral() -} - -dependencies { - providedCompile 'javax.servlet:servlet-api:2.5' - providedCompile 'javax.servlet.jsp:jsp-api:2.1' - - compile files('/src/main/webapp/WEB-INF/lib/ItsNat_1.3.1.jar') - compile 'org.apache.xmlgraphics:batik-dom:1.7@jar' - compile 'org.apache.xmlgraphics:batik-xml:1.7@jar' - compile 'org.apache.xmlgraphics:batik-util:1.7@jar' - compile 'net.sourceforge.nekohtml:nekohtml:1.9.12@jar' - compile 'xalan:serializer:2.7.1@jar' - compile 'org.codehaus.groovy:groovy-all:2.1.6@jar' - - runtime files('/src/main/webapp/WEB-INF/lib/ItsNat-1.3.1.jar') - runtime 'org.apache.xmlgraphics:batik-dom:1.7' - runtime 'org.apache.xmlgraphics:batik-xml:1.7' - runtime 'org.apache.xmlgraphics:batik-util:1.7' - runtime 'net.sourceforge.nekohtml:nekohtml:1.9.12' - runtime 'xalan:serializer:2.7.1' - runtime 'org.codehaus.groovy:groovy-all:2.1.6' -} - -sourceSets.main.java.srcDirs 'src/main/webapp/WEB-INF/javaex/code' -sourceSets.main.java.srcDirs 'src/main/webapp/WEB-INF/groovyex/code' - -task explodedWar(type: Copy) { - into "$buildDir/web" - with war -} - - -war.dependsOn explodedWar - diff --git a/relproxy_test_itsnat/conf/conf_relproxy.properties b/relproxy_test_itsnat/conf/conf_relproxy.properties deleted file mode 100644 index 73a72a1..0000000 --- a/relproxy_test_itsnat/conf/conf_relproxy.properties +++ /dev/null @@ -1,2 +0,0 @@ - -webapp_folder=relproxy_test_itsnat-0.1-SNAPSHOT diff --git a/relproxy_test_itsnat/nb-configuration.xml b/relproxy_test_itsnat/nb-configuration.xml deleted file mode 100644 index b92960b..0000000 --- a/relproxy_test_itsnat/nb-configuration.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - 1.5 - Tomcat - all - js/libs - false - true - - diff --git a/relproxy_test_itsnat/pom.xml b/relproxy_test_itsnat/pom.xml deleted file mode 100644 index 56bbd9b..0000000 --- a/relproxy_test_itsnat/pom.xml +++ /dev/null @@ -1,153 +0,0 @@ - - 4.0.0 - - com.innowhere - relproxy_test_itsnat - war - 0.1-SNAPSHOT - - relproxy_test_itsnat - https://github.com/jmarranz/relproxy/ - - - UTF-8 - - - - - - javax.servlet - servlet-api - 2.5 - provided - - - - javax.servlet.jsp - jsp-api - 2.1 - provided - - - - - - - ItsNat - ItsNat-jar - 1.3.1 - system - ${basedir}/src/main/webapp/WEB-INF/lib/ItsNat-1.3.1.jar - - - - org.apache.xmlgraphics - batik-dom - 1.7 - - - - org.apache.xmlgraphics - batik-xml - 1.7 - - - - org.apache.xmlgraphics - batik-util - 1.7 - - - - net.sourceforge.nekohtml - nekohtml - 1.9.12 - - - - xalan - serializer - 2.7.1 - - - - org.codehaus.groovy - groovy-all - 2.1.6 - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.9.1 - - - add-source - generate-sources - - add-source - - - - ${basedir}/../relproxy/src/main/java - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.0.2 - - 1.6 - 1.6 - ${project.build.sourceEncoding} - - - - - org.apache.maven.plugins - maven-resources-plugin - 2.4.3 - - ${project.build.sourceEncoding} - - - - - - - - - ../relproxy/src/main/java - - - - src/main/webapp/WEB-INF/groovyex/code - - - src/main/webapp/WEB-INF/javaex/code - - - src/main/webapp/WEB-INF/javaex/code2 - - - - - diff --git a/relproxy_test_itsnat/src/main/java/example/groovyex/ItsNatGroovyServlet.java b/relproxy_test_itsnat/src/main/java/example/groovyex/ItsNatGroovyServlet.java deleted file mode 100644 index 3d84c78..0000000 --- a/relproxy_test_itsnat/src/main/java/example/groovyex/ItsNatGroovyServlet.java +++ /dev/null @@ -1,110 +0,0 @@ - -package example.groovyex; - -import groovy.lang.Binding; -import groovy.lang.Closure; -import groovy.servlet.ServletCategory; -import groovy.util.GroovyScriptEngine; -import groovy.util.ResourceException; -import groovy.util.ScriptException; -import java.io.File; -import java.io.IOException; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import org.codehaus.groovy.runtime.GroovyCategorySupport; -import org.itsnat.core.http.HttpServletWrapper; - - -/** - * Inspired on: - * https://github.com/groovy/groovy-core/blob/master/subprojects/groovy-servlet/src/main/java/groovy/servlet/GroovyServlet.java - * - * @author jmarranz - */ -public class ItsNatGroovyServlet extends HttpServletWrapper -{ - protected GroovyScriptEngine gse; - - public ItsNatGroovyServlet() - { - } - - public GroovyScriptEngine getGroovyScriptEngine() - { - return gse; - } - - public String getScriptRootPath(ServletConfig config) throws ServletException - { - String scriptRootPath = config.getInitParameter("scriptRootPath"); - if (scriptRootPath == null) throw new ServletException("Missing servlet init param scriptRootPath"); - return getServletContext().getRealPath("/") + "/WEB-INF/" + scriptRootPath + "/"; - } - - public String getInitScript(ServletConfig config) throws ServletException - { - String initScript = config.getInitParameter("initScript"); - if (initScript == null) throw new ServletException("Missing servlet init param initScript"); - return initScript; - } - - @Override - public void init(ServletConfig config) throws ServletException - { - super.init(config); - - // Set up the scripting engine - - String pathPrefix = getScriptRootPath(config); - - try - { - this.gse = new GroovyScriptEngine(new String[]{pathPrefix}); - } - catch(IOException ex) { throw new RuntimeException(ex); } - - //gse.getConfig().setMinimumRecompilationInterval(0); - - //System.out.println("MinimumRecompilationInterval " + gse.getConfig().getMinimumRecompilationInterval()); - - getServletContext().log("Groovy servlet initialized on " + gse + "."); - - String initScript = getInitScript(config); - - File initFile = new File(pathPrefix + initScript); - if (!initFile.exists()) - throw new ServletException(initFile.getAbsolutePath() + " does not exist"); - - final Binding binding = new Binding(); - binding.setVariable("itsNatServlet", itsNatServlet); - binding.setVariable("servlet", this); - binding.setVariable("config", config); - binding.setVariable("context", getServletContext()); - binding.setVariable("application", getServletContext()); - - com.innowhere.relproxy.gproxy.GProxyGroovyScriptEngine.class.getName(); - com.innowhere.relproxy.gproxy.GProxyConfig.class.getName(); - com.innowhere.relproxy.gproxy.GProxy.class.getName(); - - execGroovyScript(initScript,binding); - } - - protected void execGroovyScript(final String filePath,final Binding binding) - { - Closure closure = new Closure(gse) - { - @Override - public Object call() { - try { - return ((GroovyScriptEngine)getDelegate()).run(filePath, binding); - } - catch (ResourceException e) { throw new RuntimeException(e); } - catch (ScriptException e) { throw new RuntimeException(e); } - } - }; - GroovyCategorySupport.use(ServletCategory.class, closure); - } - -} - - diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/City.java b/relproxy_test_itsnat/src/main/java/example/javaex/City.java deleted file mode 100644 index 1c88e5a..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/City.java +++ /dev/null @@ -1,17 +0,0 @@ -package example.javaex; - -/** - * - * @author jmarranz - */ -public class City -{ - protected String name; - - public City(String name) - { - this.name = name; - } - - public String getName() { return name; } -} diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/FalseDB.java b/relproxy_test_itsnat/src/main/java/example/javaex/FalseDB.java deleted file mode 100644 index b92651e..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/FalseDB.java +++ /dev/null @@ -1,22 +0,0 @@ -package example.javaex; - -import java.util.LinkedList; - -/** - * - * @author jmarranz - */ -public class FalseDB -{ - protected LinkedList cities; - - public FalseDB() - { - cities = new LinkedList(); - cities.add(new City("Madrid")); - cities.add(new City("Barcelona")); - cities.add(new City("Bilbao")); - } - - public LinkedList getCityList() { return cities; /*cities;*/ } -} \ No newline at end of file diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/JProxyExampleServlet.java b/relproxy_test_itsnat/src/main/java/example/javaex/JProxyExampleServlet.java deleted file mode 100644 index 9bc7397..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/JProxyExampleServlet.java +++ /dev/null @@ -1,46 +0,0 @@ - -package example.javaex; - -import com.innowhere.relproxy.jproxy.JProxy; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.http.HttpServletWrapper; -import org.itsnat.core.tmpl.ItsNatDocumentTemplate; - - -/** - * - * @author jmarranz - */ -public class JProxyExampleServlet extends HttpServletWrapper -{ - public JProxyExampleServlet() - { - } - - @Override - public void init(ServletConfig config) throws ServletException - { - super.init(config); - - ServletContext context = config.getServletContext(); - - String pathPrefix = context.getRealPath("/") + "/WEB-INF/javaex/pages/"; - - final FalseDB db = new FalseDB(); - - ItsNatDocumentTemplate docTemplate; - docTemplate = itsNatServlet.registerItsNatDocumentTemplate("javaex","text/html", pathPrefix + "javaex.html"); - ItsNatServletRequestListener listener = JProxy.create(new example.javaex.JProxyExampleLoadListener(db), ItsNatServletRequestListener.class); - docTemplate.addItsNatServletRequestListener(listener); - - ItsNatServletRequestListener original = new example.javaex.JProxyExampleLoadListener(db); - ItsNatServletRequestListener proxy = JProxy.create(original, ItsNatServletRequestListener.class); - ItsNatServletRequestListener proxy2 = JProxy.create(original, ItsNatServletRequestListener.class); - System.out.println("EQUALS TEST (true if not reloaded): " + (proxy.equals(proxy2))); - } - -} - diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/JProxyServletContextListener.java b/relproxy_test_itsnat/src/main/java/example/javaex/JProxyServletContextListener.java deleted file mode 100644 index e8b00f3..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/JProxyServletContextListener.java +++ /dev/null @@ -1,136 +0,0 @@ -package example.javaex; - -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.jproxy.JProxy; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyConfig; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import java.io.File; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import javax.servlet.ServletContext; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; - -/** - * - * @author jmarranz - */ -public class JProxyServletContextListener implements ServletContextListener -{ - @Override - public void contextInitialized(ServletContextEvent sce) - { - System.out.println("ServletContextListener contextInitialized"); - - ServletContext context = sce.getServletContext(); - String realPath = context.getRealPath("/"); - String[] inputPaths = new String[] - { realPath + "/WEB-INF/javaex/code/", - realPath + "/WEB-INF/javaex/code2/", - realPath + "/../../src/main/java/" }; - - JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() - { - @Override - public boolean isExcluded(File file, File rootFolderOfSources) - { - String rootFolderAbsPath = rootFolderOfSources.getAbsolutePath(); - String absPath = file.getAbsolutePath(); - if (rootFolderAbsPath.endsWith(File.separatorChar + "code") || rootFolderAbsPath.endsWith(File.separatorChar + "code2")) - { - return absPath.endsWith(JProxyExampleAuxIgnored.class.getSimpleName() + ".java"); - } - else // /../../src/main/java/ - { - if (file.isDirectory()) - { - return absPath.endsWith(File.separatorChar + "innowhere") || // Por si acaso el código fuente de RelProxy lo tenemos copiado para testear, tenemos que excluirlo - absPath.endsWith(File.separatorChar + "nothotreload"); - } - else - { - return !absPath.contains(File.separatorChar + "hotreload" + File.separatorChar); - } - } - } - }; - - - String classFolder = null; // Optional: context.getRealPath("/") + "/WEB-INF/classes"; - Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - long scanPeriod = 200; - - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - @Override - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ - @Override - public void beforeCompile(File file) - { - System.out.println("Before compile: " + file); - } - - @Override - public void afterCompile(File file) - { - System.out.println("After compile: " + file); - } - }; - - JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() - { - @Override - public void onDiagnostics(DiagnosticCollector diagnostics) - { - List> diagList = diagnostics.getDiagnostics(); - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - }; - - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPaths(inputPaths) - .setJProxyInputSourceFileExcludedListener(excludedListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyCompilerListener(compilerListener) - .setJProxyDiagnosticsListener(diagnosticsListener); - - JProxy.init(jpConfig); - - } - - @Override - public void contextDestroyed(ServletContextEvent sce) - { - System.out.println("ServletContextListener contextDestroyed"); - JProxy.stop(); - } - -} diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/hotreload/JProxyExampleAux2.java b/relproxy_test_itsnat/src/main/java/example/javaex/hotreload/JProxyExampleAux2.java deleted file mode 100644 index ceaee39..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/hotreload/JProxyExampleAux2.java +++ /dev/null @@ -1,14 +0,0 @@ -package example.javaex.hotreload; - -/** - * - * @author jmarranz - */ -public class JProxyExampleAux2 -{ - public static void log() - { - System.out.println("JProxyExampleAux2: 1 " + JProxyExampleAux2.class.getClassLoader().hashCode()); - } -} - diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/nothotreload/JProxyExampleAuxIgnored2.java b/relproxy_test_itsnat/src/main/java/example/javaex/nothotreload/JProxyExampleAuxIgnored2.java deleted file mode 100644 index 445aa74..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/nothotreload/JProxyExampleAuxIgnored2.java +++ /dev/null @@ -1,14 +0,0 @@ -package example.javaex.nothotreload; - -/** - * - * @author jmarranz - */ -public class JProxyExampleAuxIgnored2 -{ - public static void log() - { - System.out.println("JProxyExampleAuxIgnored2: 2 " + JProxyExampleAuxIgnored2.class.getClassLoader().hashCode()); - } -} - diff --git a/relproxy_test_itsnat/src/main/java/example/javaex/nothotreload/JProxyExampleAuxIgnored3.java b/relproxy_test_itsnat/src/main/java/example/javaex/nothotreload/JProxyExampleAuxIgnored3.java deleted file mode 100644 index bcbfeba..0000000 --- a/relproxy_test_itsnat/src/main/java/example/javaex/nothotreload/JProxyExampleAuxIgnored3.java +++ /dev/null @@ -1,14 +0,0 @@ -package example.javaex.nothotreload; - -/** - * - * @author jmarranz - */ -public class JProxyExampleAuxIgnored3 -{ - public static void log() - { - System.out.println("JProxyExampleAuxIgnored3: 1 " + JProxyExampleAuxIgnored3.class.getClassLoader().hashCode()); - } -} - diff --git a/relproxy_test_itsnat/src/main/webapp/META-INF/context.xml b/relproxy_test_itsnat/src/main/webapp/META-INF/context.xml deleted file mode 100644 index af070b4..0000000 --- a/relproxy_test_itsnat/src/main/webapp/META-INF/context.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/City.groovy b/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/City.groovy deleted file mode 100644 index ead1995..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/City.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package example.groovyex; - -/** - * - * @author jmarranz - */ -class City -{ - def name; - - City(String name) - { - this.name = name; - } - - def getName() { return name; } -} diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/FalseDB.groovy b/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/FalseDB.groovy deleted file mode 100644 index f703dd6..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/FalseDB.groovy +++ /dev/null @@ -1,20 +0,0 @@ -package example.groovyex; - -/** - * - * @author jmarranz - */ -class FalseDB -{ - def cities - - FalseDB() - { - cities = new LinkedList(); - cities.add(new City("Madrid")); - cities.add(new City("Barcelona")); - cities.add(new City("Bilbao")); - } - - def getCityList() { return cities; /*cities;*/ } -} diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/GroovyExampleDocument.groovy b/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/GroovyExampleDocument.groovy deleted file mode 100644 index a07fbcd..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/GroovyExampleDocument.groovy +++ /dev/null @@ -1,36 +0,0 @@ - -package example.groovyex; - -import org.w3c.dom.events.Event -import org.w3c.dom.events.EventListener - -class GroovyExampleDocument -{ - def itsNatDoc // ItsNatHTMLDocument - def textInput // ItsNatHTMLInputText - def resultsElem // Element - - GroovyExampleDocument(itsNatDoc,db) - { - this.itsNatDoc = itsNatDoc - - if (db.getCityList().size() != 3) - throw new RuntimeException("Unexpected"); - - def doc = itsNatDoc.getHTMLDocument() - - def compMgr = itsNatDoc.getItsNatComponentManager() - this.textInput = compMgr.createItsNatComponentById("inputId") - - def buttonElem = doc.getElementById("buttonId") - buttonElem.addEventListener("click", - { Event evt -> def text = textInput.getText(); resultsElem.setTextContent(text); } as EventListener, false) - - def closure = { println("GroovyExampleDocument closure 2 ") } - closure(); - - this.resultsElem = doc.getElementById("resultsId") - - println("GroovyExampleDocument 2 ") - } -} diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/GroovyExampleLoadListener.groovy b/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/GroovyExampleLoadListener.groovy deleted file mode 100644 index 66ec2f9..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/GroovyExampleLoadListener.groovy +++ /dev/null @@ -1,28 +0,0 @@ - -package example.groovyex; - -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.ItsNatServletRequest; -import org.itsnat.core.ItsNatServletResponse; -import example.groovyex.FalseDB; - -class GroovyExampleLoadListener implements ItsNatServletRequestListener -{ - def db - - GroovyExampleLoadListener() - { - } - - GroovyExampleLoadListener(FalseDB db) // Explicit type tells Groovy to reload FalseDB class when changed - { - this.db = db; - } - - void processRequest(ItsNatServletRequest request, ItsNatServletResponse response) - { - println("GroovyExampleLoadListener 4 "); - - new example.groovyex.GroovyExampleDocument(request.getItsNatDocument(),db); - } -} diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/groovy_servlet_init.groovy b/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/groovy_servlet_init.groovy deleted file mode 100644 index c0b94bb..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/code/example/groovyex/groovy_servlet_init.groovy +++ /dev/null @@ -1,50 +0,0 @@ - -package example.groovyex; - -import org.itsnat.core.http.ItsNatHttpServlet; -import org.itsnat.core.tmpl.ItsNatDocumentTemplate; -import org.itsnat.core.event.ItsNatServletRequestListener; -import groovy.util.GroovyScriptEngine; -import java.lang.reflect.Method; -import com.innowhere.relproxy.RelProxyOnReloadListener; -import com.innowhere.relproxy.gproxy.GProxy; -import com.innowhere.relproxy.gproxy.GProxyGroovyScriptEngine; -import com.innowhere.relproxy.gproxy.GProxyConfig; - - -GroovyScriptEngine groovyEngine = servlet.getGroovyScriptEngine(); - -def gproxyGroovyEngine = { - String scriptName -> return (java.lang.Class)groovyEngine.loadScriptByName(scriptName) - } as GProxyGroovyScriptEngine; - -/* This alternative throws a weird error when called loadScriptByName, why? -GProxyGroovyScriptEngine groovyEngine = - { - loadScriptByName : { String scriptName -> return (java.lang.Class)servlet.getGroovyScriptEngine().loadScriptByName(scriptName) } - } as GProxyGroovyScriptEngine; -*/ - -def reloadListener = { - Object objOld,Object objNew,Object proxy, Method method, Object[] args -> - println("Reloaded " + objNew + " Calling method: " + method) - } as RelProxyOnReloadListener; - -def gpConfig = GProxy.createGProxyConfig(); -gpConfig.setEnabled(true) - .setRelProxyOnReloadListener(reloadListener) - .setGProxyGroovyScriptEngine(gproxyGroovyEngine); - -GProxy.init(gpConfig); - - -String pathPrefix = context.getRealPath("/") + "/WEB-INF/groovyex/pages/"; - -def docTemplate; -docTemplate = itsNatServlet.registerItsNatDocumentTemplate("groovyex","text/html", pathPrefix + "groovyex.html"); - -def db = new FalseDB(); - -ItsNatServletRequestListener listener = GProxy.create(new example.groovyex.GroovyExampleLoadListener(db), ItsNatServletRequestListener.class); -docTemplate.addItsNatServletRequestListener(listener); - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/pages/groovyex.html b/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/pages/groovyex.html deleted file mode 100644 index 969460f..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/groovyex/pages/groovyex.html +++ /dev/null @@ -1,23 +0,0 @@ - - - -GProxy Example (Groovy) - - -

GProxy Example (Groovy)

- -
-
- Say something to GProxy:
- -
- -
-
You said:
-
- -

- RETURN - - - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleAuxIgnored.java b/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleAuxIgnored.java deleted file mode 100644 index eafe6e4..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleAuxIgnored.java +++ /dev/null @@ -1,14 +0,0 @@ -package example.javaex; - -/** - * - * @author jmarranz - */ -public class JProxyExampleAuxIgnored -{ - public static void log() - { - System.out.println("JProxyExampleAuxIgnored: 2 " + JProxyExampleAuxIgnored.class.getClassLoader().hashCode()); - } -} - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleDocument.java b/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleDocument.java deleted file mode 100644 index 903e65a..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleDocument.java +++ /dev/null @@ -1,87 +0,0 @@ -package example.javaex; - -import com.innowhere.relproxy.jproxy.JProxy; -import example.javaex.hotreload.JProxyExampleAux2; -import example.javaex.nothotreload.JProxyExampleAuxIgnored2; -import example.javaex.nothotreload.JProxyExampleAuxIgnored3; -import org.itsnat.comp.ItsNatComponentManager; -import org.itsnat.comp.text.ItsNatHTMLInputText; -import org.itsnat.core.ItsNatServletRequest; -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.html.ItsNatHTMLDocument; -import org.w3c.dom.Element; -import org.w3c.dom.events.Event; -import org.w3c.dom.events.EventListener; -import org.w3c.dom.events.EventTarget; -import org.w3c.dom.html.HTMLDocument; - -public class JProxyExampleDocument extends JProxyExampleDocumentBase -{ - protected ItsNatHTMLDocument itsNatDoc; // ItsNatHTMLDocument - protected ItsNatHTMLInputText textInput; // ItsNatHTMLInputText - protected Element resultsElem; // Element - - public static class AuxMember - { - public static void log() - { - System.out.println(AuxMember.class.getName() + ": 1 " + AuxMember.class.getClassLoader().hashCode()); - } - } - - public JProxyExampleDocument() // Requerido por el listener ejemplo anonymous inner class del "dlbclick" - { - } - - public JProxyExampleDocument(ItsNatServletRequest request,ItsNatHTMLDocument itsNatDoc,FalseDB db) - { - class AuxMemberInMethod - { - public void log() - { - System.out.println("JProxyExampleDocument.AuxMemberInMethod: 1 " + AuxMemberInMethod.class.getClassLoader().hashCode()); - } - } - - this.itsNatDoc = itsNatDoc; - - if (db.getCityList().size() != 3) - throw new RuntimeException("Unexpected"); - - HTMLDocument doc = itsNatDoc.getHTMLDocument(); - - ItsNatComponentManager compMgr = itsNatDoc.getItsNatComponentManager(); - this.textInput = (ItsNatHTMLInputText)compMgr.createItsNatComponentById("inputId"); - - EventListener listener = new EventListener() - { - { - System.out.println("JProxyExampleDocument Anonymous Inner 1 " + this.getClass().getClassLoader().hashCode()); - } - - @Override - public void handleEvent(Event evt) - { - String text = textInput.getText(); - String comment = " YES I SAID THIS (" + evt.getType() + ")"; - resultsElem.setTextContent(text + comment); - } - }; - - Element buttonElem = doc.getElementById("buttonId"); - ((EventTarget)buttonElem).addEventListener("click",listener,false); - - ((EventTarget)buttonElem).addEventListener("dblclick", JProxy.create(listener, EventListener.class) ,false); - - this.resultsElem = doc.getElementById("resultsId"); - - System.out.println("JProxyExampleDocument 1 " + this.getClass().getClassLoader().hashCode()); - new AuxMemberInMethod().log(); - AuxMember.log(); - JProxyExampleAux.log(); - JProxyExampleAux2.log(); - JProxyExampleAuxIgnored.log(); - JProxyExampleAuxIgnored2.log(); - JProxyExampleAuxIgnored3.log(); - } -} diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleDocumentBase.java b/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleDocumentBase.java deleted file mode 100644 index 4b6c98c..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleDocumentBase.java +++ /dev/null @@ -1,13 +0,0 @@ -package example.javaex; - -/** - * - * @author jmarranz - */ -public class JProxyExampleDocumentBase -{ - public JProxyExampleDocumentBase() - { - System.out.println("JProxyExampleDocumentBase 7 " + this.getClass().getClassLoader().hashCode()); - } -} diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleLoadListener.java b/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleLoadListener.java deleted file mode 100644 index 550320c..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code/example/javaex/JProxyExampleLoadListener.java +++ /dev/null @@ -1,36 +0,0 @@ -package example.javaex; - -/** - * - * @author jmarranz - */ -import org.itsnat.core.event.ItsNatServletRequestListener; -import org.itsnat.core.ItsNatServletRequest; -import org.itsnat.core.ItsNatServletResponse; -import org.itsnat.core.html.ItsNatHTMLDocument; - -public class JProxyExampleLoadListener implements ItsNatServletRequestListener -{ - protected final FalseDB db; - protected static Integer testStatic = 10; - protected static final Integer testStaticFinal = 11; - - public JProxyExampleLoadListener() - { - this(null); - } - - public JProxyExampleLoadListener(FalseDB db) - { - this.db = db; - } - - - @Override - public void processRequest(ItsNatServletRequest request, ItsNatServletResponse response) - { - System.out.println("JProxyExampleLoadListener 1 " + this.getClass().getClassLoader().hashCode()); - new example.javaex.JProxyExampleDocument(request,(ItsNatHTMLDocument)request.getItsNatDocument(),db); - } -} - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code2/example/javaex/JProxyExampleAux.java b/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code2/example/javaex/JProxyExampleAux.java deleted file mode 100644 index f04ad4d..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/code2/example/javaex/JProxyExampleAux.java +++ /dev/null @@ -1,14 +0,0 @@ -package example.javaex; - -/** - * - * @author jmarranz - */ -public class JProxyExampleAux -{ - public static void log() - { - System.out.println("JProxyExampleAux: 1 " + JProxyExampleAux.class.getClassLoader().hashCode()); - } -} - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/pages/javaex.html b/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/pages/javaex.html deleted file mode 100644 index ba0aa8a..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/javaex/pages/javaex.html +++ /dev/null @@ -1,24 +0,0 @@ - - - -JProxy Example (Java) - - -

JProxy Example (Java)

- -
Note: in NetBeans after every hot reloadable source change, execute Ant sync.xml and reload
-
-
- Say something to JProxy:
- -
- -
-
You said:
-
- -

- RETURN - - - diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/lib/ItsNat-1.3.1.jar b/relproxy_test_itsnat/src/main/webapp/WEB-INF/lib/ItsNat-1.3.1.jar deleted file mode 100644 index e2ff917..0000000 Binary files a/relproxy_test_itsnat/src/main/webapp/WEB-INF/lib/ItsNat-1.3.1.jar and /dev/null differ diff --git a/relproxy_test_itsnat/src/main/webapp/WEB-INF/web.xml b/relproxy_test_itsnat/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d93ce5b..0000000 --- a/relproxy_test_itsnat/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - relproxy_test_itsnat - - ItsNatGroovyServlet - example.groovyex.ItsNatGroovyServlet - - scriptRootPath - groovyex/code - - - initScript - example/groovyex/groovy_servlet_init.groovy - - - - ItsNatGroovyServlet - /groovyex - - - ItsNatJProxyServlet - example.javaex.JProxyExampleServlet - - - ItsNatJProxyServlet - /javaex - - - - 30 - - - - index.html - - - - - example.javaex.JProxyServletContextListener - - - diff --git a/relproxy_test_itsnat/src/main/webapp/index.html b/relproxy_test_itsnat/src/main/webapp/index.html deleted file mode 100644 index 904f782..0000000 --- a/relproxy_test_itsnat/src/main/webapp/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - RelProxy Examples - - -

RelProxy Examples

- -

This project is mainly created to help development and testing of RelProxy, it binds directly RelProxy source code instead the jar

- -

RelProxy provides a simple approach for hot class reload for Java and Groovy

- -

Source code of RelProxy and these examples are HERE

- - - - - - - - - - - diff --git a/relproxy_test_itsnat/src/main/webapp/jsp/test_jproxy_start.jsp b/relproxy_test_itsnat/src/main/webapp/jsp/test_jproxy_start.jsp deleted file mode 100644 index 9f3b620..0000000 --- a/relproxy_test_itsnat/src/main/webapp/jsp/test_jproxy_start.jsp +++ /dev/null @@ -1,31 +0,0 @@ -<%@page import="com.innowhere.relproxy.jproxy.JProxy"%> -<%@page contentType="text/html" pageEncoding="UTF-8"%> - - - - - Test JProxy class reloader RE-START - - -

Test JProxy class reloader RE-START

- - <% - boolean res = JProxy.start(); - if (res) - { - %> -

Source change detection was re-started!

- - <% } - else - { - %> -

Source change detection start action is failed, maybe is already running!

- - <% - } - %> - - - - diff --git a/relproxy_test_itsnat/src/main/webapp/jsp/test_jproxy_stop.jsp b/relproxy_test_itsnat/src/main/webapp/jsp/test_jproxy_stop.jsp deleted file mode 100644 index cc46a55..0000000 --- a/relproxy_test_itsnat/src/main/webapp/jsp/test_jproxy_stop.jsp +++ /dev/null @@ -1,31 +0,0 @@ -<%@page import="com.innowhere.relproxy.jproxy.JProxy"%> -<%@page contentType="text/html" pageEncoding="UTF-8"%> - - - - - Test JProxy class reloader STOP - - -

Test JProxy class reloader STOP

- - <% - boolean res = JProxy.stop(); - if (res) - { - %> -

Source change detection is disabled!

- - <% } - else - { - %> -

Source change detection stop action is failed, maybe is already stopped!

- - <% - } - %> - - - - diff --git a/relproxy_test_itsnat/sync.xml b/relproxy_test_itsnat/sync.xml deleted file mode 100644 index 322fa15..0000000 --- a/relproxy_test_itsnat/sync.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/com/sillelien/jas/Demo.java b/src/main/java/com/sillelien/jas/Demo.java new file mode 100644 index 0000000..f705ece --- /dev/null +++ b/src/main/java/com/sillelien/jas/Demo.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas; + +import com.sillelien.jas.jproxy.JProxy; +import com.sillelien.jas.jproxy.JProxyConfig; +import com.sillelien.jas.jproxy.JProxyScriptEngine; +import com.sillelien.jas.jproxy.JProxyScriptEngineFactory; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.util.Collections; +import java.util.List; + +public final class Demo { + + @NotNull + private static final Logger log = LoggerFactory.getLogger("Demo"); + + public static void main(@NotNull String[] ignored) throws Exception { + + //Initializing and configuring the JSR-223 script engine + JProxyConfig jpConfig = JProxy.createJProxyConfig(); + jpConfig.setEnabled(true) + .setRelProxyOnReloadListener((objOld, objNew, proxy, method, args) -> { + //TODO + }) + // .setInputPath(".") + .setScanPeriod(-1) + .setClassFolder("./tmp/classes") + .setCompilationOptions(Collections.emptyList()) + .setJProxyDiagnosticsListener(diagnostics -> { + List> diagnosticList = diagnostics.getDiagnostics(); + diagnosticList.stream() + .filter(diagnostic -> diagnostic.getKind().equals(Diagnostic.Kind.ERROR)) + .forEach(i->log.debug(i.toString())); + }); + + JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); + + ScriptEngineManager manager = new ScriptEngineManager(); + manager.registerEngineName("java", factory); + Bindings bindings = manager.getBindings(); + bindings.put("in", "World"); + + ScriptEngine engine = manager.getEngineByName("java"); + JProxyScriptEngine scriptEngine = (JProxyScriptEngine) engine; + scriptEngine.init(jpConfig); + + //Your code goes here, e.g. + + scriptEngine.eval("System.out.println(\"Hello \"+context.getAttribute(\"in\",javax.script.ScriptContext.ENGINE_SCOPE));return null;\n",bindings); + + } +} diff --git a/src/main/java/com/sillelien/jas/RelProxy.java b/src/main/java/com/sillelien/jas/RelProxy.java new file mode 100644 index 0000000..ffd6094 --- /dev/null +++ b/src/main/java/com/sillelien/jas/RelProxy.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas; + +/** + * Is the root of RelProxy + * + * @author Jose Maria Arranz Santamaria + */ +public final class RelProxy { + /** + * Returns the version of this RelProxy library. + * + * @return the version of the library. + */ + public static String getVersion() { + return "0.8.8"; + } +} diff --git a/src/main/java/com/sillelien/jas/RelProxyException.java b/src/main/java/com/sillelien/jas/RelProxyException.java new file mode 100644 index 0000000..9f7f54e --- /dev/null +++ b/src/main/java/com/sillelien/jas/RelProxyException.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas; + +import org.jetbrains.annotations.NotNull; + +/** + * Internal checked exceptions thrown by RelProxy and library specific errors are wrapped into this exception class. + * + * @author Jose Maria Arranz Santamaria + */ +public class RelProxyException extends RuntimeException { + /** + * Constructs a new exception with the specified message and cause. + *

Parameters are passed to the super constructor.

+ * + * @param message the detail message + * @param cause the cause + */ + public RelProxyException(@NotNull String message, @NotNull Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified message. + *

Parameter is passed to the super constructor.

+ * + * @param message the detail message + */ + public RelProxyException(@NotNull String message) { + super(message); + } + + /** + * Constructs a new exception with the specified cause. + *

Parameter is passed to the super constructor.

+ * + * @param cause the cause + */ + public RelProxyException(@NotNull Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/sillelien/jas/RelProxyOnReloadListener.java b/src/main/java/com/sillelien/jas/RelProxyOnReloadListener.java new file mode 100644 index 0000000..4a9861b --- /dev/null +++ b/src/main/java/com/sillelien/jas/RelProxyOnReloadListener.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas; + +import com.sillelien.jas.jproxy.JProxyConfig; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; + +/** + * Is the interface needed to register a class reload listener. + *

An object implementing this interface can optionally be registered on RelProxy to listen when the method of a proxy object has been called + * and the class of the original object associated has been reloaded (and a new "original" object based on the new class was created to replace it). + *

+ * + * @author Jose Maria Arranz Santamaria + * @see JProxyConfig#setRelProxyOnReloadListener(RelProxyOnReloadListener) + */ +public interface RelProxyOnReloadListener { + /** + * Called when some source code change has happened and a new class has been compiled and reloaded. + * + * @param objOld the old object before class reload. + * @param objNew the new object based on the new class loaded by the new class loader. + * @param proxy the proxy object created by {@link java.lang.reflect.Proxy} being used. + * @param method the method being called through the proxy object. + * @param args the parameters being used in the method call. + */ + void onReload(@Nullable Object objOld, + @Nullable Object objNew, + @Nullable Object proxy, + @NotNull Method method, + @Nullable Object[] args); +} diff --git a/src/main/java/com/sillelien/jas/impl/FileExt.java b/src/main/java/com/sillelien/jas/impl/FileExt.java new file mode 100644 index 0000000..c032695 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/FileExt.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl; + +import com.sillelien.jas.RelProxyException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; + +/** + * @author jmarranz + */ +public class FileExt { + @NotNull + protected final File file; + @NotNull + protected final String cannonicalPath; // El obtener el cannonicalPath exige acceder al sistema de archivos, por eso nos inventamos esta clase, para evitar sucesivas llamadas a File.getCanonicalPath() + + public FileExt(@NotNull File file) { + this.file = file; + try { + cannonicalPath = file.getCanonicalPath(); + } catch (IOException ex) { + throw new RelProxyException(ex); + } + } + + @NotNull + public File getFile() { + return file; + } + + @NotNull + public String getCanonicalPath() { + return cannonicalPath; + } +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyConfigBaseImpl.java b/src/main/java/com/sillelien/jas/impl/GenericProxyConfigBaseImpl.java similarity index 52% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyConfigBaseImpl.java rename to src/main/java/com/sillelien/jas/impl/GenericProxyConfigBaseImpl.java index 72863e3..a590f7f 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/GenericProxyConfigBaseImpl.java +++ b/src/main/java/com/sillelien/jas/impl/GenericProxyConfigBaseImpl.java @@ -1,24 +1,24 @@ -package com.innowhere.relproxy.impl; - -import com.innowhere.relproxy.RelProxyOnReloadListener; - -/** - * - * @author jmarranz - */ -public class GenericProxyConfigBaseImpl -{ - protected boolean enabled = true; - protected RelProxyOnReloadListener relListener; - - public boolean isEnabled() - { - return enabled; - } - - public RelProxyOnReloadListener getRelProxyOnReloadListener() - { - return relListener; - } - -} +package com.sillelien.jas.impl; + +import com.sillelien.jas.RelProxyOnReloadListener; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class GenericProxyConfigBaseImpl { + protected boolean enabled = true; + + @NotNull + protected RelProxyOnReloadListener relListener; + + public boolean isEnabled() { + return enabled; + } + + @NotNull + public RelProxyOnReloadListener getRelProxyOnReloadListener() { + return relListener; + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/GenericProxyImpl.java b/src/main/java/com/sillelien/jas/impl/GenericProxyImpl.java new file mode 100644 index 0000000..4ec8cc2 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/GenericProxyImpl.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.RelProxyOnReloadListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +/** + * @author jmarranz + */ +public abstract class GenericProxyImpl { + @Nullable + protected RelProxyOnReloadListener reloadListener; + + public GenericProxyImpl() { + } + + public static void checkSingletonNull(@Nullable GenericProxyImpl singleton) { + if (singleton != null) + throw new RelProxyException("Already initialized"); + } + + protected static void checkSingletonExists(@Nullable GenericProxyImpl singleton) { + if (singleton == null) + throw new RelProxyException("Execute first the init method"); + } + + protected void init(@NotNull GenericProxyConfigBaseImpl config) { + reloadListener = config.getRelProxyOnReloadListener(); + } + + @Nullable + public RelProxyOnReloadListener getRelProxyOnReloadListener() { + return reloadListener; + } + + @Nullable + public T create(@Nullable T obj, @NotNull Class clasz) { + if (obj == null) return null; + + return (T) create(obj, new Class[]{clasz}); + } + + @Nullable + public Object create(@Nullable Object obj, @NotNull Class[] classes) { + if (obj == null) return null; + + InvocationHandler handler = createGenericProxyInvocationHandler(obj); + + Class aClass = obj.getClass(); + assert aClass != null; + Object proxy = Proxy.newProxyInstance(aClass.getClassLoader(), classes, handler); + return proxy; + } + + + @NotNull + public abstract GenericProxyInvocationHandler createGenericProxyInvocationHandler(@NotNull Object obj); +} diff --git a/src/main/java/com/sillelien/jas/impl/GenericProxyInvocationHandler.java b/src/main/java/com/sillelien/jas/impl/GenericProxyInvocationHandler.java new file mode 100644 index 0000000..db6143a --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/GenericProxyInvocationHandler.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl; + +import com.sillelien.jas.RelProxyOnReloadListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Objects; + +/** + * @author jmarranz + */ +public abstract class GenericProxyInvocationHandler implements InvocationHandler { + @NotNull + protected GenericProxyImpl root; + @NotNull + protected GenericProxyVersionedObject verObj; + + public GenericProxyInvocationHandler(@NotNull GenericProxyImpl root) { + this.root = root; + } + + @NotNull + private Object getCurrent() { + assert verObj != null; + return verObj.getCurrent(); + } + + @NotNull + private Object getNewVersion() throws Throwable { + assert verObj != null; + return verObj.getNewVersion(); + } + + @Nullable + @Override + public synchronized Object invoke(@NotNull Object proxy, @NotNull Method method, @Nullable Object[] args) throws Throwable { + Object oldObj = getCurrent(); + Object obj = getNewVersion(); + + assert root != null; + RelProxyOnReloadListener reloadListener = root.getRelProxyOnReloadListener(); + if (!Objects.equals(oldObj, obj) && (reloadListener != null)) + reloadListener.onReload(oldObj, obj, proxy, method, args); + + if ((args != null) && (args.length == 1)) { + // Conseguimos que en proxy1.equals(proxy2) se usen los objetos asociados no los propios proxies, para ello obtenemos el objeto asociado al parámetro + // No hace falta que equals forme parte de la interface, pero está ahí implícitamente + // hashCode() como no tiene params es llamado sin problema de conversiones + Object param = args[0]; + if (!(param instanceof Proxy) || // Si es una clase generada com.sun.proxy.$ProxyN (N=1,2...) es también derivada de Proxy + !Objects.equals(method.getName(), "equals") || + !boolean.class.equals(method.getReturnType())) { + return method.invoke(obj, args); + } + Class[] paramTypes = method.getParameterTypes(); + assert paramTypes != null; + if ((paramTypes.length == 1) && Object.class.equals(paramTypes[0])) { + InvocationHandler paramInvHandler = Proxy.getInvocationHandler(param); + if (paramInvHandler instanceof GenericProxyInvocationHandler) { + args[0] = ((GenericProxyInvocationHandler) paramInvHandler).getCurrent(); // reemplazamos el Proxy por el objeto asociado + } + } + } + + return method.invoke(obj, args); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/GenericProxyVersionedObject.java b/src/main/java/com/sillelien/jas/impl/GenericProxyVersionedObject.java new file mode 100644 index 0000000..f0595c2 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/GenericProxyVersionedObject.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl; + +import com.sillelien.jas.RelProxyException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Objects; + +/** + * @author jmarranz + */ +public abstract class GenericProxyVersionedObject { + @NotNull + protected Object obj; + @NotNull + protected GenericProxyInvocationHandler parent; + + public GenericProxyVersionedObject(@NotNull Object obj, @NotNull GenericProxyInvocationHandler parent) { + this.obj = obj; + this.parent = parent; + } + + protected static void getTreeFields(@NotNull Class clasz, @NotNull Object obj, @NotNull ArrayList fieldList, @Nullable ArrayList valueList) throws IllegalAccessException { + getFields(clasz, obj, fieldList, valueList); + Class superClass = clasz.getSuperclass(); + if (superClass != null) + getTreeFields(superClass, obj, fieldList, valueList); + } + + protected static void getFields(@NotNull Class clasz, + @NotNull Object obj, + @NotNull ArrayList fieldList, + @Nullable ArrayList valueList) throws IllegalAccessException { + Field[] fieldListClass = clasz.getDeclaredFields(); + assert fieldListClass != null; + for (int i = 0; i < fieldListClass.length; i++) { + Field field = fieldListClass[i]; + fieldList.add(field); + if (valueList != null) { + field.setAccessible(true); + Object value = field.get(obj); + valueList.add(value); + } + } + } + + @NotNull + public Object getCurrent() { + return obj; + } + + @NotNull + public Object getNewVersion() throws Throwable { + Class newClass = reloadClass(); + if (newClass == null) + return obj; + + Class oldClass = obj.getClass(); + if (!Objects.equals(newClass, oldClass)) { + obj = copy(oldClass, obj, newClass); + } + + return obj; + } + + @NotNull + private Object copy(@NotNull Class oldClass, @NotNull Object oldObj, @NotNull Class newClass) throws IllegalAccessException, InstantiationException, IllegalArgumentException, InvocationTargetException { + Object newObj; + + ArrayList fieldListOld = new ArrayList<>(); + ArrayList valueListOld = new ArrayList<>(); + + getTreeFields(oldClass, oldObj, fieldListOld, valueListOld); + + Class enclosingClassNew = newClass.getEnclosingClass(); + if (enclosingClassNew == null) { + Constructor construc; + try { + construc = newClass.getConstructor(); + } catch (NoSuchMethodException ex) { + throw new RelProxyException("Cannot reload " + newClass.getName() + " a default empty of params constructor is required", ex); + } + assert construc != null; + newObj = construc.newInstance(); + } else { + // En el caso de inner class o anonymous inner class el constructor por defecto se obtiene de forma diferente, útil para los EventListener de ItsNat + Constructor construc; + try { + construc = newClass.getDeclaredConstructor(enclosingClassNew); + } catch (NoSuchMethodException ex) // Yo creo que nunca ocurre al menos no en anonymous inner classes pero por si acaso + { + throw new RelProxyException("Cannot reload " + newClass.getName() + " a default empty of params constructor is required", ex); + } + assert construc != null; + construc.setAccessible(true); // Necesario + + // http://stackoverflow.com/questions/1816458/getting-hold-of-the-outer-class-object-from-the-inner-class-object + + + Field enclosingFieldOld; + try { + enclosingFieldOld = oldClass.getDeclaredField("this$0"); + } catch (NoSuchFieldException ex) { + throw new RelProxyException(ex); + } + assert enclosingFieldOld != null; + enclosingFieldOld.setAccessible(true); + Object enclosingObjectOld = enclosingFieldOld.get(oldObj); + assert enclosingObjectOld != null; + Object enclosingObjectNew = copy(enclosingObjectOld.getClass(), enclosingObjectOld, enclosingClassNew); + + newObj = construc.newInstance(enclosingObjectNew); + } + + + ArrayList fieldListNew = new ArrayList<>(); + + getTreeFields(newClass, newObj, fieldListNew, null); + + if (fieldListOld.size() != fieldListNew.size()) + throw new RelProxyException("Cannot reload " + newClass.getName() + " number of fields have changed, redeploy"); + + for (int i = 0; i < fieldListOld.size(); i++) { + Field fieldOld = fieldListOld.get(i); + Field fieldNew = fieldListNew.get(i); + assert fieldOld != null; + assert fieldNew != null; + + if ((enclosingClassNew != null) && "this$0".equals(fieldOld.getName()) && "this$0".equals(fieldNew.getName())) + continue; // Ya están correctamente definidos + + + Class type = fieldOld.getType(); + assert type != null; + + Class fieldNewType = fieldNew.getType(); + + if ((!ignoreField(fieldOld) && !fieldOld.getName().equals(fieldNew.getName())) || + !type.equals(fieldNewType)) + throw new RelProxyException("Cannot reload " + newClass.getName() + " fields have changed, redeploy"); + + Object fieldObj = valueListOld.get(i); + fieldNew.setAccessible(true); + int modifiersNew = fieldNew.getModifiers(); + boolean isStaticFinal = Modifier.isStatic(modifiersNew) && Modifier.isFinal(modifiersNew); + Field modifiersField = null; + if (isStaticFinal) { + // http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection + try { + modifiersField = Field.class.getDeclaredField("modifiers"); + } catch (NoSuchFieldException ex) { + throw new RelProxyException(ex); + } + assert modifiersField != null; + modifiersField.setAccessible(true); + modifiersField.setInt(fieldNew, fieldNew.getModifiers() & ~Modifier.FINAL); // Quitamos el modifier final + } + + fieldNew.set(newObj, fieldObj); + + if (modifiersField != null) { + modifiersField.setInt(fieldNew, fieldNew.getModifiers() & ~Modifier.FINAL); // Restauramos el modifier final + } + } + return newObj; + } + + @Nullable + protected abstract Class reloadClass(); + + protected abstract boolean ignoreField(@NotNull Field field); +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/JProxyConfigImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/JProxyConfigImpl.java new file mode 100644 index 0000000..85621da --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/JProxyConfigImpl.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.RelProxyOnReloadListener; +import com.sillelien.jas.impl.GenericProxyConfigBaseImpl; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import com.sillelien.jas.jproxy.JProxyCompilerListener; +import com.sillelien.jas.jproxy.JProxyConfig; +import com.sillelien.jas.jproxy.JProxyDiagnosticsListener; +import com.sillelien.jas.jproxy.JProxyInputSourceFileExcludedListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jmarranz + */ +public class JProxyConfigImpl extends GenericProxyConfigBaseImpl implements JProxyConfig { + @Nullable + protected File folderSources; + + @Nullable + protected FolderSourceList folderSourceList; + + @Nullable + protected FolderSourceList requiredExtraJarPaths; + + @Nullable + protected JProxyInputSourceFileExcludedListener excludedListener; + + @Nullable + protected JProxyCompilerListener compilerListener; + + @Nullable + protected String classFolder; + + protected long scanPeriod = -1; + + @Nullable + protected Iterable compilationOptions; + + @Nullable + protected JProxyDiagnosticsListener diagnosticsListener; + + protected boolean test; + + @NotNull + private List imports= new ArrayList<>(); + @NotNull + private List staticImports= new ArrayList<>(); + + @NotNull + @Override + public JProxyConfig setEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + @NotNull + @Override + public JProxyConfig setRelProxyOnReloadListener(@NotNull RelProxyOnReloadListener relListener) { + this.relListener = relListener; + return this; + } + + @NotNull + @Override + public JProxyConfig setInputPath(@Nullable String inputPath) { + String[] inputPaths = (inputPath != null) ? new String[]{inputPath} : null; + setInputPaths(inputPaths); // inputPath es null en el caso de shell interactive + return this; + } + + @NotNull + @Override + public JProxyConfig setInputPaths(@Nullable String[] inputPaths) { + folderSourceList = new FolderSourceList(inputPaths, true); // inputPaths es null en el caso de shell interactive + return this; + } + + @NotNull + @Override + public JProxyConfig setRequiredExtraJarPaths(@NotNull String[] inputJarPaths) { + requiredExtraJarPaths = new FolderSourceList(inputJarPaths, false); // inputPaths es null en el caso de shell interactive + return this; + } + + @NotNull + @Override + public JProxyConfig setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener excludedListener) { + this.excludedListener = excludedListener; + return this; + } + + @NotNull + @Override + public JProxyConfig setJProxyCompilerListener(@NotNull JProxyCompilerListener compilerListener) { + this.compilerListener = compilerListener; + return this; + } + + @NotNull + @Override + public JProxyConfig setClassFolder(String classFolder) { + this.classFolder = classFolder; + return this; + } + + @NotNull + @Override + public JProxyConfig setScanPeriod(long scanPeriod) { + if (scanPeriod == 0) throw new RelProxyException("scanPeriod cannot be zero"); + this.scanPeriod = scanPeriod; + return this; + } + + @NotNull + @Override + public JProxyConfig setCompilationOptions(@NotNull Iterable compilationOptions) { + this.compilationOptions = compilationOptions; + return this; + } + + @NotNull + @Override + public JProxyConfig setJProxyDiagnosticsListener(@NotNull JProxyDiagnosticsListener diagnosticsListener) { + this.diagnosticsListener = diagnosticsListener; + return this; + } + + @Nullable + public FolderSourceList getFolderSourceList() { + return folderSourceList; + } + + @Nullable + public FolderSourceList getRequiredExtraJarPaths() { + return requiredExtraJarPaths; + } + + @Nullable + public JProxyInputSourceFileExcludedListener getJProxyInputSourceFileExcludedListener() { + return excludedListener; + } + + @Nullable + public JProxyCompilerListener getJProxyCompilerListener() { + return compilerListener; + } + + @Nullable + public String getClassFolder() { + return classFolder; + } + + public long getScanPeriod() { + return scanPeriod; + } + + @Nullable + public Iterable getCompilationOptions() { + return compilationOptions; + } + + @Nullable + public JProxyDiagnosticsListener getJProxyDiagnosticsListener() { + return diagnosticsListener; + } + + public boolean isTest() { + return test; + } + + public void setTest(boolean test) { + this.test = test; + } + + @NotNull + public List getImports() { + return imports; + } + + @NotNull + public JProxyConfig setImports(@NotNull List imports) { + this.imports = imports; + return this; + } + + @NotNull + public List getStaticImports() { + return staticImports; + } + + @NotNull + public JProxyConfig setStaticImports(@NotNull List staticImports) { + this.staticImports = staticImports; + return this; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/JProxyDefaultImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/JProxyDefaultImpl.java new file mode 100644 index 0000000..e92ba31 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/JProxyDefaultImpl.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy; + +import com.sillelien.jas.impl.jproxy.core.JProxyImpl; +import com.sillelien.jas.jproxy.JProxyConfig; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author jmarranz + */ +public class JProxyDefaultImpl extends JProxyImpl { + public JProxyDefaultImpl() { + super(); + } + + @Nullable + @Override + public Class getMainParamClass() { + return null; + } + + public static JProxyConfig createJProxyConfig() { + return new JProxyConfigImpl(); + } + + public static void initStatic(@NotNull JProxyConfigImpl config) { + if (!config.isEnabled()) return; + + checkSingletonNull(SINGLETON); + SINGLETON = new JProxyDefaultImpl(); + SINGLETON.init(config); + } + + + @Nullable + public static T createStatic(@NotNull T obj, @NotNull Class clasz) { + if (SINGLETON == null) + return obj; // No se ha llamado al init o enabled = false + + return SINGLETON.create(obj, clasz); + } + + @Nullable + public static Object createStatic(@NotNull Object obj, @NotNull Class[] classes) { + if (SINGLETON == null) + return obj; // No se ha llamado al init o enabled = false + + return SINGLETON.create(obj, classes); + } + + + public static boolean isEnabledStatic() { + if (SINGLETON == null) + return false; + + return SINGLETON.isEnabled(); + } + + + public static boolean isRunningStatic() { + if (SINGLETON == null) + return false; + + return SINGLETON.isRunning(); + } + + public static boolean stopStatic() { + if (SINGLETON == null) + return false; + + return SINGLETON.stop(); + } + + public static boolean startStatic() { + if (SINGLETON == null) + return false; + + return SINGLETON.start(); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/JProxyUtil.java b/src/main/java/com/sillelien/jas/impl/jproxy/JProxyUtil.java new file mode 100644 index 0000000..b813ed1 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/JProxyUtil.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy; + +import com.sillelien.jas.RelProxyException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.net.URLConnection; + +/** + * @author jmarranz + */ +public final class JProxyUtil { + public static String getCanonicalPath(@NotNull File file) { + try { + return file.getCanonicalPath(); + } catch (IOException ex) { + throw new RelProxyException(ex); + } + } + + public static String getFileExtension(@NotNull File file) { + String path = file.getAbsolutePath(); + int pos = path.lastIndexOf('.'); + if (pos != -1) + return path.substring(pos + 1); + return ""; + } + + @Nullable + public static File getParentDir(@NotNull File file) { + return file.getParentFile(); + } + + @NotNull + public static byte[] readURL(@NotNull URL url) { + URLConnection urlCon; + try { + urlCon = url.openConnection(); + return readInputStream(urlCon.getInputStream()); + } catch (IOException ex) { + throw new RelProxyException(ex); + } + } + + public static byte[] readFile(@NotNull File file) { + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + } catch (FileNotFoundException ex) { + throw new RelProxyException(ex); + } + + return readInputStream(fis); + } + + @NotNull + public static byte[] readInputStream(@NotNull InputStream is) { + return readInputStream(is, 50); // 50Kb => unas 100 lecturas 5 Mb + } + + @NotNull + public static byte[] readInputStream(@NotNull InputStream is, int bufferSizeKb) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + byte[] buffer = new byte[bufferSizeKb * 1024]; + + int size; + while ((size = is.read(buffer)) != -1) { + out.write(buffer, 0, size); + } + } catch (IOException ex) { + throw new RelProxyException(ex); + } finally { + try { + is.close(); + } catch (IOException ex2) { + throw new RelProxyException(ex2); + } + } + + return out.toByteArray(); + } + + public static void saveFile(@NotNull File file, @NotNull byte[] content) { + File parent = getParentDir(file); + if (parent != null) parent.mkdirs(); + FileOutputStream out = null; + try { + out = new FileOutputStream(file); + out.write(content, 0, content.length); + } catch (IOException ex) { + throw new RelProxyException(ex); + } finally { + if (out != null) try { + out.close(); + } catch (IOException ex2) { + throw new RelProxyException(ex2); + } + } + } + + public static String readTextFile(@NotNull File file, @NotNull String encoding) { + Reader reader = null; + try { + reader = new InputStreamReader(new FileInputStream(file), encoding); // FileReader no permite especificar el encoding y total no hace nada que no haga InputStreamReader + } catch (IOException ex) { + throw new RelProxyException(ex); + } + + return readTextFile(reader); + } + + public static String readTextFile(@NotNull Reader reader) { + BufferedReader br = null; + try { + br = new BufferedReader(reader); // FileReader no permite especificar el encoding y total no hace nada que no haga InputStreamReader + StringBuilder sb = new StringBuilder(); + String line = br.readLine(); + + while (line != null) { + sb.append(line); + sb.append('\n'); + line = br.readLine(); + } + return sb.toString(); + } catch (IOException ex) { + throw new RelProxyException(ex); + } finally { + if (br != null) try { + br.close(); + } catch (IOException ex) { + throw new RelProxyException(ex); + } + } + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyImpl.java new file mode 100644 index 0000000..e6efe52 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyImpl.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core; + +import com.sillelien.jas.impl.GenericProxyImpl; +import com.sillelien.jas.impl.GenericProxyInvocationHandler; +import com.sillelien.jas.impl.jproxy.JProxyConfigImpl; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngine; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import com.sillelien.jas.jproxy.JProxyCompilerListener; +import com.sillelien.jas.jproxy.JProxyDiagnosticsListener; +import com.sillelien.jas.jproxy.JProxyInputSourceFileExcludedListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author jmarranz + */ +public abstract class JProxyImpl extends GenericProxyImpl { + @NotNull + public static JProxyImpl SINGLETON; + @Nullable + protected JProxyEngine engine; + @NotNull + private List imports; + @NotNull + private List staticImports; + + + protected JProxyImpl() { + super(); + } + + @Nullable + public static ClassLoader getDefaultClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + @Nullable + public ClassDescriptorSourceScript init(@NotNull JProxyConfigImpl config) { + return init(config, null, null); + } + + @Nullable + public ClassDescriptorSourceScript init(@NotNull JProxyConfigImpl config, @Nullable SourceScriptRoot scriptFile, @Nullable ClassLoader classLoader) { + super.init(config); + + FolderSourceList folderSourceList = config.getFolderSourceList(); + FolderSourceList requiredExtraJarPaths = config.getRequiredExtraJarPaths(); + JProxyInputSourceFileExcludedListener excludedListener = config.getJProxyInputSourceFileExcludedListener(); + JProxyCompilerListener compilerListener = config.getJProxyCompilerListener(); + String classFolder = config.getClassFolder(); + long scanPeriod = config.getScanPeriod(); + Iterable compilationOptions = config.getCompilationOptions(); + JProxyDiagnosticsListener diagnosticsListener = config.getJProxyDiagnosticsListener(); + boolean enabled = config.isEnabled(); + imports= config.getImports(); + staticImports= config.getStaticImports(); + classLoader = (classLoader != null) ? classLoader : getDefaultClassLoader(); + engine = new JProxyEngine(this, enabled, scriptFile, classLoader, folderSourceList, requiredExtraJarPaths, classFolder, + scanPeriod, excludedListener, compilerListener, compilationOptions, diagnosticsListener); + + return engine.init(); + } + + @Nullable + public JProxyEngine getJProxyEngine() { + return engine; + } + + public boolean isEnabled() { + assert engine != null; + return engine.isEnabled(); + } + + public boolean isRunning() { + assert engine != null; + return engine.isRunning(); + } + + public boolean stop() { + assert engine != null; + return engine.stop(); + } + + public boolean start() { + assert engine != null; + return engine.start(); + } + + @NotNull + @Override + public GenericProxyInvocationHandler createGenericProxyInvocationHandler(@NotNull Object obj) { + return new JProxyInvocationHandler(obj, this); + } + + @Nullable + public abstract Class getMainParamClass(); + + @NotNull + public List getImports() { + return imports; + } + + public void setImports(@NotNull List imports) { + this.imports = imports; + } + + @NotNull + public List getStaticImports() { + return staticImports; + } + + public void setStaticImports(@NotNull List staticImports) { + this.staticImports = staticImports; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyInvocationHandler.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyInvocationHandler.java new file mode 100644 index 0000000..ef055dc --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyInvocationHandler.java @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core; + +import com.sillelien.jas.impl.GenericProxyInvocationHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * @author jmarranz + */ +public class JProxyInvocationHandler extends GenericProxyInvocationHandler { + public JProxyInvocationHandler(@NotNull Object obj, @NotNull JProxyImpl root) { + super(root); + verObj = new JProxyVersionedObject(obj, this); + } + + @NotNull + public JProxyImpl getJProxyImpl() { + return Objects.requireNonNull((JProxyImpl) root); + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyVersionedObject.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyVersionedObject.java new file mode 100644 index 0000000..3db66a9 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/JProxyVersionedObject.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core; + +import com.sillelien.jas.impl.GenericProxyVersionedObject; +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngine; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; + +/** + * @author jmarranz + */ +public class JProxyVersionedObject extends GenericProxyVersionedObject { + @NotNull + protected String className; + + public JProxyVersionedObject(@NotNull Object obj, @NotNull JProxyInvocationHandler parent) { + super(obj, parent); + Class aClass = obj.getClass(); + assert aClass != null; + className = aClass.getName(); + } + + @Nullable + public JProxyInvocationHandler getJProxyInvocationHandler() { + return (JProxyInvocationHandler) parent; + } + + @Nullable + @Override + protected Class reloadClass() { + JProxyInvocationHandler jProxyInvocationHandler = getJProxyInvocationHandler(); + assert jProxyInvocationHandler != null; + JProxyEngine engine = jProxyInvocationHandler.getJProxyImpl().getJProxyEngine(); + assert engine != null; + engine.reloadWhenChanged(); + return engine.findClass(className); + } + + @Override + protected boolean ignoreField(@NotNull Field field) { + return false; // Todos cuentan (útil en Groovy no en Java) + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/FolderSourceList.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/FolderSourceList.java new file mode 100644 index 0000000..2430fa8 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/FolderSourceList.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.FileExt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +/** + * @author jmarranz + */ +public class FolderSourceList { + @Nullable + protected FileExt[] sourceList; + + public FolderSourceList(@Nullable final String[] sourcePathList, boolean expectedDirectory) { + if (sourcePathList != null) // En el caso de shell interactivo es null + { + // El convertir siempre a File los paths es para normalizar paths + sourceList = new FileExt[sourcePathList.length]; + for (int i = 0; i < sourcePathList.length; i++) { + String pathname = sourcePathList[i]; + assert pathname != null; + File folder = new File(pathname); + if (!folder.exists()) + throw new RelProxyException("Source folder does not exist: " + folder.getAbsolutePath()); + boolean isDirectory = folder.isDirectory(); + if (expectedDirectory) { + if (!isDirectory) + throw new RelProxyException("Source folder is not a directory: " + folder.getAbsolutePath()); + } else { + if (isDirectory) + throw new RelProxyException("Expected a file not a directory: " + folder.getAbsolutePath()); + } + sourceList[i] = new FileExt(folder); + } + } + } + + @Nullable + public FileExt[] getArray() { + return sourceList; + } + + @Nullable + public String buildClassNameFromFile(@NotNull FileExt sourceFile) { + assert sourceList != null; + for (FileExt rootFolderOfSources : sourceList) { + assert rootFolderOfSources != null; + String className = buildClassNameFromFile(sourceFile, rootFolderOfSources); + if (className != null) + return className; + } + throw new RelProxyException("File not found in source folders: " + sourceFile.getFile().getAbsolutePath()); + } + + @Nullable + public static String buildClassNameFromFile(@NotNull FileExt sourceFile, @NotNull FileExt rootFolderOfSources) { + String path = sourceFile.getCanonicalPath(); + + String rootFolderOfSourcesAbsPath = rootFolderOfSources.getCanonicalPath(); + int pos = path.indexOf(rootFolderOfSourcesAbsPath); + if (pos == 0) // Está en este source folder + { + path = path.substring(rootFolderOfSourcesAbsPath.length() + 1); // Sumamos +1 para quitar también el / separador del pathInput y el path relativo de la clase + // Puede no tener extensión (script) o bien ser .java o bien ser una inventada (ej .jsh), la quitamos si existe + pos = path.lastIndexOf('.'); + if (pos != -1) + path = path.substring(0, pos); + path = path.replace(File.separatorChar, '.'); // getAbsolutePath() normaliza con el caracter de la plataforma + return path; + } + return null; + } + + +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyClassLoader.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyClassLoader.java similarity index 52% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyClassLoader.java rename to src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyClassLoader.java index ac1a163..4ea3e1c 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyClassLoader.java +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyClassLoader.java @@ -1,150 +1,174 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import java.net.URL; - -/** - * - * @author jmarranz - */ -public class JProxyClassLoader extends ClassLoader -{ - protected final JProxyEngine engine; - - public JProxyClassLoader(JProxyEngine engine) - { - super(engine.getRootClassLoader()); - - this.engine = engine; - } - - public Class defineClass(ClassDescriptor classDesc) - { - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - String className = classDesc.getClassName(); - byte[] classBytes = classDesc.getClassBytes(); - Class clasz = defineClass(className,classBytes, 0, classBytes.length); - classDesc.setLastLoadedClass(clasz); - return clasz; - } - } - - @Override - protected Class findClass(String name) throws ClassNotFoundException - { - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - return loadClass(name,true); - - /* - Class cls = findLoadedClass(name); - if (cls == null) - return loadClass(name,true); - - //if (cls == null) - // return getParent().loadClass(name); // Dará un ClassNotFoundException si no puede cargarla - - return cls; - */ - } - } - - public Class loadClass(ClassDescriptor classDesc,boolean resolve) - { - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - Class clasz = classDesc.getLastLoadedClass(); - if (clasz != null && clasz.getClassLoader() == this) return clasz; // Glup, ya fue cargada - clasz = defineClass(classDesc); - if (resolve) { - resolveClass(clasz); - } - return clasz; - } - } - - public Class loadInnerClass(ClassDescriptorSourceUnit parentDesc,String innerClassName) - { - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - ClassDescriptor classDesc = parentDesc.getInnerClassDescriptor(innerClassName,false); - if (classDesc == null || classDesc.getClassBytes() == null) - { - byte[] classBytes = getClassBytesFromResource(innerClassName); - if (classBytes == null) return null; - if (classDesc == null) classDesc = parentDesc.addInnerClassDescriptor(innerClassName); - classDesc.setClassBytes(classBytes); - } - - return defineClass(classDesc); - } - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException - { - // Inspiraciones en URLClassLoader.findClass y en el propio análisis de ClassLoader.loadClass - // Lo redefinimos por si acaso porque el objetivo es recargar todas las clases hot-reloaded en este ClassLoader y no delegar en el parent - // (el comportamiento por defecto de loadClass) pues las clases cargadas con el parent tenderán a cargar las clases vinculadas con dicho ClassLoader - - // En teoría este método redefinido no es necesario porque manualmente detectamos los cambios de código fuente, recompilamos y recargamos explícitamente - // con defineClass el cual no carga también las innerclasses vinculadas, - // pero si el código fuente tiene innerclasses y no ha sido cambiado nunca, las innerclasses pueden no ser conocidas como ClassDescriptor, - // necesitamos detectar las innerclasses para cargarlas también tras la carga de la clase contenedora, - // para ello ejecutamos Class.getDeclaredClasses() para que cargue las innerclasses indirectamente, pasando entonces por aquí. - - // Hay un caso más y es el de la clase base que es una clase hot loadable con su propio archivo, al ejecutar el defineClass en la clase - // derivada la clase base también debe cargarse en ese momento y es posible que no haya sido hecho explícitamente (por ej porque no ha cambiado o su carga va después) - // por lo que pasaremos por aquí y debemos cargarla aquí, luego no hay problema de recarga explícita porque sabemos que ha sido ya cargada - // y tampoco hay problema de auto-salvado del .class o eliminación del mismo puesto que al ser un archivo fuente normal se tratará por si mismo - // aunque la carga en el class loader se haya hecho a través de una clase derivada quizás antes - - Object monitor = engine.getMonitor(); - synchronized(monitor) - { - Class cls = findLoadedClass(name); - if (cls == null) - { - ClassDescriptor classDesc = engine.getClassDescriptor(name); // Si es una inner class se crea el descriptor y se añade al source file asociado automáticamente - if (classDesc != null) - { - byte[] classBytes = classDesc.getClassBytes(); - if (classBytes == null) - { - classBytes = getClassBytesFromResource(name); // No puede ser nulo - classDesc.setClassBytes(classBytes); - } - - cls = defineClass(classDesc); - } - - if (cls == null) - { - cls = getParent().loadClass(name); // Dará un ClassNotFoundException si no puede cargarla - } - } - - if (cls == null) throw new ClassNotFoundException(name); - - if (resolve) { - resolveClass(cls); - } - return cls; - } - } - - private byte[] getClassBytesFromResource(String className) - { - String relClassPath = ClassDescriptor.getRelativeClassFilePathFromClassName(className); - URL urlClass = getResource(relClassPath); - if (urlClass == null) return null; - return JProxyUtil.readURL(urlClass); - } -} +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr; + +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URL; +import java.util.Objects; + +/** + * @author jmarranz + */ +public class JProxyClassLoader extends ClassLoader { + @NotNull + protected final JProxyEngine engine; + + public JProxyClassLoader(@NotNull JProxyEngine engine) { + super(engine.getRootClassLoader()); + + this.engine = engine; + } + + @NotNull + public Class defineClass(@NotNull ClassDescriptor classDesc) { + Object monitor = engine.getMonitor(); + synchronized (monitor) { + String className = classDesc.getClassName(); + byte[] classBytes = classDesc.getClassBytes(); + assert classBytes != null; + @NotNull Class clasz = defineClass(className, classBytes, 0, classBytes.length); + classDesc.setLastLoadedClass(clasz); + return clasz; + } + } + + @Override + @NotNull + protected Class findClass(@NotNull String name) throws ClassNotFoundException { + Object monitor = engine.getMonitor(); + synchronized (monitor) { + return loadClass(name, true); + + /* + Class cls = findLoadedClass(name); + if (cls == null) + return loadClass(name,true); + + //if (cls == null) + // return getParent().loadClass(name); // Dará un ClassNotFoundException si no puede cargarla + + return cls; + */ + } + } + + @NotNull + public Class loadClass(@NotNull ClassDescriptor classDesc, boolean resolve) { + Object monitor = engine.getMonitor(); + synchronized (monitor) { + Class clasz = classDesc.getLastLoadedClass(); + if ((clasz != null) && Objects.equals(clasz.getClassLoader(), this)) return clasz; // Glup, ya fue cargada + clasz = defineClass(classDesc); + if (resolve) { + resolveClass(clasz); + } + return clasz; + } + } + + @Nullable + public Class loadInnerClass(@NotNull ClassDescriptorSourceUnit parentDesc, @NotNull String innerClassName) { + Object monitor = engine.getMonitor(); + synchronized (monitor) { + ClassDescriptor classDesc = parentDesc.getInnerClassDescriptor(innerClassName, false); + if ((classDesc == null) || (classDesc.getClassBytes() == null)) { + byte[] classBytes = getClassBytesFromResource(innerClassName); + if (classBytes == null) { + return null; + } + if (classDesc == null) { + classDesc = parentDesc.addInnerClassDescriptor(innerClassName); + } + if (classDesc != null) { + classDesc.setClassBytes(classBytes); + } + } + + if (classDesc != null) { + return defineClass(classDesc); + } else { + return null; + } + } + } + + @Override + @NotNull + protected Class loadClass(@NotNull String name, boolean resolve) throws ClassNotFoundException { + // Inspiraciones en URLClassLoader.findClass y en el propio análisis de ClassLoader.loadClass + // Lo redefinimos por si acaso porque el objetivo es recargar todas las clases hot-reloaded en este ClassLoader y no delegar en el parent + // (el comportamiento por defecto de loadClass) pues las clases cargadas con el parent tenderán a cargar las clases vinculadas con dicho ClassLoader + + // En teoría este método redefinido no es necesario porque manualmente detectamos los cambios de código fuente, recompilamos y recargamos explícitamente + // con defineClass el cual no carga también las innerclasses vinculadas, + // pero si el código fuente tiene innerclasses y no ha sido cambiado nunca, las innerclasses pueden no ser conocidas como ClassDescriptor, + // necesitamos detectar las innerclasses para cargarlas también tras la carga de la clase contenedora, + // para ello ejecutamos Class.getDeclaredClasses() para que cargue las innerclasses indirectamente, pasando entonces por aquí. + + // Hay un caso más y es el de la clase base que es una clase hot loadable con su propio archivo, al ejecutar el defineClass en la clase + // derivada la clase base también debe cargarse en ese momento y es posible que no haya sido hecho explícitamente (por ej porque no ha cambiado o su carga va después) + // por lo que pasaremos por aquí y debemos cargarla aquí, luego no hay problema de recarga explícita porque sabemos que ha sido ya cargada + // y tampoco hay problema de auto-salvado del .class o eliminación del mismo puesto que al ser un archivo fuente normal se tratará por si mismo + // aunque la carga en el class loader se haya hecho a través de una clase derivada quizás antes + + Object monitor = engine.getMonitor(); + synchronized (monitor) { + Class cls = findLoadedClass(name); + if (cls == null) { + ClassDescriptor classDesc = engine.getClassDescriptor(name); // Si es una inner class se crea el descriptor y se añade al source file asociado automáticamente + if (classDesc != null) { + byte[] classBytes = classDesc.getClassBytes(); + if (classBytes == null) { + classBytes = getClassBytesFromResource(name); // No puede ser nulo + classDesc.setClassBytes(classBytes); + } + + cls = defineClass(classDesc); + } + + if (cls == null) { + ClassLoader parent = getParent(); + if (parent != null) { + cls = parent.loadClass(name); // Dará un ClassNotFoundException si no puede cargarla + } + } + } + + if (cls == null) throw new ClassNotFoundException(name); + + if (resolve) { + resolveClass(cls); + } + return cls; + } + } + + @Nullable + private byte[] getClassBytesFromResource(@NotNull String className) { + String relClassPath = ClassDescriptor.getRelativeClassFilePathFromClassName(className); + URL urlClass = getResource(relClassPath); + if (urlClass == null) { + return null; + } + return JProxyUtil.readURL(urlClass); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyEngine.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyEngine.java new file mode 100644 index 0000000..c544f18 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyEngine.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr; + +import com.sillelien.jas.impl.jproxy.core.JProxyImpl; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorInner; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import com.sillelien.jas.jproxy.JProxyCompilerListener; +import com.sillelien.jas.jproxy.JProxyDiagnosticsListener; +import com.sillelien.jas.jproxy.JProxyInputSourceFileExcludedListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.Timer; +import java.util.TimerTask; + +/** + * @author jmarranz + */ +public class JProxyEngine { + @NotNull + protected final Object monitor = new Object(); // Podríamos usar este objeto JProxyEngine directamente pero el monitor es mejor para análisis de dependencias + + @NotNull + private static final Logger log = LoggerFactory.getLogger("JProxyEngine"); + + @NotNull + protected final JProxyImpl parent; + @NotNull + protected final JProxyEngineChangeDetectorAndCompiler delegateChangeDetector; + @Nullable + protected final ClassLoader rootClassLoader; + @Nullable + protected JProxyClassLoader customClassLoader; + protected final long scanPeriod; + @NotNull + protected final String sourceEncoding = "UTF-8"; // Por ahora, provisional + public volatile boolean stop; + @Nullable + protected TimerTask task; + protected boolean pendingReload; + protected final boolean enabled; + + public JProxyEngine(@NotNull JProxyImpl parent, boolean enabled, @Nullable SourceScriptRoot scriptFile, @Nullable ClassLoader rootClassLoader, @Nullable FolderSourceList folderSourceList, @Nullable FolderSourceList requiredExtraJarPaths, + @Nullable String folderClasses, long scanPeriod, @Nullable JProxyInputSourceFileExcludedListener excludedListener, + @Nullable JProxyCompilerListener compilerListener, @Nullable Iterable compilationOptions, @Nullable JProxyDiagnosticsListener diagnosticsListener) { + this.parent = parent; + this.enabled = enabled; + this.rootClassLoader = rootClassLoader; + this.scanPeriod = scanPeriod; + + + delegateChangeDetector = new JProxyEngineChangeDetectorAndCompiler(this, scriptFile, folderSourceList, + requiredExtraJarPaths, folderClasses, excludedListener, + compilationOptions, diagnosticsListener, + compilerListener); + customClassLoader = null; //new JProxyClassLoader(this); + } + + @NotNull + public Object getMonitor() { + return monitor; + } + + @NotNull + public JProxyImpl getJProxy() { + return parent; + } + + public boolean isEnabled() { + return enabled; + } + + @Nullable + public ClassDescriptorSourceScript init() { + synchronized (getMonitor()) { + ClassDescriptorSourceScript scriptFileDesc = detectChangesInSources(); // Primera vez para detectar cambios en los .java respecto a los .class mientras el servidor estaba parado + + reloadWhenChanged(); // La primera vez cargamos pues el código fuente manda sobre los .class + + startScanner(); + + return scriptFileDesc; + } + } + + /* + public JProxyClassLoader getJProxyClassLoader() + { + return customClassLoader; + } + */ + + @Nullable + public ClassLoader getCurrentClassLoader() { + if (customClassLoader != null) { + return customClassLoader; + } + return rootClassLoader; + } + + private boolean startScanner() { + if (scanPeriod > 0) // Si es 0 o negativo sólo se recargan una vez (la inicial ya ejecutada) + { + task = new TimerTask() { + @Override + public void run() { + if (stop) { + cancel(); + return; + } + + try { + detectChangesInSources(); // Está sincronizado las partes que lo necesitan + } catch (Exception ex) { + log.error(ex.getMessage(),ex); // Si dejamos subir la excepción se acabó el + // timer + } + } + }; + + new Timer().schedule(task, scanPeriod, scanPeriod); // Ojo, después de la primera llamada a detectChangesInSources() + return true; + } else { + return false; + } + } + + public void setPendingReload() { + pendingReload = true; + } + + + @Nullable + public ClassLoader getRootClassLoader() { + return rootClassLoader; + } + + @NotNull + public String getSourceEncoding() { + return sourceEncoding; + } + + public boolean isRunning() { + synchronized (getMonitor()) { + return (task != null) && (scanPeriod > 0); + } + } + + public boolean stop() { + synchronized (getMonitor()) { + if (task != null) { + stop = true; + task.cancel(); + task = null; + return true; + } else { + return false; + } + } + } + + public boolean start() { + synchronized (getMonitor()) { + if (task == null) { + stop = false; + return startScanner(); + } else return false; + } + } + + + @Nullable + public ClassDescriptor getClassDescriptor(@NotNull String className) { + synchronized (getMonitor()) { + return delegateChangeDetector.getClassDescriptor(className); + } + } + + @Nullable + public Class findClass(@NotNull String className) { + // Si ya está cargada la devuelve, y si no se cargó por ningún JProxyClassLoader se intenta cargar por el parent ClassLoader, por lo que siempre devolverá distinto de null si la clase está en el classpath, que debería ser lo normal + synchronized (getMonitor()) { + try { + if (customClassLoader != null) { + return customClassLoader.findClass(className); + } else { + ClassLoader rootClassLoader = this.rootClassLoader; + assert rootClassLoader != null; + return rootClassLoader.loadClass(className); + } + } catch (ClassNotFoundException ex) { + return null; + } + } + } + + private void addNewClassLoader() { + ClassDescriptorSourceFileRegistry sourceRegistry = delegateChangeDetector.getClassDescriptorSourceFileRegistry(); + + assert sourceRegistry != null; + for (ClassDescriptorSourceUnit sourceFile : sourceRegistry.getClassDescriptorSourceFileColl()) { + sourceFile.resetLastLoadedClass(); // resetea también las innerclasses + } + + customClassLoader = new JProxyClassLoader(this); + } + + + @NotNull + private Class reloadSource(@NotNull ClassDescriptorSourceUnit sourceFile) { + assert customClassLoader != null; + Class clasz = customClassLoader.loadClass(sourceFile, true); + reloadInnerClassesOnly(sourceFile, clasz); + return clasz; + } + + private void reloadInnerClassesOnly(@NotNull ClassDescriptorSourceUnit sourceFile, @NotNull Class classParent) { + + LinkedList innerClassDescList = sourceFile.getInnerClassDescriptors(); + if ((innerClassDescList != null) && !innerClassDescList.isEmpty()) { + // En el caso de una clase que ha sido compilada, las inner classes se descubren todas + for (ClassDescriptorInner innerClassDesc : innerClassDescList) { + assert customClassLoader != null; + customClassLoader.loadClass(innerClassDesc, true); + } + } else // Auto-Detección de innerclasses: puede ser un archivo fuente que posiblemente nunca se hayan tocado desde la carga inicial y por tanto quizás se desconocen las innerclasses + { + // Aprovechando la carga de la clase, hacemos el esfuerzo de cargar todas las clases dependientes lo más posible + classParent.getDeclaredClasses(); // Provoca que las inner clases miembro indirectamente se procesen y carguen a través del JProxyClassLoader de la clase padre clasz + + // Ahora bien, lo anterior NO sirve para las anonymous inner classes, afortunadamente en ese caso podemos conocer y cargar por fuerza bruta + // http://stackoverflow.com/questions/1654889/java-reflection-how-can-i-retrieve-anonymous-inner-classes?rq=1 + + for (int i = 1; i < Integer.MAX_VALUE; i++) // No te asustes por el MAX_VALUE, se parará tras unos poquitos ciclos + { + String anonClassName = sourceFile.getClassName() + "$" + i; + assert customClassLoader != null; + Class innerClasz = customClassLoader.loadInnerClass(sourceFile, anonClassName); + if (innerClasz == null) break; // No hay más o no hay ninguna (si i es 1) + } + + // ¿Qué es lo que queda por cargar pero que no podemos hacer explícitamente? + // 1) Las clases privadas autónomas que fueron definidas en el mismo archivo que la clase principal: no las soportamos pues no podemos identificar en el ClassLoader que es una clase "hot reloadable", no son inner classes en el sentido estricto + // 2) Las clases privadas "inner" locales, es decir no anónimas declaradas dentro de un método, se cargarán la primera vez que se usen, no podemos conocerlas a priori + // porque siguen la notación className$NclassName ej. JReloadExampleDocument$1AuxMemberInMethod. No hay problema con que se carguen con un class loader antiguo pues + // el ClassLoader de la clase padre contenedora será el encargado de cargarla en cuanto se pase por el método que la declara. + } + } + + @Nullable + public ClassDescriptorSourceScript detectChangesInSources() { + return delegateChangeDetector.detectChangesInSources(); + } + + @Nullable + public ClassDescriptorSourceScript detectChangesInSourcesAndReload() { + ClassDescriptorSourceScript res = detectChangesInSources(); + reloadWhenChanged(); + return res; + } + + public boolean reloadWhenChanged() { + synchronized (getMonitor()) { + if (pendingReload) { + addNewClassLoader(); + + ClassDescriptorSourceFileRegistry sourceRegistry = delegateChangeDetector.getClassDescriptorSourceFileRegistry(); + + assert sourceRegistry != null; + for (ClassDescriptorSourceUnit sourceFile : sourceRegistry.getClassDescriptorSourceFileColl()) // sourceRegistry NUNCA es nulo pues se ejecuta una primera vez en tiempo de inicialización + { + // if (sourceFilesCompiled.contains(sourceFile)) + // continue; + // las clases deleted no están en sourceFileMap por lo que no hay que filtrarlas + reloadSource(sourceFile); // Ponemos detectInnerClasses a true porque son archivos fuente que posiblemente nunca se hayan tocado desde la carga inicial y por tanto quizás se desconocen las innerclasses + } + + pendingReload = false; + return true; + } + return false; + } + } + +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyEngineChangeDetectorAndCompiler.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyEngineChangeDetectorAndCompiler.java similarity index 51% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyEngineChangeDetectorAndCompiler.java rename to src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyEngineChangeDetectorAndCompiler.java index b6f482b..355592c 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/JProxyEngineChangeDetectorAndCompiler.java +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JProxyEngineChangeDetectorAndCompiler.java @@ -1,305 +1,326 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootInMemory; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootFile; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorInner; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; -import com.innowhere.relproxy.impl.jproxy.JProxyUtil; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.JProxyCompilerContext; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.JProxyCompilerInMemory; -import com.innowhere.relproxy.jproxy.JProxyCompilerListener; -import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; -import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; -import java.io.File; -import java.util.ArrayList; -import java.util.LinkedList; - -/** - * - * @author jmarranz - */ -public class JProxyEngineChangeDetectorAndCompiler -{ - protected final JProxyEngine engine; - protected final JProxyCompilerInMemory compiler; - protected final FolderSourceList folderSourceList; - protected final FolderSourceList requiredExtraJarPaths; - protected final SourceScriptRoot scriptFile; // Puede ser nulo - protected final String folderClasses; // Puede ser nulo (es decir NO salvar como .class los cambios) - protected final JProxyInputSourceFileExcludedListener excludedListener; - protected final JavaSourcesSearch sourcesSearch; - protected final JProxyCompilerListener compilerListener; - protected volatile ClassDescriptorSourceFileRegistry sourceRegistry; - - public JProxyEngineChangeDetectorAndCompiler(JProxyEngine engine,SourceScriptRoot scriptFile,FolderSourceList folderSourceList,FolderSourceList requiredExtraJarPaths, - String folderClasses, JProxyInputSourceFileExcludedListener excludedListener,Iterable compilationOptions,JProxyDiagnosticsListener diagnosticsListener, - JProxyCompilerListener compilerListener) - { - this.engine = engine; - this.scriptFile = scriptFile; - this.folderSourceList = folderSourceList; - this.requiredExtraJarPaths = requiredExtraJarPaths; - this.folderClasses = folderClasses; - this.excludedListener = excludedListener; - this.compiler = new JProxyCompilerInMemory(this,compilationOptions,diagnosticsListener); - this.sourcesSearch = new JavaSourcesSearch(this); - this.compilerListener = compilerListener; - } - - public JProxyEngine getJProxyEngine() - { - return engine; - } - - public FolderSourceList getFolderSourceList() - { - return folderSourceList; - } - - public FolderSourceList getRequiredExtraJarPaths() - { - return requiredExtraJarPaths; - } - - public JProxyInputSourceFileExcludedListener getJProxyInputSourceFileExcludedListener() - { - return excludedListener; - } - - public ClassDescriptorSourceFileRegistry getClassDescriptorSourceFileRegistry() - { - return sourceRegistry; - } - - public ClassDescriptor getClassDescriptor(String className) - { - return sourceRegistry.getClassDescriptor(className); - } - - private boolean isSaveClassesMode() - { - return (folderClasses != null); - } - - private JProxyCompilerListener getJProxyCompilerListener() - { - return compilerListener; - } - - private void cleanBeforeCompile(ClassDescriptorSourceUnit sourceFile) - { - if (isSaveClassesMode()) - deleteClasses(sourceFile); // Antes de que nos las carguemos en memoria la clase principal y las inner tras recompilar - - sourceFile.cleanOnSourceCodeChanged(); // El código fuente nuevo puede haber cambiado totalmente las innerclasses antiguas (añadido, eliminado) y por supuesto el bytecode necesita olvidarse - } - - private void compile(ClassDescriptorSourceUnit sourceFile,JProxyCompilerContext context) - { - if (sourceFile.getClassBytes() != null) - return; // Ya ha sido compilado seguramente por dependencia de un archivo compilado inmediatamente antes, recuerda que el atributo classBytes se pone a null antes de compilar los archivos cambiados/nuevos - - compiler.compileSourceFile(sourceFile,context,engine.getCurrentClassLoader(),sourceRegistry); - } - - public ClassDescriptorSourceScript detectChangesInSources() - { - Object monitor = getJProxyEngine().getMonitor(); - - boolean firstTime; - - synchronized(monitor) - { - if (this.sourceRegistry == null) // Es null la primera vez - { - firstTime = true; - this.sourceRegistry = new ClassDescriptorSourceFileRegistry(); - } - else - { - firstTime = false; - sourceRegistry.setAllClassDescriptorSourceFilesPendingToRemove( true ); // A medida que los vamos encontrando ponemos a false, es mucho más rápido que recrear el registro si no ha cambiado nada (lo normal) - } - } - - LinkedList updatedSourceFiles = new LinkedList(); - LinkedList newSourceFiles = new LinkedList(); - - ClassDescriptorSourceScript scriptFileDesc = sourcesSearch.sourceFileSearch(firstTime,scriptFile,sourceRegistry,updatedSourceFiles,newSourceFiles); - - LinkedList deletedSourceFiles = new LinkedList(); - - if (!firstTime) - { - synchronized(monitor) - { - // Obtenemos los deletedSourceFiles detectados (si es firstTime no tiene sentido hacer esto no haría nada pero nos ahorramos synchronized y llamada) - sourceRegistry.getAllClassDescriptorSourceFilesPendingToRemove(deletedSourceFiles); - } - } - - if (updatedSourceFiles.isEmpty() && newSourceFiles.isEmpty() && deletedSourceFiles.isEmpty()) - return scriptFileDesc; - - // También el hecho de eliminar una clase debe implicar crear un ClassLoader nuevo para que dicha clase desaparezca de las clases cargadas aunque será muy raro que sólo eliminemos un .java y no añadamos/cambiemos otros, otro motico es porque si tenemos configurado el autosalvado de .class tenemos que eliminar en ese caso - - synchronized(monitor) - { - if (!firstTime) - { - if (!deletedSourceFiles.isEmpty()) // En firstTime no tiene sentido que haya eliminados - { - for(ClassDescriptorSourceUnit classDesc : deletedSourceFiles) - sourceRegistry.removeClassDescriptorSourceUnit(classDesc.getClassName()); - } - - if (!newSourceFiles.isEmpty()) // En firstTime ya están añadidos en sourceRegistry explícitamente al recorrer los sources - { - for(ClassDescriptorSourceUnit classDesc : newSourceFiles) - sourceRegistry.addClassDescriptorSourceUnit(classDesc); - } - } - - ArrayList sourceFilesToCompile = new ArrayList(updatedSourceFiles.size() + newSourceFiles.size()); - sourceFilesToCompile.addAll(updatedSourceFiles); - sourceFilesToCompile.addAll(newSourceFiles); - - updatedSourceFiles = null; // Ya no se necesita - newSourceFiles = null; // Ya no se necesita - - if (!sourceFilesToCompile.isEmpty()) - { - // Eliminamos el estado de la anterior compilación de todas las clases que van a recompilarse antes de compilarlas porque al compilar una clase es posible que - // se necesite recompilar al mismo tiempo una dependiente de otra (ej clase base) y luego se intente compilar la dependiente y sería un problema que limpiáramos antes de compilar cada archivo - for(ClassDescriptorSourceUnit sourceFile : sourceFilesToCompile) - cleanBeforeCompile(sourceFile); - - - JProxyCompilerContext context = compiler.createJProxyCompilerContext(); - JProxyCompilerListener compilerListener = getJProxyCompilerListener(); - try - { - - for(ClassDescriptorSourceUnit sourceFile : sourceFilesToCompile) - { - File file = null; - if (compilerListener != null) - { - SourceUnit srcUnit = sourceFile.getSourceUnit(); - if (srcUnit instanceof SourceFileJavaNormal) - file = ((SourceFileJavaNormal)srcUnit).getFileExt().getFile(); - else if (srcUnit instanceof SourceScriptRootFile) - file = ((SourceScriptRootFile)srcUnit).getFileExt().getFile(); - else if (srcUnit instanceof SourceScriptRootInMemory) // Caso de shell interactive y code snippet, en ese caso NO hay listener porque no hay forma de definirlo - file = null; - } - - if (compilerListener != null && file != null) - compilerListener.beforeCompile(file); - - compile(sourceFile,context); - - if (compilerListener != null && file != null) - compilerListener.afterCompile(file); - } - } - finally - { - context.close(); - } - - if (isSaveClassesMode()) - { - for(ClassDescriptorSourceUnit sourceFile : sourceFilesToCompile) - { - saveClasses(sourceFile); - } - } - } - - if (isSaveClassesMode() && !deletedSourceFiles.isEmpty()) - for(ClassDescriptorSourceUnit sourceFile : deletedSourceFiles) - deleteClasses(sourceFile); - - deletedSourceFiles = null; // Ya no se necesita - - boolean setPendingReload = true; - if (sourceFilesToCompile.size() == 1) - { - ClassDescriptorSourceUnit sourceFile = sourceFilesToCompile.get(0); - SourceUnit sourceUnit = sourceFile.getSourceUnit(); - if ((sourceUnit instanceof SourceScriptRootInMemory) && ((SourceScriptRootInMemory)sourceUnit).isEmptyCode()) - { - // Leer notas en SourceScriptRootInMemory.isEmptyCode() de esta manera evitamos crear un ClassLoader nuevo inútilmente por culpa de una clase - // root que no sirve para nada, ello impide que el registro/desregistro en colecciones funcione bien pues la instancia - // en el proxy que añade se ha recreado y es diferente por tanto a la instancia del proxy que elimina pues hace lo mismo por su parte - // aunque el ClassLoader sea el mismo. Si hemos cambiado el código del listener tiene sentido, pero inútilmente por una clase estúpida es tontería - setPendingReload = false; - } - } - - if (setPendingReload) - engine.setPendingReload(); - } - - return scriptFileDesc; - } - - private void saveClasses(ClassDescriptorSourceUnit sourceFile) - { - // Salvamos la clase principal - { - File classFilePath = ClassDescriptor.getAbsoluteClassFilePathFromClassNameAndClassPath(sourceFile.getClassName(),folderClasses); - JProxyUtil.saveFile(classFilePath,sourceFile.getClassBytes()); - } - - // Salvamos las innerclasses si hay, no hay problema de clases inner no detectadas pues lo están todas pues sólo se salva tras una compilación - LinkedList innerClassDescList = sourceFile.getInnerClassDescriptors(); - if (innerClassDescList != null && !innerClassDescList.isEmpty()) - { - for(ClassDescriptorInner innerClassDesc : innerClassDescList) - { - File classFilePath = ClassDescriptor.getAbsoluteClassFilePathFromClassNameAndClassPath(innerClassDesc.getClassName(),folderClasses); - JProxyUtil.saveFile(classFilePath,innerClassDesc.getClassBytes()); - } - } - } - - private void deleteClasses(ClassDescriptorSourceUnit sourceFile) - { - // Puede ocurrir que esta clase nunca se haya cargado y se ha modificado el código fuente y queramos limpiar los .class correspondientes pues se van a recrear - // como no conocemos qué inner clases están asociadas para saber que .class hay que eliminar, pues lo que hacemos es directamente obtener los .class que hay - // en el directorio con el fin de eliminar todos .class que tengan el patrón de ser inner classes del source file de acuerdo a su nombre - // así conseguimos por ejemplo también eliminar las local classes (inner clases con nombre declaradas dentro de un método) que no hay manera de conocer - // a través de la carga de la clase - - // Hay un caso en el que puede haber .class que ya no están en el código fuente y es cuando tocamos el código fuente ANTES de cargar y eliminamos algún .java, - // al cargar como no existe el archivo no lo relacionamos con los .class - // La solución sería en tiempo de carga forzar una carga de todas las clases y de ahí deducir todos los .class que deben existir (excepto las clases locales - // que no podríamos detectarlas), pero el que haya .class sobrantes antiguos no es gran problema. - - File classFilePath = ClassDescriptor.getAbsoluteClassFilePathFromClassNameAndClassPath(sourceFile.getClassName(),folderClasses); - File parentDir = JProxyUtil.getParentDir(classFilePath); - String[] fileNameList = parentDir.list(); // Es más ligero que listFiles() que crea File por cada resultado - if (fileNameList != null) // Si es null es que el directorio no está creado - { - for (String fileName : fileNameList) - { - int pos = fileName.lastIndexOf(".class"); - if (pos == -1) continue; - String simpleClassName = fileName.substring(0, pos); - if (sourceFile.getSimpleClassName().equals(simpleClassName) || - sourceFile.isInnerClass(sourceFile.getPackageName() + simpleClassName)) - { - new File(parentDir,fileName).delete(); - } - } - } - } -} +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr; + +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorInner; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.JProxyCompilerContext; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.JProxyCompilerInMemory; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootFile; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootInMemory; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceUnit; +import com.sillelien.jas.jproxy.JProxyCompilerListener; +import com.sillelien.jas.jproxy.JProxyDiagnosticsListener; +import com.sillelien.jas.jproxy.JProxyInputSourceFileExcludedListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Objects; + +/** + * @author jmarranz + */ +public class JProxyEngineChangeDetectorAndCompiler { + @NotNull + protected final JProxyEngine engine; + @NotNull + protected final JProxyCompilerInMemory compiler; + @Nullable + protected final FolderSourceList folderSourceList; + @Nullable + protected final FolderSourceList requiredExtraJarPaths; + @Nullable + protected final SourceScriptRoot scriptFile; // Puede ser nulo + @Nullable + protected final String folderClasses; // Puede ser nulo (es decir NO salvar como .class los cambios) + @Nullable + protected final JProxyInputSourceFileExcludedListener excludedListener; + @NotNull + protected final JavaSourcesSearch sourcesSearch; + + @Nullable + protected final JProxyCompilerListener compilerListener; + + @Nullable + protected volatile ClassDescriptorSourceFileRegistry sourceRegistry; + + public JProxyEngineChangeDetectorAndCompiler(@NotNull JProxyEngine engine, + @Nullable SourceScriptRoot scriptFile, + @Nullable FolderSourceList folderSourceList, + @Nullable FolderSourceList requiredExtraJarPaths, + @Nullable String folderClasses, + @Nullable JProxyInputSourceFileExcludedListener excludedListener, + @Nullable Iterable compilationOptions, + @Nullable JProxyDiagnosticsListener diagnosticsListener, + @Nullable JProxyCompilerListener compilerListener) { + this.engine = engine; + this.scriptFile = scriptFile; + this.folderSourceList = folderSourceList; + this.requiredExtraJarPaths = requiredExtraJarPaths; + this.folderClasses = folderClasses; + this.excludedListener = excludedListener; + compiler = new JProxyCompilerInMemory(this, compilationOptions, diagnosticsListener); + sourcesSearch = new JavaSourcesSearch(this); + this.compilerListener = compilerListener; + } + + @NotNull + public JProxyEngine getJProxyEngine() { + return engine; + } + + @Nullable + public FolderSourceList getFolderSourceList() { + return folderSourceList; + } + + @Nullable + public FolderSourceList getRequiredExtraJarPaths() { + return requiredExtraJarPaths; + } + + @Nullable + public JProxyInputSourceFileExcludedListener getJProxyInputSourceFileExcludedListener() { + return excludedListener; + } + + @Nullable + public ClassDescriptorSourceFileRegistry getClassDescriptorSourceFileRegistry() { + return sourceRegistry; + } + + @Nullable + public ClassDescriptor getClassDescriptor(@NotNull String className) { + ClassDescriptorSourceFileRegistry sourceRegistry = this.sourceRegistry; + assert sourceRegistry != null; + return sourceRegistry.getClassDescriptor(className); + } + + private boolean isSaveClassesMode() { + return (folderClasses != null); + } + + @Nullable + private JProxyCompilerListener getJProxyCompilerListener() { + return compilerListener; + } + + private void cleanBeforeCompile(@NotNull ClassDescriptorSourceUnit sourceFile) { + if (isSaveClassesMode()) + deleteClasses(sourceFile); // Antes de que nos las carguemos en memoria la clase principal y las inner tras recompilar + + sourceFile.cleanOnSourceCodeChanged(); // El código fuente nuevo puede haber cambiado totalmente las innerclasses antiguas (añadido, eliminado) y por supuesto el bytecode necesita olvidarse + } + + private void compile(@NotNull ClassDescriptorSourceUnit sourceFile, @NotNull JProxyCompilerContext context) { + if (sourceFile.getClassBytes() != null) + return; // Ya ha sido compilado seguramente por dependencia de un archivo compilado inmediatamente antes, recuerda que el atributo classBytes se pone a null antes de compilar los archivos cambiados/nuevos + + ClassLoader currentClassLoader = engine.getCurrentClassLoader(); + assert currentClassLoader != null; + compiler.compileSourceFile(sourceFile, context, currentClassLoader, sourceRegistry); + } + + @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") + @Nullable + public ClassDescriptorSourceScript detectChangesInSources() { + Object monitor = getJProxyEngine().getMonitor(); + + boolean firstTime; + + ClassDescriptorSourceFileRegistry sourceRegistry = this.sourceRegistry; + synchronized (monitor) { + if (sourceRegistry == null) /* Es null la primera vez*/ { + firstTime = true; + sourceRegistry= new ClassDescriptorSourceFileRegistry(); + this.sourceRegistry= sourceRegistry; + } else { + firstTime = false; + sourceRegistry.setAllClassDescriptorSourceFilesPendingToRemove(true); // A medida que los vamos encontrando ponemos a false, es mucho más rápido que recrear el registro si no ha cambiado nada (lo normal) + } + } + + LinkedList updatedSourceFiles = new LinkedList<>(); + LinkedList newSourceFiles = new LinkedList<>(); + + ClassDescriptorSourceScript scriptFileDesc = sourcesSearch.sourceFileSearch(firstTime, scriptFile, sourceRegistry, updatedSourceFiles, newSourceFiles); + + LinkedList deletedSourceFiles = new LinkedList<>(); + + if (!firstTime) { + synchronized (monitor) { + // Obtenemos los deletedSourceFiles detectados (si es firstTime no tiene sentido hacer esto no haría nada pero nos ahorramos synchronized y llamada) + sourceRegistry.getAllClassDescriptorSourceFilesPendingToRemove(deletedSourceFiles); + } + } + + if (updatedSourceFiles.isEmpty() && newSourceFiles.isEmpty() && deletedSourceFiles.isEmpty()) + return scriptFileDesc; + + // También el hecho de eliminar una clase debe implicar crear un ClassLoader nuevo para que dicha clase desaparezca de las clases cargadas aunque será muy raro que sólo eliminemos un .java y no añadamos/cambiemos otros, otro motico es porque si tenemos configurado el autosalvado de .class tenemos que eliminar en ese caso + + synchronized (monitor) { + if (!firstTime) { + if (!deletedSourceFiles.isEmpty()) // En firstTime no tiene sentido que haya eliminados + { + for (ClassDescriptorSourceUnit classDesc : deletedSourceFiles) + sourceRegistry.removeClassDescriptorSourceUnit(classDesc.getClassName()); + } + + if (!newSourceFiles.isEmpty()) // En firstTime ya están añadidos en sourceRegistry explícitamente al recorrer los sources + { + for (ClassDescriptorSourceUnit classDesc : newSourceFiles) + sourceRegistry.addClassDescriptorSourceUnit(classDesc); + } + } + + ArrayList sourceFilesToCompile = new ArrayList<>(updatedSourceFiles.size() + newSourceFiles.size()); + sourceFilesToCompile.addAll(updatedSourceFiles); + sourceFilesToCompile.addAll(newSourceFiles); + + updatedSourceFiles = null; // Ya no se necesita + newSourceFiles = null; // Ya no se necesita + + if (!sourceFilesToCompile.isEmpty()) { + // Eliminamos el estado de la anterior compilación de todas las clases que van a recompilarse antes de compilarlas porque al compilar una clase es posible que + // se necesite recompilar al mismo tiempo una dependiente de otra (ej clase base) y luego se intente compilar la dependiente y sería un problema que limpiáramos antes de compilar cada archivo + for (ClassDescriptorSourceUnit sourceFile : sourceFilesToCompile) + cleanBeforeCompile(sourceFile); + + + JProxyCompilerContext context = compiler.createJProxyCompilerContext(); + JProxyCompilerListener compilerListener = getJProxyCompilerListener(); + try { + + for (ClassDescriptorSourceUnit sourceFile : sourceFilesToCompile) { + File file = null; + if (compilerListener != null) { + SourceUnit srcUnit = sourceFile.getSourceUnit(); + if (srcUnit instanceof SourceFileJavaNormal) + file = ((SourceFileJavaNormal) srcUnit).getFileExt().getFile(); + else if (srcUnit instanceof SourceScriptRootFile) + file = ((SourceScriptRootFile) srcUnit).getFileExt().getFile(); + else if (srcUnit instanceof SourceScriptRootInMemory) // Caso de shell interactive y code snippet, en ese caso NO hay listener porque no hay forma de definirlo + file = null; + } + + if ((compilerListener != null) && (file != null)) + compilerListener.beforeCompile(file); + + compile(sourceFile, context); + + if ((compilerListener != null) && (file != null)) + compilerListener.afterCompile(file); + } + } finally { + context.close(); + } + + if (isSaveClassesMode()) { + for (ClassDescriptorSourceUnit sourceFile : sourceFilesToCompile) { + saveClasses(sourceFile); + } + } + } + + if (isSaveClassesMode() && !deletedSourceFiles.isEmpty()) + for (ClassDescriptorSourceUnit sourceFile : deletedSourceFiles) + deleteClasses(sourceFile); + + deletedSourceFiles = null; // Ya no se necesita + + boolean setPendingReload = true; + if (sourceFilesToCompile.size() == 1) { + ClassDescriptorSourceUnit sourceFile = sourceFilesToCompile.get(0); + assert sourceFile != null; + SourceUnit sourceUnit = sourceFile.getSourceUnit(); + if ((sourceUnit instanceof SourceScriptRootInMemory) && ((SourceScriptRootInMemory) sourceUnit).isEmptyCode()) { + // Leer notas en SourceScriptRootInMemory.isEmptyCode() de esta manera evitamos crear un ClassLoader nuevo inútilmente por culpa de una clase + // root que no sirve para nada, ello impide que el registro/desregistro en colecciones funcione bien pues la instancia + // en el proxy que añade se ha recreado y es diferente por tanto a la instancia del proxy que elimina pues hace lo mismo por su parte + // aunque el ClassLoader sea el mismo. Si hemos cambiado el código del listener tiene sentido, pero inútilmente por una clase estúpida es tontería + setPendingReload = false; + } + } + + if (setPendingReload) + engine.setPendingReload(); + } + + return scriptFileDesc; + } + + private void saveClasses(@NotNull ClassDescriptorSourceUnit sourceFile) { + // Salvamos la clase principal + { + assert folderClasses != null; + File classFilePath = ClassDescriptor.getAbsoluteClassFilePathFromClassNameAndClassPath(Objects.requireNonNull(sourceFile).getClassName(), folderClasses); + @Nullable byte[] classBytes = Objects.requireNonNull(sourceFile).getClassBytes(); + JProxyUtil.saveFile(classFilePath, Objects.requireNonNull(classBytes)); + } + + // Salvamos las innerclasses si hay, no hay problema de clases inner no detectadas pues lo están todas pues sólo se salva tras una compilación + LinkedList innerClassDescList = sourceFile.getInnerClassDescriptors(); + if ((innerClassDescList != null) && !innerClassDescList.isEmpty()) { + for (ClassDescriptorInner innerClassDesc : innerClassDescList) { + File classFilePath = ClassDescriptor.getAbsoluteClassFilePathFromClassNameAndClassPath(innerClassDesc.getClassName(), folderClasses); + JProxyUtil.saveFile(classFilePath, Objects.requireNonNull(innerClassDesc.getClassBytes())); + } + } + } + + private void deleteClasses(@NotNull ClassDescriptorSourceUnit sourceFile) { + // Puede ocurrir que esta clase nunca se haya cargado y se ha modificado el código fuente y queramos limpiar los .class correspondientes pues se van a recrear + // como no conocemos qué inner clases están asociadas para saber que .class hay que eliminar, pues lo que hacemos es directamente obtener los .class que hay + // en el directorio con el fin de eliminar todos .class que tengan el patrón de ser inner classes del source file de acuerdo a su nombre + // así conseguimos por ejemplo también eliminar las local classes (inner clases con nombre declaradas dentro de un método) que no hay manera de conocer + // a través de la carga de la clase + + // Hay un caso en el que puede haber .class que ya no están en el código fuente y es cuando tocamos el código fuente ANTES de cargar y eliminamos algún .java, + // al cargar como no existe el archivo no lo relacionamos con los .class + // La solución sería en tiempo de carga forzar una carga de todas las clases y de ahí deducir todos los .class que deben existir (excepto las clases locales + // que no podríamos detectarlas), pero el que haya .class sobrantes antiguos no es gran problema. + + String folderClasses = this.folderClasses; + assert folderClasses != null; + File classFilePath = ClassDescriptor.getAbsoluteClassFilePathFromClassNameAndClassPath(sourceFile.getClassName(), folderClasses); + File parentDir = JProxyUtil.getParentDir(classFilePath); + assert parentDir != null; + String[] fileNameList = parentDir.list(); // Es más ligero que listFiles() que crea File por cada resultado + if (fileNameList != null) // Si es null es que el directorio no está creado + { + for (String fileName : fileNameList) { + int pos = fileName.lastIndexOf(".class"); + if (pos == -1) continue; + String simpleClassName = fileName.substring(0, pos); + if (sourceFile.getSimpleClassName().equals(simpleClassName) || + sourceFile.isInnerClass(sourceFile.getPackageName() + simpleClassName)) { + new File(parentDir, fileName).delete(); + } + } + } + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JavaSourcesSearch.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JavaSourcesSearch.java new file mode 100644 index 0000000..4d2a06e --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/JavaSourcesSearch.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileJava; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootFileJavaExt; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceUnit; +import com.sillelien.jas.jproxy.JProxyInputSourceFileExcludedListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.net.URL; +import java.util.LinkedList; + +/** + * Additions hello@neilellis.me + * + * @author jmarranz + */ +public class JavaSourcesSearch { + @NotNull + protected final JProxyEngineChangeDetectorAndCompiler parent; + + public JavaSourcesSearch(@NotNull JProxyEngineChangeDetectorAndCompiler parent) { + if (parent == null) { + throw new IllegalArgumentException("JProxyEngineChangeDetectorAndCompiler parent was null"); + } + this.parent = parent; + } + + @NotNull + public JProxyEngineChangeDetectorAndCompiler getJProxyEngineChangeDetectorAndCompiler() { + return parent; + } + + @Nullable + public ClassDescriptorSourceScript sourceFileSearch(boolean firstTime, @Nullable SourceScriptRoot scriptFile, @NotNull ClassDescriptorSourceFileRegistry sourceRegistry, @NotNull LinkedList updatedSourceFiles, @NotNull LinkedList newSourceFiles) { + ClassDescriptorSourceScript scriptFileDesc = (scriptFile == null) ? null : processSourceFileScript(firstTime, scriptFile, sourceRegistry, updatedSourceFiles, newSourceFiles); + JProxyEngineChangeDetectorAndCompiler parent = this.parent; + FolderSourceList folderSourceList = parent.getFolderSourceList(); + if (folderSourceList != null) { + FileExt[] folderSourceArray = folderSourceList.getArray(); + // Es el caso de shell interactivo o code snippet + if (folderSourceArray == null) { + return scriptFileDesc; + } + + + boolean allEmpty = true; + + String scriptFileJavaCannonPath = ((scriptFile != null) && (scriptFile instanceof SourceScriptRootFileJavaExt)) ? ((SourceScriptRootFileJavaExt) scriptFile).getFileExt().getCanonicalPath() : null; + + for (int i = 0; i < folderSourceArray.length; i++) { + FileExt rootFolderOfSources = folderSourceArray[i]; + String[] children = rootFolderOfSources.getFile().list(); + if (children == null) { + continue; // El que ha configurado los rootFolders es tonto y ha puesto alguno nulo o no es válido el path + } + if (children.length == 0) { + continue; // Empty + } + + if (allEmpty) { + allEmpty = false; + } + recursiveSourceFileJavaSearch(firstTime, scriptFileJavaCannonPath, i, rootFolderOfSources, children, sourceRegistry, updatedSourceFiles, newSourceFiles); + } + + if (allEmpty) { + throw new RelProxyException("All specified input source folders are empty"); + } + + } + return scriptFileDesc; + } + + @NotNull + private ClassDescriptorSourceUnit processSourceFile(boolean firstTime, @NotNull SourceUnit file, boolean script, @NotNull ClassDescriptorSourceFileRegistry sourceRegistry, @NotNull LinkedList updatedSourceFiles, @NotNull LinkedList newSourceFiles) { + JProxyEngine engine = parent.getJProxyEngine(); + String className = file.getClassName(); + + long timestampSourceFile = file.lastModified(); + ClassDescriptorSourceUnit sourceFile; + if (!firstTime) { + Object monitor = engine.getMonitor(); + + synchronized (monitor) { + sourceFile = sourceRegistry.getClassDescriptorSourceUnit(className); + } + + if (sourceFile != null) { // Changed + long oldTimestamp = sourceFile.getTimestamp(); + + if (timestampSourceFile > oldTimestamp) { + synchronized (monitor) { + sourceFile.updateTimestamp(timestampSourceFile); + } + updatedSourceFiles.add(sourceFile); + } + + sourceFile.setPendingToRemove(false); // Found, is not deleted because it still exists + } else {// New class + sourceFile = ClassDescriptorSourceUnit.create(script, engine, className, file, timestampSourceFile); + sourceFile.setPendingToRemove(false); // It is already by default but just to be clear + newSourceFiles.add(sourceFile); + } + } else // The first time, we see if the source code has been changed with respect to the .class in the file system + { + String relClassPath = ClassDescriptor.getRelativeClassFilePathFromClassName(className); + ClassLoader parentClassLoader = engine.getRootClassLoader(); + assert parentClassLoader != null; + URL urlClass = parentClassLoader.getResource(relClassPath); + if (urlClass != null) { + String urlClassExt = urlClass.toExternalForm(); + // If the .class is in a JAR we could get the timestamp of the file inside the jar but that has a .java "out" reloadable indicates that we want to "replace" the jar so you will always be considered that the source file has been modified more recently + String path = urlClass.getPath(); + assert path != null; + long timestampCompiledClass = urlClassExt.startsWith("file:") ? new File(path).lastModified() : 0; // 0 cuando está en un JAR + + if (timestampSourceFile > timestampCompiledClass) { + sourceFile = ClassDescriptorSourceUnit.create(script, engine, className, file, timestampSourceFile); + updatedSourceFiles.add(sourceFile); // Hay que recompilar +//System.out.println("UPDATED: " + className + " " + urlClass.toExternalForm() + " " + (timestampSourceFile - timestampCompiledClass)); + } else { + // Esto es lo normal en carga si no hemos tocado el código tras el deploy, que el .class sea más reciente que el .java + sourceFile = ClassDescriptorSourceUnit.create(script, engine, className, file, timestampCompiledClass); + byte[] classBytes = JProxyUtil.readURL(urlClass); + sourceFile.setClassBytes(classBytes); + // Falta cargar las posibles inner classes, hay que tener en cuenta que este archivo NO se va a compilar porque no ha cambiado respecto a .class conocido +//System.out.println("NOT UPDATED: " + className + " " + urlClass.toExternalForm() + " " + (timestampSourceFile - timestampCompiledClass)); + } + + } else // No hay .class, es un archivo fuente nuevo creado antes de cargar la app web, hay que compilar si o si + { + sourceFile = ClassDescriptorSourceUnit.create(script, engine, className, file, timestampSourceFile); + newSourceFiles.add(sourceFile); + } + + Object monitor = engine.getMonitor(); + synchronized (monitor) { + sourceRegistry.addClassDescriptorSourceUnit(sourceFile); // El registro de archivos se hace por primera vez por lo que hay que añadirlos todos inicialmente, updatedSourceFiles y newSourceFiles indicarán en este caso los que hay que recompilar además + } + } + + return sourceFile; + } + + @NotNull + private ClassDescriptorSourceFileJava processSourceFileJava(boolean firstTime, @NotNull SourceFileJavaNormal file, @NotNull ClassDescriptorSourceFileRegistry sourceRegistry, @NotNull LinkedList updatedSourceFiles, @NotNull LinkedList newSourceFiles) { + return (ClassDescriptorSourceFileJava) processSourceFile(firstTime, file, false, sourceRegistry, updatedSourceFiles, newSourceFiles); + } + + @NotNull + private ClassDescriptorSourceScript processSourceFileScript(boolean firstTime, @NotNull SourceScriptRoot file, @NotNull ClassDescriptorSourceFileRegistry sourceRegistry, @NotNull LinkedList updatedSourceFiles, @NotNull LinkedList newSourceFiles) { + return (ClassDescriptorSourceScript) processSourceFile(firstTime, file, true, sourceRegistry, updatedSourceFiles, newSourceFiles); + } + + private void recursiveSourceFileJavaSearch(boolean firstTime, @Nullable String scriptFileJavaCannonPath, int rootFolderOfSourcesIndex, @NotNull FileExt parentPath, @NotNull String[] relPathList, @NotNull ClassDescriptorSourceFileRegistry sourceRegistry, @NotNull LinkedList updatedSourceFiles, @NotNull LinkedList newSourceFiles) { + FolderSourceList folderSourceList = parent.getFolderSourceList(); + if (folderSourceList == null) { + throw new RelProxyException("Could not search " + parentPath.getCanonicalPath() + " as parent.getFolderSourceList() was null"); + } + @Nullable FileExt[] array = folderSourceList.getArray(); + assert array != null; + FileExt rootFolderOfSources = array[rootFolderOfSourcesIndex]; + assert rootFolderOfSources != null; + JProxyInputSourceFileExcludedListener listener = parent.getJProxyInputSourceFileExcludedListener(); + + for (String relPath : relPathList) { + File file = new File(parentPath.getCanonicalPath() + "/" + relPath); + FileExt fileExt = new FileExt(file); + if (file.isDirectory()) { + if ((listener != null) && listener.isExcluded(file, rootFolderOfSources.getFile())) { + continue; + } + + @Nullable String[] children = file.list(); + if (children != null) { + recursiveSourceFileJavaSearch(firstTime, scriptFileJavaCannonPath, rootFolderOfSourcesIndex, fileExt, children, sourceRegistry, updatedSourceFiles, newSourceFiles); + } + } else { + String ext = JProxyUtil.getFileExtension(file); // Si no tiene extensión devuelve "" + if (!"java".equals(ext)) { + continue; + } + //if (!"jsh".equals(ext)) continue; + + String cannonPath = JProxyUtil.getCanonicalPath(file); + if ((scriptFileJavaCannonPath != null) && scriptFileJavaCannonPath.equals(cannonPath)) { + continue; // Es el propio archivo script inicial que es .java, así evitamos considerarlo dos veces + } + + if ((listener != null) && listener.isExcluded(file, rootFolderOfSources.getFile())) { + continue; + } + + SourceFileJavaNormal sourceFile = new SourceFileJavaNormal(fileExt, rootFolderOfSources); + processSourceFileJava(firstTime, sourceFile, sourceRegistry, updatedSourceFiles, newSourceFiles); + } + } + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptor.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptor.java new file mode 100644 index 0000000..fb162fa --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptor.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +/** + * @author jmarranz + */ +public abstract class ClassDescriptor { + @NotNull + protected final String className; // El nombre basado en puntos pero usando $ en el caso de innerclasses + @NotNull + protected final String simpleClassName; // className sin el package + @NotNull + protected final String packageName; // El package pero acabado en un "." o bien "" si no hay package, el motivo de acabar en un punto es simplemente para poder concatenar ciegamente el package y el simpleClassName + @Nullable + protected byte[] classBytes; + @Nullable + protected Class clasz; + + public ClassDescriptor(@NotNull String className) { + this.className = className; + int pos = className.lastIndexOf('.'); + simpleClassName = (pos != -1) ? className.substring(pos + 1) : className; + packageName = (pos != -1) ? className.substring(0, pos + 1) : ""; // SE INCLUYE EL . en el caso de existir package + } + + public abstract boolean isInnerClass(); + + @NotNull + public String getClassName() { + return className; + } + + @NotNull + public String getSimpleClassName() { + return simpleClassName; + } + + @NotNull + public String getPackageName() { + return packageName; + } + + @Nullable + public byte[] getClassBytes() { + return classBytes; + } + + public void setClassBytes(@Nullable byte[] classBytes) { + this.classBytes = classBytes; + } + + @Nullable + public Class getLastLoadedClass() { + return clasz; + } + + public void setLastLoadedClass(@Nullable Class clasz) { + this.clasz = clasz; + } + + public void resetLastLoadedClass() { + setLastLoadedClass(null); + } + + /* + public String getClassFileNameFromClassName() + { + return getClassFileNameFromClassName(className); + } + */ + + public static String getClassFileNameFromClassName(@NotNull String className) { + // Es válido también para las innerclasses (ej Nombre$Otro => Nombre$Otro.class, Nombre$1 => Nombre$1.class, Nombre$1Nombre => Nombre$1Nombre.class + int pos = className.lastIndexOf("."); + if (pos != -1) className = className.substring(pos + 1); + return className + ".class"; + } + + @NotNull + public static String getRelativeClassFilePathFromClassName(@NotNull String className) { + return className.replace('.', '/') + ".class"; // alternativa: className.replaceAll("\\.", "/") + ".class" + } + + @NotNull + public static String getRelativePackagePathFromClassName(@NotNull String className) { + String packageName = className.replace('.', '/'); + int pos = packageName.lastIndexOf('/'); + if (pos == -1) return packageName; + return packageName.substring(0, pos); + } + + @NotNull + public static File getAbsoluteClassFilePathFromClassNameAndClassPath(@NotNull String className, @NotNull String classPath) { + String relativePath = getRelativeClassFilePathFromClassName(className); + assert classPath != null; + classPath = classPath.trim(); + if (!classPath.endsWith("/") && !classPath.endsWith("\\")) classPath += File.separatorChar; + return new File(classPath + relativePath); + } + + @NotNull + public static String getClassNameFromRelativeClassFilePath(@NotNull String path) { + // Ej. org/w3c/dom/Element.class => org.w3c.dom.Element + String binaryName = path.replaceAll("/", "."); + assert binaryName != null; + return binaryName.replaceAll(".class$", ""); // El $ indica "el .class del final" + } + + @NotNull + public static String getClassNameFromPackageAndClassFileName(@NotNull String packageName, @NotNull String fileName) { + String className = packageName + "." + fileName; + return className.replaceAll(".class$", ""); // El $ indica "el .class del final" + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorInner.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorInner.java new file mode 100644 index 0000000..1d6e8cc --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorInner.java @@ -0,0 +1,26 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc; + +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class ClassDescriptorInner extends ClassDescriptor { + @NotNull + protected final ClassDescriptorSourceUnit parent; + + public ClassDescriptorInner(@NotNull String className, @NotNull ClassDescriptorSourceUnit parent) { + super(className); + this.parent = parent; + } + + @Override + public boolean isInnerClass() { + return true; + } + + @NotNull + public ClassDescriptorSourceUnit getClassDescriptorSourceUnit() { + return parent; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileJava.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileJava.java new file mode 100644 index 0000000..89637f9 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileJava.java @@ -0,0 +1,26 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc; + +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngine; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class ClassDescriptorSourceFileJava extends ClassDescriptorSourceUnit { + public ClassDescriptorSourceFileJava(@NotNull JProxyEngine engine, @NotNull String className, @NotNull SourceFileJavaNormal sourceFile, long timestamp) { + super(engine, className, sourceFile, timestamp); + } + + @NotNull + public SourceFileJavaNormal getSourceFileJavaNormal() { + return (SourceFileJavaNormal) sourceUnit; + } + + @NotNull + public FileExt getSourceFile() { + return getSourceFileJavaNormal().getFileExt(); + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileRegistry.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileRegistry.java new file mode 100644 index 0000000..538f4be --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceFileRegistry.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * @author jmarranz + */ +public class ClassDescriptorSourceFileRegistry { + @NotNull + protected final Map sourceUnitMapByClassName; + + public ClassDescriptorSourceFileRegistry() { + sourceUnitMapByClassName = new HashMap<>(); + } + + public ClassDescriptorSourceFileRegistry(@NotNull ClassDescriptorSourceFileRegistry origin) { + sourceUnitMapByClassName = new HashMap<>(origin.sourceUnitMapByClassName); + } + + public boolean isEmpty() { + return sourceUnitMapByClassName.isEmpty(); + } + + @NotNull + public Collection getClassDescriptorSourceFileColl() { + return sourceUnitMapByClassName.values(); + } + + @Nullable + public ClassDescriptorSourceUnit getClassDescriptorSourceUnit(@NotNull String className) { + return sourceUnitMapByClassName.get(className); + } + + @Nullable + public ClassDescriptorSourceUnit removeClassDescriptorSourceUnit(@NotNull String className) { + return sourceUnitMapByClassName.remove(className); + } + + public void addClassDescriptorSourceUnit(@NotNull ClassDescriptorSourceUnit sourceFile) { + sourceUnitMapByClassName.put(sourceFile.getClassName(), sourceFile); + } + + public void setAllClassDescriptorSourceFilesPendingToRemove(boolean pending) { + for (Map.Entry entries : sourceUnitMapByClassName.entrySet()) { + ClassDescriptorSourceUnit classDescriptorSourceUnit = entries.getValue(); + assert classDescriptorSourceUnit != null; + classDescriptorSourceUnit.setPendingToRemove(pending); + } + } + + @NotNull + public LinkedList getAllClassDescriptorSourceFilesPendingToRemove(@NotNull LinkedList deletedSourceFiles) { + for (Map.Entry entries : sourceUnitMapByClassName.entrySet()) { + ClassDescriptorSourceUnit classDesc = entries.getValue(); + assert classDesc != null; + boolean pending = classDesc.isPendingToRemove(); + if (pending) + deletedSourceFiles.add(classDesc); + } + return deletedSourceFiles; + } + + @Nullable + public ClassDescriptor getClassDescriptor(@NotNull String className) { + // Puede ser el de una innerclass + // Las innerclasses no están como tales en sourceFileMap pues sólo está la clase contenedora pero también la consideramos hotloadable + String parentClassName; + int pos = className.lastIndexOf('$'); + boolean inner; + if (pos != -1) { + parentClassName = className.substring(0, pos); + inner = true; + } else { + parentClassName = className; + inner = false; + } + ClassDescriptorSourceUnit sourceDesc = sourceUnitMapByClassName.get(parentClassName); + if (sourceDesc == null) + return null; + if (!inner) return sourceDesc; + return sourceDesc.getInnerClassDescriptor(className, true); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceScript.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceScript.java new file mode 100644 index 0000000..4c6987c --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceScript.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.jproxy.core.JProxyImpl; +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngine; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; + +/** + * @author jmarranz + */ +public class ClassDescriptorSourceScript extends ClassDescriptorSourceUnit { + @NotNull + protected String source; + + public ClassDescriptorSourceScript(@NotNull JProxyEngine engine, @NotNull String className, @NotNull SourceScriptRoot sourceFile, long timestamp) { + super(engine, className, sourceFile, timestamp); + + generateSourceCode(); + } + + + @NotNull + public SourceScriptRoot getSourceScript() { + return (SourceScriptRoot) sourceUnit; + } + + private void generateSourceCode() { + boolean[] hasHashBang = new boolean[1]; + + String scriptCode = getSourceScript().getScriptCode(getEncoding(), hasHashBang); + + boolean completeClass = isCompleteClass(scriptCode); + + StringBuilder finalCode = new StringBuilder(); + if (completeClass) { + if (hasHashBang[0]) + finalCode.append("\n"); // Como hemos quitado la línea #! añadimos una nueva para que los números de línea en caso de error coincidan con el original + finalCode.append(scriptCode); + } else { + JProxyImpl jproxy = engine.getJProxy(); + String mainParamsDec = null; + String mainReturnType = null; + + Class mainParamClass = jproxy.getMainParamClass(); + if (String[].class.equals(mainParamClass)) { + mainParamsDec = "String[] args"; + mainReturnType = "void"; + } else if (ScriptContext.class.equals(mainParamClass)) { + mainParamsDec = ScriptEngine.class.getName() + " engine," + ScriptContext.class.getName() + " context"; + mainReturnType = "Object"; + + if ("".equals(scriptCode)) scriptCode = "return null;"; + } + + List imports= jproxy.getImports(); + for (String anImport : imports) { + finalCode.append("import ").append(anImport).append(";\n"); + } + List staticImports= jproxy.getStaticImports(); + for (String anImport : staticImports) { + finalCode.append("import static ").append(anImport).append(";\n"); + } + + + finalCode.append("public class ").append(className).append(" { public static ").append(mainReturnType).append( + " main(").append(mainParamsDec).append( + ") {\n"); // Lo ponemos todo en una línea para que en caso de error la línea de error coincida con el script original pues hemos quitado la primera línea #! + finalCode.append(scriptCode); + finalCode.append(" }\n"); + finalCode.append("}\n"); + } + source = finalCode.toString(); + } + + private boolean isCompleteClass(@NotNull String code) { + // Buscamos si hay un " class ..." o un "import..." al comienzo para soportar la definición de una clase completa como script + int pos = code.indexOf("class"); + if (pos == -1) return false; + // Hay al menos un "class", ojo que puede ser parte de una variable o dentro de un comentario, pero si no existiera desde luego que no es clase completa + + pos = getFirstPosIgnoringCommentsAndSeparators(code); + if (pos == -1) return false; + + // Lo primero que nos tenemos encontrar es un import o una declaración de class + int pos2 = code.indexOf("import", pos); + if (pos2 == pos) + return true; // Si hay un import hay declaración de clase + + // Vemos si es un "public class..." o similar + int posClass = code.indexOf("class", pos); + String visibility = code.substring(pos, posClass); + visibility = visibility.trim(); // No consideramos \n hay que ser retorcido poner un \n entre el public y el class por ejemplo + if (visibility.isEmpty()) return true; // No hay visibilidad, que no compile no es cosa nuestra + return ("private".equals(visibility) || "public".equals(visibility) || "protected".equals(visibility)); + } + + private int getFirstPosIgnoringCommentsAndSeparators(@NotNull String code) { + int i = -1; + for (i = 0; i < code.length(); i++) { + char c = code.charAt(i); + if ((c == ' ') || (c == '\n') || (c == '\t')) continue; + else if ((c == '/') && ((i + 1) < code.length())) { + char c2 = code.charAt(i + 1); + if (c2 == '/') { + i = getFirstPosIgnoringOneLineComment(code, i); + if (i == -1) return -1; // Comentario mal formado + } else if (c2 == '*') { + i = getFirstPosIgnoringMultiLineComment(code, i); + if (i == -1) return -1; // Comentario mal formado + } + } else break; + } + return i; + } + + private int getFirstPosIgnoringOneLineComment(@NotNull String code, int start) { + return code.indexOf('\n', start); + } + + private int getFirstPosIgnoringMultiLineComment(@NotNull String code, int start) { + return code.indexOf("*/", start); + } + + @Override + public void updateTimestamp(long timestamp) { + long oldTimestamp = this.timestamp; + if (oldTimestamp != timestamp) { + generateSourceCode(); + } + super.updateTimestamp(timestamp); + } + + @NotNull + public String getSourceCode() { + return source; + } + + public void callMainMethod(@NotNull LinkedList argsToScript) throws Throwable { + try { + Class scriptClass = getLastLoadedClass(); + assert scriptClass != null; + Method method = scriptClass.getDeclaredMethod("main", String[].class); + String[] argsToScriptArr = !argsToScript.isEmpty() ? argsToScript.toArray( + new String[argsToScript.size()]) : new String[0]; + if (method != null) { + method.invoke(null, new Object[]{argsToScriptArr}); + } else { + throw new IllegalAccessException("No main method could be found"); + } + } catch (@NotNull IllegalAccessException | IllegalArgumentException | SecurityException ex) { + throw new RelProxyException(ex); + } catch (@NotNull InvocationTargetException ex) { + //noinspection ConstantConditions + throw ex.getCause(); + } // Los errores de ejecución se envuelven en un InvocationTargetException + } + + @Nullable + public Object callMainMethod(@NotNull ScriptEngine engine, @NotNull ScriptContext context) throws Throwable { + Class scriptClass = getLastLoadedClass(); + assert scriptClass != null; + return callMainMethod(scriptClass, engine, context); + } + + @Nullable + public static Object callMainMethod(@NotNull Class scriptClass, @NotNull ScriptEngine engine, @NotNull ScriptContext context) throws Throwable { + try { + Method method = scriptClass.getDeclaredMethod("main", ScriptEngine.class, ScriptContext.class); + if (method != null) { + return method.invoke(null, engine, context); + } else { + throw new IllegalAccessException("No main method could be found"); + } + } catch (@NotNull IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException ex) { + throw new RelProxyException(ex); + } catch (@NotNull InvocationTargetException ex) { + //noinspection ConstantConditions + throw ex.getCause(); + } // Los errores de ejecución se envuelven en un InvocationTargetException + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceUnit.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceUnit.java new file mode 100644 index 0000000..4c53c44 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/cldesc/ClassDescriptorSourceUnit.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc; + +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngine; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceFileJavaNormal; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceUnit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedList; + +/** + * @author jmarranz + */ +public abstract class ClassDescriptorSourceUnit extends ClassDescriptor { + @NotNull + protected final JProxyEngine engine; + protected volatile long timestamp; + + @NotNull + protected final SourceUnit sourceUnit; + + @NotNull + protected LinkedList innerClasses; + + protected boolean pendingToRemove; // Se usa como monohilo, no hay problemas de sincronización + + public ClassDescriptorSourceUnit(@NotNull JProxyEngine engine, @NotNull String className, @NotNull SourceUnit sourceFile, long timestamp) { + super(className); + this.engine = engine; + sourceUnit = sourceFile; + this.timestamp = timestamp; + } + + @NotNull + public static ClassDescriptorSourceUnit create(boolean script, @NotNull JProxyEngine engine, @NotNull String className, @NotNull SourceUnit sourceFile, long timestamp) { + if (sourceFile instanceof SourceScriptRoot) { + return new ClassDescriptorSourceScript(engine, className, (SourceScriptRoot) sourceFile, timestamp); + } else if (sourceFile instanceof SourceFileJavaNormal) { + return new ClassDescriptorSourceFileJava(engine, className, (SourceFileJavaNormal) sourceFile, timestamp); + } else { + throw new IllegalStateException("WTF!"); + } + } + + @NotNull + public SourceUnit getSourceUnit() { + return sourceUnit; + } + + @NotNull + public String getEncoding() { + return engine.getSourceEncoding(); + } + + @Override + public boolean isInnerClass() { + return false; + } + + public long getTimestamp() { + return timestamp; + } + + public void updateTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public boolean isPendingToRemove() { + return pendingToRemove; + } + + public void setPendingToRemove(boolean pendingToRemove) { + this.pendingToRemove = pendingToRemove; + } + + + public void cleanOnSourceCodeChanged() { + // Como ha cambiado la clase, reseteamos las dependencias + setClassBytes(null); + setLastLoadedClass(null); + clearInnerClassDescriptors(); // El código fuente nuevo puede haber cambiado totalmente las innerclasses antiguas (añadido, eliminado) + } + + public boolean isInnerClass(@NotNull String className) { + int pos = className.lastIndexOf('$'); + if (pos == -1) + return false; // No es innerclass + String baseClassName = className.substring(0, pos); + return this.className.equals(baseClassName); // Si es false es que es una innerclass pero de otra clase + } + + @Nullable + public LinkedList getInnerClassDescriptors() { + return innerClasses; + } + + public void clearInnerClassDescriptors() { + if (innerClasses != null) + innerClasses.clear(); + } + + @Nullable + public ClassDescriptorInner getInnerClassDescriptor(@NotNull String className, boolean addWhenMissing) { + if (innerClasses != null) { + for (ClassDescriptorInner classDesc : innerClasses) { + if (classDesc.getClassName().equals(className)) + return classDesc; + } + } + + if (!addWhenMissing) return null; + + return addInnerClassDescriptor(className); + } + + @Nullable + public ClassDescriptorInner addInnerClassDescriptor(@NotNull String className) { + if (!isInnerClass(className)) + return null; + + if (innerClasses == null) + innerClasses = new LinkedList<>(); + + ClassDescriptorInner classDesc = new ClassDescriptorInner(className, this); + innerClasses.add(classDesc); + return classDesc; + } + + @Override + public void resetLastLoadedClass() { + super.resetLastLoadedClass(); + + LinkedList innerClassDescList = getInnerClassDescriptors(); + if (innerClassDescList != null) { + for (ClassDescriptorInner innerClassDesc : innerClassDescList) + innerClassDesc.resetLastLoadedClass(); + } + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilationException.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilationException.java new file mode 100644 index 0000000..e741adc --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilationException.java @@ -0,0 +1,24 @@ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class JProxyCompilationException extends RelProxyException { + @NotNull + protected ClassDescriptorSourceUnit sourceUnit; + + public JProxyCompilationException(@NotNull ClassDescriptorSourceUnit sourceUnit) { + super("Compilation error"); + this.sourceUnit = sourceUnit; + } + + @NotNull + public ClassDescriptorSourceUnit getClassDescriptorSourceUnit() { + return sourceUnit; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilerContext.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilerContext.java new file mode 100644 index 0000000..533cb88 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilerContext.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.jproxy.JProxyDiagnosticsListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import java.io.IOException; +import java.util.List; + +/** + * @author jmarranz + */ +public class JProxyCompilerContext { + @NotNull + protected StandardJavaFileManager standardFileManager; + + @Nullable + protected DiagnosticCollector diagnostics; + + @Nullable + protected JProxyDiagnosticsListener diagnosticsListener; + + @NotNull + private static final Logger log = LoggerFactory.getLogger("JProxyCompilerContext"); + + public JProxyCompilerContext(@NotNull StandardJavaFileManager standardFileManager, + @NotNull DiagnosticCollector diagnostics, + @Nullable JProxyDiagnosticsListener diagnosticsListener) { + this.standardFileManager = standardFileManager; + this.diagnostics = diagnostics; + this.diagnosticsListener = diagnosticsListener; + } + + public @NotNull StandardJavaFileManager getStandardFileManager() { + return standardFileManager; + } + + @Nullable + public DiagnosticCollector getDiagnosticCollector() { + return diagnostics; + } + + public void close() { + try { + standardFileManager.close(); + } catch (IOException ex) { + throw new RelProxyException(ex); + } + + List> diagList = null; + if (diagnostics != null) { + diagList = diagnostics.getDiagnostics(); + if (!diagList.isEmpty()) { + if (diagnosticsListener != null) { + diagnosticsListener.onDiagnostics(diagnostics); + } else { + int i = 1; + for (Diagnostic diagnostic : diagList) { + log.debug("Diagnostic {}", i); + log.debug(" code: {}", diagnostic.getCode()); + log.debug(" kind: {}", diagnostic.getKind()); + log.debug(" line number: {}", diagnostic.getLineNumber()); + log.debug(" column number: {}", diagnostic.getColumnNumber()); + log.debug(" start position: {}", diagnostic.getStartPosition()); + log.debug(" position: {}", diagnostic.getPosition()); + log.debug(" end position: {}", diagnostic.getEndPosition()); + log.debug(" source: {}", diagnostic.getSource()); + log.debug(" message: {}", diagnostic.getMessage(null)); + i++; + } + } + } + } + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilerInMemory.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilerInMemory.java new file mode 100644 index 0000000..4ddf9d8 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JProxyCompilerInMemory.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngineChangeDetectorAndCompiler; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorInner; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileJava; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputSourceInMemory; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectOutputClass; +import com.sillelien.jas.jproxy.JProxyDiagnosticsListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * @author jmarranz + */ +public class JProxyCompilerInMemory { + @NotNull + private static final Logger log = LoggerFactory.getLogger(JProxyCompilerInMemory.class); + @NotNull + protected JProxyEngineChangeDetectorAndCompiler parent; + @NotNull + protected JavaCompiler compiler; + @Nullable + protected Iterable compilationOptions; + @Nullable + protected JProxyDiagnosticsListener diagnosticsListener; + + public JProxyCompilerInMemory(@NotNull JProxyEngineChangeDetectorAndCompiler engine, + @Nullable Iterable compilationOptions, + @Nullable JProxyDiagnosticsListener diagnosticsListener) { + parent = engine; + this.compilationOptions = compilationOptions; + this.diagnosticsListener = diagnosticsListener; + compiler = ToolProvider.getSystemJavaCompiler(); + } + + public void compileSourceFile(@NotNull ClassDescriptorSourceUnit sourceFileDesc, + @NotNull JProxyCompilerContext context, + @NotNull ClassLoader currentClassLoader, + @Nullable ClassDescriptorSourceFileRegistry sourceRegistry) { + //File sourceFile = sourceFileDesc.getSourceFile(); + LinkedList outClassList = compile(sourceFileDesc, context, currentClassLoader, sourceRegistry); + + if (outClassList == null) { + throw new JProxyCompilationException(sourceFileDesc); + } + + String className = sourceFileDesc.getClassName(); + + // Puede haber más de un resultado cuando hay inner classes y/o clase privada en el mismo archivo o bien simplemente clases dependientes + for (JavaFileObjectOutputClass outClass : outClassList) { + String currClassName = outClass.binaryName(); + byte[] classBytes = outClass.getBytes(); + assert classBytes != null; + + if (className.equals(currClassName)) { + sourceFileDesc.setClassBytes(classBytes); + } else { + ClassDescriptorInner innerClass = sourceFileDesc.getInnerClassDescriptor(currClassName, true); + if (innerClass != null) { + innerClass.setClassBytes(classBytes); + } else { + // Lo mismo es un archivo dependiente e incluso una inner class pero de otra clase que está siendo usada en el archivo compilado + assert sourceRegistry != null; + ClassDescriptor dependentClass = sourceRegistry.getClassDescriptor(currClassName); + if (dependentClass != null) { + dependentClass.setClassBytes(classBytes); + } else { + // Seguramente es debido a que el archivo java tiene una clase privada autónoma declarada en el mismo archivo .java (las que se ponen después de la clase principal pública normal), no permitimos estas clases porque sólo podemos + // detectarlas cuando cambiamos el código fuente, pero no si el código fuente no se ha tocado, por ejemplo no tenemos + // forma de conseguir que se recarguen de forma determinista y si posteriormente se cargara via ClassLoader al usarse no podemos reconocer que es una clase + // "hot reloadable" (quizás a través del package respecto a las demás clases hot pero no es muy determinista pues nada impide la mezcla de hot y no hot en el mismo package) + // Es una limitación mínima. + + // También puede ser un caso de clase excluida por el listener de exclusión, no debería ocurrir, tengo un caso de test en donde ocurre a posta + // (caso de JProxyExampleAuxIgnored cuando se cambia la JProxyExampleDocument que la usa) pero en programación normal no. + + if (parent.getJProxyInputSourceFileExcludedListener() == null) { + throw new RelProxyException("Unexpected class when compiling: " + currClassName + " maybe it is an autonomous private class declared in the same java file of the principal class, this kind of classes are not supported in hot reload"); + } else { + log.error( + "Unexpected class when compiling: " + currClassName + " maybe it is an excluded class or is an autonomous private class declared in the same java file of the principal class, this kind of classes are not supported in hot reload"); + } + } + } + } + } + } + + @NotNull + public JProxyCompilerContext createJProxyCompilerContext() { + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null); + assert standardFileManager != null; + return new JProxyCompilerContext(standardFileManager, diagnostics, diagnosticsListener); + } + + @Nullable + private LinkedList compile(@NotNull ClassDescriptorSourceUnit sourceFileDesc, + @NotNull JProxyCompilerContext context, + @NotNull ClassLoader currentClassLoader, + @Nullable ClassDescriptorSourceFileRegistry sourceRegistry) { + // http://stackoverflow.com/questions/12173294/compiling-fully-in-memory-with-javax-tools-javacompiler + // http://www.accordess.com/wpblog/an-overview-of-java-compilation-api-jsr-199/ + // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/com/sun/tools/javac/util/JavacFileManager.java?av=h#JavacFileManager + // http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/tools/StandardLocation.java + // http://books.brainysoftware.com/java6_sample/chapter2.pdf + // http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html + // http://www.javablogging.com/dynamic-in-memory-compilation/ Si no queremos generar archivos + // http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html + // http://stackoverflow.com/questions/264828/controlling-the-classpath-in-a-servlet?rq=1 + // http://stackoverflow.com/questions/1563909/how-to-set-classpath-when-i-use-javax-tools-javacompiler-compile-the-source + // http://stackoverflow.com/questions/10767048/javacompiler-with-custom-classloader-and-filemanager + + + StandardJavaFileManager standardFileManager = context.getStandardFileManager(); // recuerda que el StandardJavaFileManager puede reutilizarse entre varias compilaciones consecutivas mientras se cierre al final + + Iterable compilationUnits; + + if (sourceFileDesc instanceof ClassDescriptorSourceFileJava) { + List sourceFileList = new ArrayList<>(); + sourceFileList.add(((ClassDescriptorSourceFileJava) sourceFileDesc).getSourceFile().getFile()); + compilationUnits = standardFileManager.getJavaFileObjectsFromFiles(sourceFileList); + } else if (sourceFileDesc instanceof ClassDescriptorSourceScript) { + ClassDescriptorSourceScript sourceFileDescScript = (ClassDescriptorSourceScript) sourceFileDesc; + LinkedList compilationUnitsList = new LinkedList<>(); + String code = sourceFileDescScript.getSourceCode(); + compilationUnitsList.add(new JavaFileObjectInputSourceInMemory(sourceFileDescScript.getClassName(), code, + sourceFileDescScript.getEncoding(), + sourceFileDescScript.getTimestamp())); + compilationUnits = compilationUnitsList; + } else { + throw new RelProxyException("Internal error"); + } + + assert sourceRegistry != null; + JavaFileManagerInMemory fileManagerInMemory = new JavaFileManagerInMemory(standardFileManager, currentClassLoader, + sourceRegistry, + parent.getRequiredExtraJarPaths()); + + assert compilationUnits != null; + boolean success = compile(compilationUnits, fileManagerInMemory, context); + if (!success) { + return null; + } + + LinkedList classObj = fileManagerInMemory.getJavaFileObjectOutputClassList(); + return classObj; + } + + private boolean compile(@NotNull Iterable compilationUnits, + @NotNull JavaFileManager fileManager, + @NotNull JProxyCompilerContext context) { + /* + String systemClassPath = System.getProperty("java.class.path"); + */ + + LinkedList finalCompilationOptions = new LinkedList<>(); + if (compilationOptions != null) { + for (String option : compilationOptions) { + finalCompilationOptions.add(option); + } + } + + FolderSourceList folderSourceList1 = parent.getFolderSourceList(); + if (folderSourceList1 != null) { + FileExt[] folderSourceList = folderSourceList1.getArray(); + if (folderSourceList != null) { + finalCompilationOptions.add("-classpath"); + StringBuilder classPath = new StringBuilder(); + for (int i = 0; i < folderSourceList.length; i++) { + FileExt folderSources = folderSourceList[i]; + classPath.append(folderSources.getCanonicalPath()); + if (i < (folderSourceList.length - 1)) { + classPath.append(File.pathSeparatorChar); + } + } + finalCompilationOptions.add(classPath.toString()); + } + } + DiagnosticCollector diagnostics = context.getDiagnosticCollector(); + JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, finalCompilationOptions, null, + compilationUnits); + boolean success = task.call(); + + return success; + } + + +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JavaFileManagerInMemory.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JavaFileManagerInMemory.java similarity index 51% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JavaFileManagerInMemory.java rename to src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JavaFileManagerInMemory.java index 4ac011f..db210ea 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/JavaFileManagerInMemory.java +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JavaFileManagerInMemory.java @@ -1,129 +1,148 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JProxyJavaFileObjectInput; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInMemory; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFileSystem; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectOutputClass; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.FolderSourceList; -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; -import javax.tools.JavaFileObject; -import javax.tools.JavaFileObject.Kind; -import javax.tools.StandardJavaFileManager; -import javax.tools.StandardLocation; - - -/** - * - * http://www.javablogging.com/dynamic-in-memory-compilation/ - * http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html - * http://grepcode.com/file/repo1.maven.org/maven2/org.st-js/generator/3.0.3/org/stjs/generator/javac/CustomClassloaderJavaFileManager.java - * - * @author jmarranz - */ -public class JavaFileManagerInMemory extends ForwardingJavaFileManager -{ - private final LinkedList outputClassList = new LinkedList(); - private final JavaFileObjectInputClassFinderByClassLoader classFinder; - private final ClassDescriptorSourceFileRegistry sourceRegistry; - - public JavaFileManagerInMemory(StandardJavaFileManager standardFileManager,ClassLoader classLoader,ClassDescriptorSourceFileRegistry sourceRegistry,FolderSourceList requiredExtraJarPaths) - { - super(standardFileManager); - this.sourceRegistry = sourceRegistry; - this.classFinder = new JavaFileObjectInputClassFinderByClassLoader(classLoader,requiredExtraJarPaths); - } - - public LinkedList getJavaFileObjectOutputClassList() - { - return outputClassList; - } - - @Override - public JavaFileObject getJavaFileForOutput(Location location,String className, Kind kind, FileObject sibling) throws IOException - { - // Normalmente sólo habrá un resultado pero se da el caso de compilar una clase con una o varias inner classes, el compilador las compila de una vez - JavaFileObjectOutputClass outClass = new JavaFileObjectOutputClass(className, kind); - outputClassList.add(outClass); - return outClass; - } - - @Override - public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException - { - if (location == StandardLocation.PLATFORM_CLASS_PATH) // let standard manager hanfle - return super.list(location, packageName, kinds, recurse); // En este caso nunca (con PLATFORM_CLASS_PATH) va a encontrar nuestros sources ni .class - else if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) - { - if (packageName.equals("java") || packageName.startsWith("java.")) // a hack to let standard manager handle locations like "java.lang" or "java.util", clases sólo cargables por el system class loader. Estrictamente no es necesario pero derivamos la inmensa mayoría de las clases estándar al método por defecto, NO añadimos "javax." pues hay extensiones tal y como el estándar servlet que no forma parte del Java core - return super.list(location, packageName, kinds, recurse); - else - { - // El StandardJavaFileManager al que hacemos forward es "configurado" por el compilador al que está asociado cuando hay una tarea de compilación - // dicha configuración es por ejemplo el classpath tanto para encontrar .class como .java - // En nuestro caso no disponemos del classpath de los .class, disponemos del ClassLoader a través del cual podemos obtener "a mano" via resources los - // JavaFileObject de los .class que necesitamos. - // Ahora bien, no es el caso de los archivos fuente en donde sí tenemos un path claro el cual pasamos como classpath al compilador y por tanto un super.list(location, packageName, kinds, recurse) - // nos devolverá los .java (como JavaFileObject claro) si encuentra archivos correspondientes al package buscado. - - LinkedList result = new LinkedList(); - - Iterable inFileMgr = super.list(location, packageName, kinds, recurse); // Esperamos o archivos fuente o .class de clases no recargables - if (inFileMgr instanceof Collection) - { - result.addAll((Collection)inFileMgr); - } - else - { - for(Iterator it = inFileMgr.iterator(); it.hasNext(); ) - { - JavaFileObject file = (JavaFileObject)it.next(); - result.add(file); - } - } - - List classList = classFinder.find(packageName); - - // Reemplazamos los .class de classList que son los que están en archivo "deployados" que pueden ser más antiguos que los que están en memoria - for(JavaFileObjectInputClassInFileSystem fileObj : classList) - { - String className = fileObj.getBinaryName(); - ClassDescriptorSourceUnit sourceFileDesc = sourceRegistry.getClassDescriptorSourceUnit(className); - if (sourceFileDesc != null && sourceFileDesc.getClassBytes() != null) - { - JavaFileObjectInputClassInMemory fileInput = new JavaFileObjectInputClassInMemory(className,sourceFileDesc.getClassBytes(),sourceFileDesc.getTimestamp()); - result.add(fileInput); - } - else - { - result.add(fileObj); - } - } - - // Los JavaFileObject de archivos fuente pueden ser los mimas clases que los de .class, el compilador se encargará de comparar los timestamp y elegir el .class o el source - - return result; - } - } - return Collections.emptyList(); - } - - @Override - public String inferBinaryName(Location location, JavaFileObject file) - { - if (file instanceof JProxyJavaFileObjectInput) - return ((JProxyJavaFileObjectInput)file).getBinaryName(); - - return super.inferBinaryName(location, file); - } - -} \ No newline at end of file +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp; + +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceFileRegistry; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceUnit; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JProxyJavaFileObjectInput; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFileSystem; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInMemory; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectOutputClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + + +/** + * http://www.javablogging.com/dynamic-in-memory-compilation/ + * http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html + * http://grepcode.com/file/repo1.maven.org/maven2/org.st-js/generator/3.0.3/org/stjs/generator/javac/CustomClassloaderJavaFileManager.java + * + * @author jmarranz + */ +public class JavaFileManagerInMemory extends ForwardingJavaFileManager { + @NotNull + private final LinkedList outputClassList = new LinkedList<>(); + + @NotNull + private final JavaFileObjectInputClassFinderByClassLoader classFinder; + + @NotNull + private final ClassDescriptorSourceFileRegistry sourceRegistry; + + public JavaFileManagerInMemory(@NotNull StandardJavaFileManager standardFileManager, @NotNull ClassLoader classLoader, @NotNull ClassDescriptorSourceFileRegistry sourceRegistry, @Nullable FolderSourceList requiredExtraJarPaths) { + super(standardFileManager); + this.sourceRegistry = sourceRegistry; + classFinder = new JavaFileObjectInputClassFinderByClassLoader(classLoader, requiredExtraJarPaths); + } + + @NotNull + public LinkedList getJavaFileObjectOutputClassList() { + return outputClassList; + } + + @NotNull + @Override + public JavaFileObject getJavaFileForOutput(@NotNull Location location, + @NotNull String className, + @NotNull Kind kind, + @NotNull FileObject sibling) throws IOException { + // Normalmente sólo habrá un resultado pero se da el caso de compilar una clase con una o varias inner classes, el compilador las compila de una vez + JavaFileObjectOutputClass outClass = new JavaFileObjectOutputClass(className, kind); + outputClassList.add(outClass); + return outClass; + } + + @NotNull + @Override + public Iterable list(@NotNull Location location, + @NotNull String packageName, + @NotNull Set kinds, + boolean recurse) throws IOException { + if (location == StandardLocation.PLATFORM_CLASS_PATH) // let standard manager hanfle + return super.list(location, packageName, kinds, recurse); // En este caso nunca (con PLATFORM_CLASS_PATH) va a encontrar nuestros sources ni .class + else if ((location == StandardLocation.CLASS_PATH) && kinds.contains(Kind.CLASS)) { + if ("java".equals(packageName) || packageName.startsWith( + "java.")) // a hack to let standard manager handle locations like "java.lang" or "java.util", clases sólo cargables por el system class loader. Estrictamente no es necesario pero derivamos la inmensa mayoría de las clases estándar al método por defecto, NO añadimos "javax." pues hay extensiones tal y como el estándar servlet que no forma parte del Java core + return super.list(location, packageName, kinds, recurse); + else { + // El StandardJavaFileManager al que hacemos forward es "configurado" por el compilador al que está asociado cuando hay una tarea de compilación + // dicha configuración es por ejemplo el classpath tanto para encontrar .class como .java + // En nuestro caso no disponemos del classpath de los .class, disponemos del ClassLoader a través del cual podemos obtener "a mano" via resources los + // JavaFileObject de los .class que necesitamos. + // Ahora bien, no es el caso de los archivos fuente en donde sí tenemos un path claro el cual pasamos como classpath al compilador y por tanto un super.list(location, packageName, kinds, recurse) + // nos devolverá los .java (como JavaFileObject claro) si encuentra archivos correspondientes al package buscado. + + LinkedList result = new LinkedList<>(); + + Iterable inFileMgr = super.list(location, packageName, kinds, recurse); // Esperamos o archivos fuente o .class de clases no recargables + if (inFileMgr instanceof Collection) { + result.addAll((Collection) inFileMgr); + } else { + for (Iterator it = inFileMgr.iterator(); it.hasNext(); ) { + JavaFileObject file = (JavaFileObject) it.next(); + result.add(file); + } + } + + List classList = classFinder.find(packageName); + + // Reemplazamos los .class de classList que son los que están en archivo "deployados" que pueden ser más antiguos que los que están en memoria + for (JavaFileObjectInputClassInFileSystem fileObj : classList) { + String className = fileObj.getBinaryName(); + assert className != null; + ClassDescriptorSourceUnit sourceFileDesc = sourceRegistry.getClassDescriptorSourceUnit(className); + if ((sourceFileDesc != null) && (sourceFileDesc.getClassBytes() != null)) { + JavaFileObjectInputClassInMemory fileInput = new JavaFileObjectInputClassInMemory(className, sourceFileDesc.getClassBytes(), sourceFileDesc.getTimestamp()); + result.add(fileInput); + } else { + result.add(fileObj); + } + } + + // Los JavaFileObject de archivos fuente pueden ser los mimas clases que los de .class, el compilador se encargará de comparar los timestamp y elegir el .class o el source + + return result; + } + } + return Collections.emptyList(); + } + + @Override + @Nullable + public String inferBinaryName(@NotNull Location location, @NotNull JavaFileObject file) { + if (file instanceof JProxyJavaFileObjectInput) + return ((JProxyJavaFileObjectInput) file).getBinaryName(); + + return super.inferBinaryName(location, file); + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JavaFileObjectInputClassFinderByClassLoader.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JavaFileObjectInputClassFinderByClassLoader.java new file mode 100644 index 0000000..ef6d957 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/JavaFileObjectInputClassFinderByClassLoader.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFile; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFileSystem; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInJar; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * http://atamur.blogspot.com.es/2009/10/using-built-in-javacompiler-with-custom.html + * + * @author jmarranz + */ +public class JavaFileObjectInputClassFinderByClassLoader { + @NotNull + private static final String CLASS_FILE_EXTENSION = ".class"; + + @NotNull + private final ClassLoader classLoader; + @Nullable + private final FolderSourceList requiredExtraJarPaths; + + public JavaFileObjectInputClassFinderByClassLoader(@NotNull ClassLoader classLoader, @Nullable FolderSourceList requiredExtraJarPaths) { + this.classLoader = classLoader; + this.requiredExtraJarPaths = requiredExtraJarPaths; + } + + @NotNull + public List find(@NotNull String packageName) throws IOException { + // http://www.dzone.com/snippets/get-all-classes-within-package + // http://sourceforge.net/p/scannotation/code/HEAD/tree/scannotation/src/main/java/org/scannotation/ClasspathUrlFinder.java#l124 + + String packagePath = packageName.replaceAll("\\.", "/"); + + List result = new ArrayList<>(); + + Enumeration urlEnumeration = classLoader.getResources(packagePath); + if (urlEnumeration.hasMoreElements()) { + while (urlEnumeration.hasMoreElements()) { // one URL for each jar on the classpath that has the given package + URL packageFolderURL = urlEnumeration.nextElement(); + listUnder(packageName, packageFolderURL, result); + } + } else { + // Enumeration vacía, chungo, esto nos ha ocurrido con el jar lib/ext/portlet.jar del Tomcat 6.2 del bundle liferay-portal-6.2-ce-ga3 + // daba un error de de javax.portlet.PortletRquest not found. + // En teoría debería responder a la búsqueda classLoader.getResources("javax/portlet") devolviendo el jar, pero devuelve un Enumeration vacío + // quizás es porque el jar no tiene un "Name: javax/portlet" en el META-INF/MANIFEST.MF (otros jar no tienen Name y funcionan no tengo claro el criterio, + // el problema debe estar en una opción del MANIFEST.MF que confunde al compilador), añadiendo el "Name: javax/portlet" funciona + // pero en plan majo permitimos al usuario que nos indique los paths de los jars "conflictivos" obtenemos los .class hijos directos por fuerza bruta del package + // requerido en el parámetro de este método find() y le evitamos modificar un jar de infraestructura que queda muy feo e inmantenible respecto a una solución + // basada en configuración del usuario. + + if (requiredExtraJarPaths != null) { + FileExt[] jarFileList = requiredExtraJarPaths.getArray(); + if (jarFileList != null) { + for (FileExt jarFile : jarFileList) { + listUnderJarCustom(packagePath, jarFile, result); + } + } + } + } + + return result; + } + + + private void listUnder(@NotNull String packageName, @NotNull URL packageFolderURL, @NotNull Collection result) { + String pkgPath = packageFolderURL.toExternalForm(); //packageFolderURL.getFile(); El problema de getFile es que también está URL-encoded (un espacio es %20) lo cual no es compatible con File + + if (pkgPath.startsWith("file:")) { + listUnderDir(packageName, pkgPath, result); + } else { // browse a jar file + listUnderJar(packageFolderURL, result); + } // maybe there can be something else for more involved class loaders + } + + private void listUnderDir(@NotNull String packageName, @NotNull String pkgPath, @NotNull Collection result) { + pkgPath = pkgPath.substring("file:".length()); + + try { + pkgPath = URLDecoder.decode(pkgPath, "UTF-8"); + } // Detecté el problema con Vaadin en un path con "Documents%20and%20Settings" con %20 obviamente no es un path correcto, deben ser espacios + catch (UnsupportedEncodingException ex) { + throw new RelProxyException(ex); + } + + File directory = new File(pkgPath); + if (!directory.isDirectory()) + throw new RelProxyException("Internal Error:" + pkgPath); + + // browse local .class files - useful for local execution + + File[] childFiles = directory.listFiles(); + assert childFiles != null; + for (File childFile : childFiles) { + if (!childFile.isFile()) continue; + + // We only want the .class files. + String name = childFile.getName(); + if (name.endsWith(CLASS_FILE_EXTENSION)) { + String binaryName = ClassDescriptor.getClassNameFromPackageAndClassFileName(packageName, name); + result.add(new JavaFileObjectInputClassInFile(childFile, binaryName, childFile.toURI())); + } + } + } + + private void listUnderJar(@NotNull URL packageFolderURL, @NotNull Collection result) { + try { + String jarUri = packageFolderURL.toExternalForm().split("!")[0]; + + JarURLConnection jarConn = (JarURLConnection) packageFolderURL.openConnection(); + String rootEntryName = jarConn.getEntryName(); + assert rootEntryName != null; + int rootEnd = rootEntryName.length() + 1; + + JarFile jarFile = jarConn.getJarFile(); + assert jarFile != null; + Enumeration entryEnum = jarFile.entries(); + while (entryEnum.hasMoreElements()) { + JarEntry jarEntry = entryEnum.nextElement(); + String name = jarEntry.getName(); + // Empieza por packagePath y no hay más folders siguientes, terminando en un .class (una clase concreta) + assert name != null; + if (name.startsWith(rootEntryName) && (name.indexOf('/', rootEnd) == -1) && name.endsWith(CLASS_FILE_EXTENSION)) { + URI uri = URI.create(jarUri + "!/" + name); + String binaryName = ClassDescriptor.getClassNameFromRelativeClassFilePath(name); + result.add(new JavaFileObjectInputClassInJar(binaryName, uri, jarEntry.getTime())); + } + } + } catch (Exception e) { + throw new RelProxyException("Wasn't able to open " + packageFolderURL + " as a jar file", e); + } + } + + + private void listUnderJarCustom(@NotNull String packagePath, @NotNull FileExt jarFile, @NotNull Collection result) { + String normalizedPath = jarFile.getCanonicalPath(); + if (normalizedPath.contains("\\")) // Windows + { + // No estoy seguro de que sea necesario normalizar pero por si acaso + normalizedPath = normalizedPath.replace("\\", "/"); // "C:/folder" + normalizedPath = "/" + normalizedPath; // "/C:/folder" + } + + String urlPath = "file:" + normalizedPath; + + URL packageFolderURL; + try { + packageFolderURL = new URL(urlPath); + } catch (MalformedURLException ex) { + throw new RelProxyException(ex); + } + + String jarUri = "jar:" + packageFolderURL.toExternalForm(); + + int posEnd = packagePath.length() + 1; + + ZipInputStream zip = null; + + try { + InputStream in = packageFolderURL.openStream(); + assert in != null; + zip = new ZipInputStream(in); + ZipEntry zipEntry = zip.getNextEntry(); + while (zipEntry != null) { + String name = zipEntry.getName(); + // Empieza por packagePath y no hay más folders siguientes, terminando en un .class (una clase concreta) + assert name != null; + if (name.startsWith(packagePath) && (name.indexOf('/', posEnd) == -1) && name.endsWith(CLASS_FILE_EXTENSION)) { + URI uri = URI.create(jarUri + "!/" + name); + String binaryName = ClassDescriptor.getClassNameFromRelativeClassFilePath(name); + result.add(new JavaFileObjectInputClassInJar(binaryName, uri, zipEntry.getTime())); + } + + zipEntry = zip.getNextEntry(); + } + } catch (IOException ex) { + throw new RelProxyException(ex); + } finally { + if (zip != null) try { + zip.close(); + } catch (IOException ex) { + throw new RelProxyException(ex); + } + } + } + + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JProxyJavaFileObjectInput.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JProxyJavaFileObjectInput.java new file mode 100644 index 0000000..adb1667 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JProxyJavaFileObjectInput.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public interface JProxyJavaFileObjectInput { + @NotNull + String getBinaryName(); +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFile.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFile.java similarity index 57% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFile.java rename to src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFile.java index 5a4665e..0fb7a68 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFile.java +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFile.java @@ -1,42 +1,41 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo.JavaFileObjectInputClassInFileSystem; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; - -/** - * - * @author jmarranz - */ -public class JavaFileObjectInputClassInFile extends JavaFileObjectInputClassInFileSystem -{ - protected File file; - - public JavaFileObjectInputClassInFile(File file,String binaryName, URI uri) - { - super(binaryName,uri,uri.getPath()); - this.file = file; - } - - @Override - public InputStream openInputStream() throws IOException - { - // Podríamos hacer uri.toURL().openStream() pero si tenemos el File es para algo - return new BufferedInputStream(new FileInputStream(file),10 * 1024); - } - - @Override - public long getLastModified() - { - return file.lastModified(); - } - - @Override - public String toString() { - return "JavaFileObjectInputClassInFile{uri=" + uri + '}'; - } -} \ No newline at end of file +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +/** + * @author jmarranz + */ +public class JavaFileObjectInputClassInFile extends JavaFileObjectInputClassInFileSystem { + @NotNull + protected File file; + + public JavaFileObjectInputClassInFile(@NotNull File file, @NotNull String binaryName, @NotNull URI uri) { + super(binaryName, uri, uri.getPath()); + this.file = file; + } + + @NotNull + @Override + public InputStream openInputStream() throws IOException { + // Podríamos hacer uri.toURL().openStream() pero si tenemos el File es para algo + return new BufferedInputStream(new FileInputStream(file), 10 * 1024); + } + + @Override + public long getLastModified() { + return file.lastModified(); + } + + @NotNull + @Override + public String toString() { + return "JavaFileObjectInputClassInFile{uri=" + uri + '}'; + } +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFileSystem.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFileSystem.java similarity index 62% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFileSystem.java rename to src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFileSystem.java index a0a8dea..dd9f5d2 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFileSystem.java +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInFileSystem.java @@ -1,92 +1,120 @@ -package com.innowhere.relproxy.impl.jproxy.core.clsmgr.comp.jfo; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.net.URI; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.NestingKind; -import javax.tools.JavaFileObject; - -/** - * - * @author jmarranz - */ -public abstract class JavaFileObjectInputClassInFileSystem implements JavaFileObject,JProxyJavaFileObjectInput -{ - protected final String binaryName; - protected final URI uri; - protected final String name; - - public JavaFileObjectInputClassInFileSystem(String binaryName, URI uri,String name) - { - this.uri = uri; - this.binaryName = binaryName; - this.name = name; - } - - @Override - public URI toUri() { - return uri; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getBinaryName() { - return binaryName; - } - - @Override - public OutputStream openOutputStream() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Reader openReader(boolean ignoreEncodingErrors) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Writer openWriter() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean delete() { - throw new UnsupportedOperationException(); - } - - @Override - public Kind getKind() { - return Kind.CLASS; - } - - @Override // copied from SimpleJavaFileManager - public boolean isNameCompatible(String simpleName, Kind kind) { - String baseName = simpleName + kind.extension; - return kind.equals(getKind()) - && (baseName.equals(getName()) - || getName().endsWith("/" + baseName)); - } - - @Override - public NestingKind getNestingKind() { - throw new UnsupportedOperationException(); - } - - @Override - public Modifier getAccessLevel() { - throw new UnsupportedOperationException(); - } - -} +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import org.jetbrains.annotations.NotNull; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.JavaFileObject; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; + +/** + * @author jmarranz + */ +public abstract class JavaFileObjectInputClassInFileSystem implements JavaFileObject, JProxyJavaFileObjectInput { + @NotNull + protected final String binaryName; + @NotNull + protected final URI uri; + @NotNull + protected final String name; + + public JavaFileObjectInputClassInFileSystem(@NotNull String binaryName, @NotNull URI uri, @NotNull String name) { + this.uri = uri; + this.binaryName = binaryName; + this.name = name; + } + + @Override + @NotNull + public URI toUri() { + return uri; + } + + @Override + @NotNull + public String getName() { + return name; + } + + @Override + @NotNull + public String getBinaryName() { + return binaryName; + } + + @NotNull + @Override + public OutputStream openOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Writer openWriter() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean delete() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Kind getKind() { + return Kind.CLASS; + } + + @Override // copied from SimpleJavaFileManager + public boolean isNameCompatible(@NotNull String simpleName, @NotNull Kind kind) { + String baseName = simpleName + kind.extension; + return kind.equals(getKind()) + && (baseName.equals(getName()) + || getName().endsWith("/" + baseName)); + } + + @NotNull + @Override + public NestingKind getNestingKind() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Modifier getAccessLevel() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInJar.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInJar.java new file mode 100644 index 0000000..d256ce9 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInJar.java @@ -0,0 +1,38 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +/** + * @author jmarranz + */ +public class JavaFileObjectInputClassInJar extends JavaFileObjectInputClassInFileSystem { + protected long timestamp; + + public JavaFileObjectInputClassInJar(@NotNull String binaryName, @NotNull URI uri, long timestamp) { + super(binaryName, uri, uri.getSchemeSpecificPart()); + this.timestamp = timestamp; + } + + @Nullable + @Override + public InputStream openInputStream() throws IOException { + assert uri != null; + return uri.toURL().openStream(); // easy way to handle any URI! + } + + @Override + public long getLastModified() { + return timestamp; + } + + @NotNull + @Override + public String toString() { + return "JavaFileObjectInputClassInJar{uri=" + uri + '}'; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInMemory.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInMemory.java new file mode 100644 index 0000000..5cc3db5 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputClassInMemory.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import org.jetbrains.annotations.NotNull; + +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; + +/** + * http://www.javablogging.com/dynamic-in-memory-compilation/ + * + * @author jmarranz + */ +public class JavaFileObjectInputClassInMemory extends SimpleJavaFileObject implements JProxyJavaFileObjectInput { + @NotNull + protected String binaryName; + @NotNull + protected byte[] byteCode; + protected long timestamp; + + public JavaFileObjectInputClassInMemory(@NotNull String name, @NotNull byte[] byteCode, long timestamp) { + super(URI.create("string:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS); + + binaryName = name; + this.byteCode = byteCode; + this.timestamp = timestamp; + } + + @NotNull + public byte[] getBytes() { + return byteCode; + } + + @Override + public long getLastModified() { + return timestamp; + } + + @NotNull + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(getBytes()); + } + + @NotNull + @Override + public OutputStream openOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public String getBinaryName() { + return binaryName; + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceBase.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceBase.java new file mode 100644 index 0000000..2cf324d --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceBase.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import com.sillelien.jas.RelProxyException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; + +/** + * http://www.javablogging.com/dynamic-in-memory-compilation/ + * + * @author jmarranz + */ +public abstract class JavaFileObjectInputSourceBase extends SimpleJavaFileObject implements JProxyJavaFileObjectInput { + @NotNull + protected String binaryName; + @NotNull + protected String encoding; + + public JavaFileObjectInputSourceBase(@NotNull String name, @NotNull String encoding) { + super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); // La extensión .java es necesaria aunque sea falsa sino da error + + binaryName = name; + this.encoding = encoding; + } + + @NotNull + protected abstract String getSource(); + + + @Nullable + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return getSource(); + } + + @NotNull + public byte[] getBytes() { + try { + return getSource().getBytes(encoding); + } catch (UnsupportedEncodingException ex) { + throw new RelProxyException(ex); + } + } + + @NotNull + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(getBytes()); + } + + @NotNull + @Override + public OutputStream openOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + @NotNull + public String getBinaryName() { + return binaryName; + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInFile.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInFile.java new file mode 100644 index 0000000..ba4f78b --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInFile.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +/** + * http://www.javablogging.com/dynamic-in-memory-compilation/ + * + * @author jmarranz + */ +public class JavaFileObjectInputSourceInFile extends JavaFileObjectInputSourceBase { + @NotNull + protected File file; + @Nullable + protected String source; + + public JavaFileObjectInputSourceInFile(@NotNull String name, @NotNull File file, @NotNull String encoding) { + super(name, encoding); + this.file = file; + } + + @NotNull + @Override + protected String getSource() { + if (source != null) + return source; + source = JProxyUtil.readTextFile(file, encoding); + return source; + } + + @Override + public long getLastModified() { + return file.lastModified(); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInMemory.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInMemory.java new file mode 100644 index 0000000..f19d84d --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectInputSourceInMemory.java @@ -0,0 +1,31 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import org.jetbrains.annotations.NotNull; + +/** + * http://www.javablogging.com/dynamic-in-memory-compilation/ + * + * @author jmarranz + */ +public class JavaFileObjectInputSourceInMemory extends JavaFileObjectInputSourceBase { + @NotNull + protected String source; + protected long timestamp; + + public JavaFileObjectInputSourceInMemory(@NotNull String name, @NotNull String source, @NotNull String encoding, long timestamp) { + super(name, encoding); + this.source = source; + this.timestamp = timestamp; + } + + @NotNull + @Override + protected String getSource() { + return source; + } + + @Override + public long getLastModified() { + return timestamp; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectOutputClass.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectOutputClass.java new file mode 100644 index 0000000..27652eb --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/comp/jfo/JavaFileObjectOutputClass.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.comp.jfo; + +import com.sillelien.jas.RelProxyException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; + +/** + * http://www.javablogging.com/dynamic-in-memory-compilation/ + * + * @author jmarranz + */ +public class JavaFileObjectOutputClass extends SimpleJavaFileObject { + + /** + * Byte code created by the compiler will be stored in this + * ByteArrayOutputStream so that we can later get the + * byte array out of it + * and put it in the memory as an instance of our class. + */ + @NotNull + protected final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + @NotNull + protected String binaryName; + + /** + * Registers the compiled class object under URI + * containing the class full name + * + * @param name Full name of the compiled class + * @param kind Kind of the data. It will be CLASS in our case + */ + public JavaFileObjectOutputClass(@NotNull String name, @NotNull Kind kind) { + super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind); + + if (!Kind.CLASS.equals(kind)) throw new RelProxyException("Unexpected"); + binaryName = name; + } + + @NotNull + public String binaryName() { + return binaryName; + } + + @Nullable + public byte[] getBytes() { + return bos.toByteArray(); + } + + @NotNull + @Override + public OutputStream openOutputStream() throws IOException { + return bos; + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceFileJavaNormal.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceFileJavaNormal.java new file mode 100644 index 0000000..10df12a --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceFileJavaNormal.java @@ -0,0 +1,27 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import com.sillelien.jas.impl.FileExt; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class SourceFileJavaNormal extends SourceUnit { + @NotNull + protected final FileExt sourceFile; + + public SourceFileJavaNormal(@NotNull FileExt sourceFile, @NotNull FileExt rootFolderOfSources) { + super(buildClassNameFromFile(sourceFile, rootFolderOfSources)); + this.sourceFile = sourceFile; + } + + @Override + public long lastModified() { + return sourceFile.getFile().lastModified(); + } + + @NotNull + public FileExt getFileExt() { + return sourceFile; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRoot.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRoot.java new file mode 100644 index 0000000..a797e05 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRoot.java @@ -0,0 +1,16 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author jmarranz + */ +public abstract class SourceScriptRoot extends SourceUnit { + public SourceScriptRoot(@Nullable String className) { + super(className); + } + + @NotNull + public abstract String getScriptCode(@NotNull String encoding, @NotNull boolean[] hasHashBang); +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFile.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFile.java new file mode 100644 index 0000000..4d98f0d --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFile.java @@ -0,0 +1,37 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public abstract class SourceScriptRootFile extends SourceScriptRoot { + @NotNull protected FileExt sourceFile; + + public SourceScriptRootFile(@NotNull FileExt sourceFile, @NotNull FolderSourceList folderSourceList) { + super(buildClassNameFromFile(sourceFile, folderSourceList)); + this.sourceFile = sourceFile; + } + + public static SourceScriptRootFile createSourceScriptRootFile(@NotNull FileExt sourceFile, @NotNull FolderSourceList folderSourceList) { + String ext = JProxyUtil.getFileExtension(sourceFile.getFile()); // Si no tiene extensión devuelve "" + if ("java".equals(ext)) + return new SourceScriptRootFileJavaExt(sourceFile, folderSourceList); + else + return new SourceScriptRootFileOtherExt(sourceFile, folderSourceList); // Caso de archivo script inicial sin extensión .java (puede ser sin extensión) + } + + @Override + public long lastModified() { + return sourceFile.getFile().lastModified(); + } + + @NotNull + public FileExt getFileExt() { + return sourceFile; + } + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileJavaExt.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileJavaExt.java new file mode 100644 index 0000000..28950b0 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileJavaExt.java @@ -0,0 +1,22 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class SourceScriptRootFileJavaExt extends SourceScriptRootFile { + public SourceScriptRootFileJavaExt(@NotNull FileExt sourceFile, @NotNull FolderSourceList folderSourceList) { + super(sourceFile, folderSourceList); + } + + @NotNull + @Override + public String getScriptCode(@NotNull String encoding, @NotNull boolean[] hasHashBang) { + hasHashBang[0] = false; + return JProxyUtil.readTextFile(sourceFile.getFile(), encoding); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileOtherExt.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileOtherExt.java new file mode 100644 index 0000000..5329d7f --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootFileOtherExt.java @@ -0,0 +1,31 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import org.jetbrains.annotations.NotNull; + +/** + * @author jmarranz + */ +public class SourceScriptRootFileOtherExt extends SourceScriptRootFile { + public SourceScriptRootFileOtherExt(@NotNull FileExt sourceFile, @NotNull FolderSourceList folderSourceList) { + super(sourceFile, folderSourceList); + } + + @NotNull + @Override + public String getScriptCode(@NotNull String encoding, @NotNull boolean[] hasHashBang) { + String codeBody = JProxyUtil.readTextFile(sourceFile.getFile(), encoding); + // Eliminamos la primera línea #! (debe estar en la primera línea y sin espacios antes) + if (codeBody.startsWith("#!")) { + hasHashBang[0] = true; + int pos = codeBody.indexOf('\n'); + if (pos != -1) // Rarísimo que sólo esté el hash bang (script vacío) + { + codeBody = codeBody.substring(pos + 1); + } + } else hasHashBang[0] = false; + return codeBody; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootInMemory.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootInMemory.java new file mode 100644 index 0000000..abbb543 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceScriptRootInMemory.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author jmarranz + */ +public final class SourceScriptRootInMemory extends SourceScriptRoot { + @NotNull + public static final String DEFAULT_CLASS_NAME = "_jproxyMainClass_"; // OJO NO CAMBIAR, está ya documentada + + @NotNull + protected String code; + protected long timestamp; + + private SourceScriptRootInMemory(@NotNull String className, @NotNull String code) { + super(className); + setScriptCode(code, System.currentTimeMillis()); + } + + public static SourceScriptRootInMemory createSourceScriptInMemory(@NotNull String code) { + return new SourceScriptRootInMemory(DEFAULT_CLASS_NAME, code); + } + + @Override + public long lastModified() { + return timestamp; // Siempre ha sido modificado + } + + @NotNull + @Override + public String getScriptCode(String encoding, @NotNull boolean[] hasHashBang) { + hasHashBang[0] = false; + String code = this.code; + assert code != null; + return code; + } + + public boolean isEmptyCode() { + // Si code es "" la clase especial se genera pero no hace nada simplemente devuelve un null. + // Este es el caso en el que utilizamos RelProxy embebido en un framework utilizando la API ScriptEngine pero únicamente porque se usa una API basada + // en interfaces, pero tiene el inconveniente de generarse un SourceScriptRootInMemory inútil que no hace nada + if (code != null) { + return code.isEmpty(); + } else { + return true; + } + } + + @Nullable + public String getScriptCode() { + return code; + } + + public final void setScriptCode(@NotNull String code, long timestamp) { + this.code = code; + this.timestamp = timestamp; + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceUnit.java b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceUnit.java new file mode 100644 index 0000000..3a328f5 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/core/clsmgr/srcunit/SourceUnit.java @@ -0,0 +1,39 @@ +package com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit; + +import com.sillelien.jas.impl.FileExt; +import com.sillelien.jas.impl.jproxy.core.clsmgr.FolderSourceList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * @author jmarranz + */ +public abstract class SourceUnit { + @NotNull + protected final String className; + + public SourceUnit(@Nullable String className) { + //noinspection ConstantConditions + this.className = Objects.requireNonNull(className); + } + + public abstract long lastModified(); + + @Nullable + protected static String buildClassNameFromFile(@NotNull FileExt sourceFile, @NotNull FolderSourceList sourceList) { + return sourceList.buildClassNameFromFile(sourceFile); + } + + @Nullable + protected static String buildClassNameFromFile(@NotNull FileExt sourceFile, @NotNull FileExt rootFolderOfSources) { + return FolderSourceList.buildClassNameFromFile(sourceFile, rootFolderOfSources); + } + + @NotNull + public String getClassName() { + return className; + } + +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/BindingsImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/BindingsImpl.java similarity index 63% rename from relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/BindingsImpl.java rename to src/main/java/com/sillelien/jas/impl/jproxy/screngine/BindingsImpl.java index 26af733..8d9c797 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/impl/jproxy/screngine/BindingsImpl.java +++ b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/BindingsImpl.java @@ -1,14 +1,12 @@ - -package com.innowhere.relproxy.impl.jproxy.screngine; - -import java.util.HashMap; -import javax.script.Bindings; - -/** - * - * @author jmarranz - */ -public class BindingsImpl extends HashMap implements Bindings -{ - -} + +package com.sillelien.jas.impl.jproxy.screngine; + +import java.util.HashMap; +import javax.script.Bindings; + +/** + * @author jmarranz + */ +public class BindingsImpl extends HashMap implements Bindings { + +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineDelegateImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineDelegateImpl.java new file mode 100644 index 0000000..411ab2f --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineDelegateImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.screngine; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.jproxy.JProxyConfigImpl; +import com.sillelien.jas.impl.jproxy.core.JProxyImpl; +import com.sillelien.jas.impl.jproxy.core.clsmgr.JProxyEngine; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptorSourceScript; +import com.sillelien.jas.impl.jproxy.core.clsmgr.comp.JProxyCompilationException; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRoot; +import com.sillelien.jas.impl.jproxy.core.clsmgr.srcunit.SourceScriptRootInMemory; +import com.sillelien.jas.impl.jproxy.shell.JProxyShellClassLoader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.script.ScriptContext; +import javax.script.ScriptException; +import java.io.File; +import java.util.Objects; + +/** + * @author jmarranz + */ +public class JProxyScriptEngineDelegateImpl extends JProxyImpl { + @NotNull + protected JProxyScriptEngineImpl parent; + @Nullable + protected ClassDescriptorSourceScript classDescSourceScript; + protected long codeBufferModTimestamp; + protected long lastCodeCompiledTimestamp; + + public JProxyScriptEngineDelegateImpl(@NotNull JProxyScriptEngineImpl parent) { + super(); + this.parent = parent; + } + + @Nullable + @Override + public ClassDescriptorSourceScript init(@NotNull JProxyConfigImpl config) { + SourceScriptRoot sourceFileScript = SourceScriptRootInMemory.createSourceScriptInMemory(""); + + JProxyShellClassLoader classLoader = null; + String classFolder = config.getClassFolder(); + if (classFolder != null) { + ClassLoader defaultClassLoader = getDefaultClassLoader(); + classLoader = new JProxyShellClassLoader(defaultClassLoader, new File(classFolder)); + } + + classDescSourceScript = init(config, sourceFileScript, classLoader); + return classDescSourceScript; + } + + @NotNull + @Override + public Class getMainParamClass() { + return ScriptContext.class; + } + + @NotNull + private SourceScriptRootInMemory getSourceScriptInMemory() { + ClassDescriptorSourceScript classDescSourceScript = this.classDescSourceScript; + assert classDescSourceScript != null; + return (SourceScriptRootInMemory) classDescSourceScript.getSourceScript(); + } + + @Nullable + public Object execute(@NotNull String code, @NotNull ScriptContext context) throws ScriptException { + Class scriptClass; + JProxyEngine jproxyEngine = getJProxyEngine(); + assert jproxyEngine != null; + Object monitor = jproxyEngine.getMonitor(); + synchronized (monitor) { + ClassDescriptorSourceScript classDescSourceScript = this.classDescSourceScript; + if (!code.equals(getSourceScriptInMemory().getScriptCode())) { + codeBufferModTimestamp = System.currentTimeMillis(); + + getSourceScriptInMemory().setScriptCode(code, codeBufferModTimestamp); + // Recuerda que cada vez que se obtiene el timestamp se llama a System.currentTimeMillis(), es imposible que el usuario haga algo en menos de 1ms + + ClassDescriptorSourceScript classDescSourceScript2 = null; + try { + classDescSourceScript2 = jproxyEngine.detectChangesInSourcesAndReload(); + } catch (JProxyCompilationException ex) { + throw new ScriptException(ex); + } + + if (!Objects.equals(classDescSourceScript2, classDescSourceScript)) + throw new RelProxyException("Internal Error"); + + lastCodeCompiledTimestamp = System.currentTimeMillis(); + if (lastCodeCompiledTimestamp == codeBufferModTimestamp) // Demasiado rápido compilando + { + // Aseguramos que el siguiente código se ejecuta si o si con un codeBufferModTimestamp mayor que el timestamp de la compilación + try { + Thread.sleep(1); + } catch (InterruptedException ex) { + throw new RelProxyException(ex); + } + } + } + + assert classDescSourceScript != null; + scriptClass = classDescSourceScript.getLastLoadedClass(); + assert scriptClass != null; + } + + try { + JProxyScriptEngineImpl parent = this.parent; + assert parent != null; + return ClassDescriptorSourceScript.callMainMethod(scriptClass, parent, context); + } catch (Throwable ex) { + Exception ex2 = (ex instanceof Exception) ? (Exception) ex : new RelProxyException(ex); + throw new ScriptException(ex2); + } + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineFactoryImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineFactoryImpl.java new file mode 100644 index 0000000..adc71a2 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineFactoryImpl.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.screngine; + +import com.sillelien.jas.RelProxy; +import com.sillelien.jas.jproxy.JProxyScriptEngineFactory; +import org.jetbrains.annotations.NotNull; + +import javax.script.ScriptEngine; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Ideas: http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy/1.6.0/org/codehaus/groovy/jsr223/GroovyScriptEngineFactory.java + * + * @author jmarranz + */ +public class JProxyScriptEngineFactoryImpl extends JProxyScriptEngineFactory { + @NotNull + protected static final String LANGUAGE_NAME = "Java"; + @NotNull + protected static final String SHORT_NAME = "java"; + @NotNull + protected static final List extensions; + @NotNull + protected static final List mimeTypes; + @NotNull + protected static final List names; + + static { + ArrayList n; + + n = new ArrayList<>(2); + n.add(SHORT_NAME); + n.add(LANGUAGE_NAME); + names = Collections.unmodifiableList(n); + + n = new ArrayList<>(1); + n.add("java"); + extensions = Collections.unmodifiableList(n); + + n = new ArrayList<>(2); + http: +//reference.sitepoint.com/html/mime-types-full + n.add("text/x-java-source"); + n.add("text/plain"); + mimeTypes = Collections.unmodifiableList(n); + } + + public JProxyScriptEngineFactoryImpl() { + super(); + } + + public static JProxyScriptEngineFactory create() { + return new JProxyScriptEngineFactoryImpl(); + } + + @NotNull + @Override + public String getEngineName() { + return "RelProxy Java Script Engine"; + } + + @NotNull + @Override + public String getEngineVersion() { + return RelProxy.getVersion(); + } + + @NotNull + @Override + public List getExtensions() { + return extensions; + } + + @NotNull + @Override + public List getMimeTypes() { + return mimeTypes; + } + + @NotNull + @Override + public List getNames() { + return names; + } + + @NotNull + @Override + public String getLanguageName() { + return LANGUAGE_NAME; + } + + @NotNull + @Override + public String getLanguageVersion() { + return System.getProperty("java.version"); // Ej 1.6.0_18 + } + + @NotNull + @Override + public Object getParameter(@NotNull String key) { + switch (key) { + case ScriptEngine.NAME: + return SHORT_NAME; + case ScriptEngine.ENGINE: + return getEngineName(); + case ScriptEngine.ENGINE_VERSION: + return getEngineVersion(); + case ScriptEngine.LANGUAGE: + return getLanguageName(); + case ScriptEngine.LANGUAGE_VERSION: + return getLanguageVersion(); + case "THREADING": + return "MULTITHREADED"; + default: + throw new IllegalArgumentException("Invalid key"); + } + } + + @NotNull + @Override + public String getMethodCallSyntax(@NotNull String obj, @NotNull String method, @NotNull String... args) { + StringBuilder ret = new StringBuilder(); + ret.append(obj).append(".").append(method).append("("); + int len = args.length; + if (len == 0) { + ret.append(")"); + return ret.toString(); + } + + for (int i = 0; i < len; i++) { + ret.append(args[i]); + if (i != (len - 1)) { + ret.append(","); + } else { + ret.append(")"); + } + } + return ret.toString(); + } + + @NotNull + @Override + public String getOutputStatement(@NotNull String toDisplay) { + StringBuilder buf = new StringBuilder(); + buf.append("System.out.println(\""); + int len = toDisplay.length(); + for (int i = 0; i < len; i++) { + char ch = toDisplay.charAt(i); + switch (ch) { + case '"': + buf.append("\\\""); + break; + case '\\': + buf.append("\\\\"); + break; + default: + buf.append(ch); + break; + } + } + buf.append("\")"); + return buf.toString(); + } + + @NotNull + @Override + public String getProgram(@NotNull String... statements) { + StringBuilder ret = new StringBuilder(); + int len = statements.length; + for (int i = 0; i < len; i++) { + ret.append(statements[i]); + ret.append('\n'); + } + return ret.toString(); + } + + @NotNull + @Override + public ScriptEngine getScriptEngine() { + return new JProxyScriptEngineImpl(this); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineImpl.java b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineImpl.java new file mode 100644 index 0000000..5f53631 --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/screngine/JProxyScriptEngineImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.screngine; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.GenericProxyImpl; +import com.sillelien.jas.impl.jproxy.JProxyConfigImpl; +import com.sillelien.jas.impl.jproxy.JProxyUtil; +import com.sillelien.jas.jproxy.JProxyConfig; +import com.sillelien.jas.jproxy.JProxyScriptEngine; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.script.AbstractScriptEngine; +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; +import java.io.Reader; + +/** + * Methods of this class are similar to JProxyDefaultImpl + * + * @author jmarranz + */ +public class JProxyScriptEngineImpl extends AbstractScriptEngine implements JProxyScriptEngine { + @NotNull + protected JProxyScriptEngineDelegateImpl jproxy; + @NotNull + protected JProxyScriptEngineFactoryImpl factory; + + public JProxyScriptEngineImpl(@NotNull JProxyScriptEngineFactoryImpl factory) { + super(); + this.factory = factory; + } + + @Override + public void init(@NotNull JProxyConfig config) { + JProxyConfigImpl configImpl = (JProxyConfigImpl) config; + assert configImpl != null; + if (!configImpl.isEnabled()) return; // jproxy quedará null + + GenericProxyImpl.checkSingletonNull(jproxy); + jproxy = new JProxyScriptEngineDelegateImpl(this); + jproxy.init(configImpl); + } + + + @Nullable + @Override + public Object eval(@NotNull String script, @NotNull ScriptContext context) throws ScriptException { + if (jproxy == null) + throw new RelProxyException("Engine is disabled"); + + return jproxy.execute(script, context); + } + + @Nullable + @Override + public Object eval(@NotNull Reader reader, @NotNull ScriptContext context) throws ScriptException { + String script = JProxyUtil.readTextFile(reader); + return eval(script, context); + } + + @NotNull + @Override + public Bindings createBindings() { + return new BindingsImpl(); + } + + @Nullable + @Override + public ScriptEngineFactory getFactory() { + return factory; + } + + @Nullable + @Override + public T create(@NotNull T obj, @NotNull Class clasz) { + if (jproxy == null) { + return obj; // No se ha llamado al init o enabled = false + } + return jproxy.create(obj, clasz); + } + + @Nullable + @Override + public Object create(@NotNull Object obj, @NotNull Class[] classes) { + if (jproxy == null) + return obj; // No se ha llamado al init o enabled = false + return jproxy.create(obj, classes); + } + + @Override + public boolean isEnabled() { + if (jproxy == null) + return false; + + return jproxy.isEnabled(); + } + + @Override + public boolean isRunning() { + if (jproxy == null) + return false; + + return jproxy.isRunning(); + } + + @Override + public boolean start() { + if (jproxy == null) + return false; + + return jproxy.start(); + } + + @Override + public boolean stop() { + if (jproxy == null) + return false; + + return jproxy.stop(); + } +} diff --git a/src/main/java/com/sillelien/jas/impl/jproxy/shell/JProxyShellClassLoader.java b/src/main/java/com/sillelien/jas/impl/jproxy/shell/JProxyShellClassLoader.java new file mode 100644 index 0000000..5ec67de --- /dev/null +++ b/src/main/java/com/sillelien/jas/impl/jproxy/shell/JProxyShellClassLoader.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.impl.jproxy.shell; + +import com.sillelien.jas.RelProxyException; +import com.sillelien.jas.impl.jproxy.core.clsmgr.cldesc.ClassDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * @author jmarranz + */ +public class JProxyShellClassLoader extends URLClassLoader { + public JProxyShellClassLoader(@Nullable ClassLoader parent, @NotNull File classFolder) { + super(toURLArray(classFolder), parent); + } + + private static URL[] toURLArray(@NotNull File file) { + try { + return new URL[]{file.toURI().toURL()}; + } catch (MalformedURLException ex) { + throw new RelProxyException(ex); + } + } + + @Nullable + @Override + protected Class findClass(@NotNull String name) throws ClassNotFoundException { + return super.findClass(name); + } + + @Nullable + @Override + protected synchronized Class loadClass(@NotNull String name, boolean resolve) throws ClassNotFoundException { + return super.loadClass(name, resolve); + } + + @NotNull + public synchronized Class defineClass(@NotNull ClassDescriptor classDesc) { + String className = classDesc.getClassName(); + byte[] classBytes = classDesc.getClassBytes(); + assert classBytes != null; + Class clasz = defineClass(className, classBytes, 0, classBytes.length); + classDesc.setLastLoadedClass(clasz); + return clasz; + } +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxy.java b/src/main/java/com/sillelien/jas/jproxy/JProxy.java similarity index 64% rename from relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxy.java rename to src/main/java/com/sillelien/jas/jproxy/JProxy.java index e6b77f3..a51380a 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxy.java +++ b/src/main/java/com/sillelien/jas/jproxy/JProxy.java @@ -1,118 +1,121 @@ - -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.impl.jproxy.JProxyConfigImpl; -import com.innowhere.relproxy.impl.jproxy.JProxyDefaultImpl; - -/** - * Is the class to create Java proxy objects based on Java objects and keep track of source code changes reloading classes when detected. - * - * @author Jose Maria Arranz Santamaria - */ -public class JProxy -{ - /** - * Creates a {@link JProxyConfig} object to be used to configure JProxy and {@link JProxyScriptEngineFactory}. - * - * @return a new configuration object. - * @see #init(JProxyConfig) - */ - public static JProxyConfig createJProxyConfig() - { - return JProxyDefaultImpl.createJProxyConfig(); - } - - /** - * Initializes JProxy with the provided configuration object. - * - * @param config - */ - public static void init(JProxyConfig config) - { - JProxyDefaultImpl.initStatic((JProxyConfigImpl)config); - } - - - - /** - * Creates a proxy object using java.lang.reflect.Proxy based on the provided Java object and the class of the implemented Java interface. - * - *

This method is a simplification for a single interface (the most common case) of {@link #create(Object,Class[])} .

- * - * @param the interface implemented by the original object and proxy object returned. - * @param obj the original object to proxy. - * @param clasz the class of the interface implemented by the original object and proxy object returned. - * @return the java.lang.reflect.Proxy object associated or the original object when GProxy is disabled. - */ - public static T create(T obj,Class clasz) - { - return JProxyDefaultImpl.createStatic(obj, clasz); - } - - /** - * Creates a proxy object using java.lang.reflect.Proxy based on the provided Java object and the classes of the implemented Java interfaces. - * - *

If JProxy has been configured and is enabled this method returns a java.lang.reflect.Proxy object implementing instead of - * the original object provided. Methods called in proxy object are received by JProxy and forwarded to the original object, if source code - * managed by JProxy has been changed, the class of the original object is reloaded based on the new source and the original object - * is recreated with the new class and fields are re-set in the new object, then the method is called on the new original object.

- * - *

If JProxy is disabled returns the original object provided with no performance penalty.

- * - * @param obj the original object to proxy. - * @param classes the classes of the interfaces implemented by the original object and proxy object returned. - * @return the java.lang.reflect.Proxy object associated or the original object when JProxy is disabled. - */ - public static Object create(Object obj,Class[] classes) - { - return JProxyDefaultImpl.createStatic(obj, classes); - } - - /** - * Informs whether JProxy is configured and enabled. - * - * @return true if enabled. - */ - public static boolean isEnabled() - { - return JProxyDefaultImpl.isEnabledStatic(); - } - - /** - * Informs whether JProxy is enabled and started (timed checking for changes). - * - * @return true if running. - */ - public static boolean isRunning() - { - return JProxyDefaultImpl.isRunningStatic(); - } - - /** - * Stops source code periodic change detection. - * - *

Periodicity of change detection is defined by {@link JProxyConfig#setScanPeriod(long)}

- * - * @return true if source change detection has been stopped, false if it is already stopped or JProxy is not enabled or initialized. - * @see #stop() - */ - public static boolean stop() - { - return JProxyDefaultImpl.stopStatic(); - } - - /** - * Starts source code periodic change detection. - * - *

Periodicity of change detection is defined by {@link JProxyConfig#setScanPeriod(long)}.

- * - *

By default when JProxy is initialized and enabled.

- * - * @return true if source change detection has been started again, false if it is already started or cannot start because JProxy is not enabled or initialized or scan period is not positive. - * @see #start() - */ - public static boolean start() - { - return JProxyDefaultImpl.startStatic(); - } -} + +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import com.sillelien.jas.impl.jproxy.JProxyConfigImpl; +import com.sillelien.jas.impl.jproxy.JProxyDefaultImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Is the class to create Java proxy objects based on Java objects and keep track of source code changes reloading classes when detected. + * + * @author Jose Maria Arranz Santamaria + */ +public final class JProxy { + /** + * Creates a proxy object using java.lang.reflect.Proxy based on the provided Java object and the class of the implemented Java interface. + *

This method is a simplification for a single interface (the most common case) of {@link #create(Object, Class[])} .

+ * + * @param the interface implemented by the original object and proxy object returned. + * @param obj the original object to proxy. + * @param clasz the class of the interface implemented by the original object and proxy object returned. + * @return the java.lang.reflect.Proxy object associated or the original object when GProxy is disabled. + */ + @Nullable + public static T create(@NotNull T obj, @NotNull Class clasz) { + return JProxyDefaultImpl.createStatic(obj, clasz); + } + + /** + * Creates a proxy object using java.lang.reflect.Proxy based on the provided Java object and the classes of the implemented Java interfaces. + *

If JProxy has been configured and is enabled this method returns a java.lang.reflect.Proxy object implementing instead of + * the original object provided. Methods called in proxy object are received by JProxy and forwarded to the original object, if source code + * managed by JProxy has been changed, the class of the original object is reloaded based on the new source and the original object + * is recreated with the new class and fields are re-set in the new object, then the method is called on the new original object.

+ *

If JProxy is disabled returns the original object provided with no performance penalty.

+ * + * @param obj the original object to proxy. + * @param classes the classes of the interfaces implemented by the original object and proxy object returned. + * @return the java.lang.reflect.Proxy object associated or the original object when JProxy is disabled. + */ + @Nullable + public static Object create(@NotNull Object obj, @NotNull Class[] classes) { + return JProxyDefaultImpl.createStatic(obj, classes); + } + + /** + * Creates a {@link JProxyConfig} object to be used to configure JProxy and {@link JProxyScriptEngineFactory}. + * + * @return a new configuration object. + * @see #init(JProxyConfig) + */ + public static JProxyConfig createJProxyConfig() { + return JProxyDefaultImpl.createJProxyConfig(); + } + + /** + * Initializes JProxy with the provided configuration object. + * + * @param config the configuration + */ + public static void init(@NotNull JProxyConfig config) { + JProxyDefaultImpl.initStatic((JProxyConfigImpl) config); + } + + /** + * Informs whether JProxy is configured and enabled. + * + * @return true if enabled. + */ + public static boolean isEnabled() { + return JProxyDefaultImpl.isEnabledStatic(); + } + + /** + * Informs whether JProxy is enabled and started (timed checking for changes). + * + * @return true if running. + */ + public static boolean isRunning() { + return JProxyDefaultImpl.isRunningStatic(); + } + + /** + * Starts source code periodic change detection. + *

Periodicity of change detection is defined by {@link JProxyConfig#setScanPeriod(long)}.

+ *

By default when JProxy is initialized and enabled.

+ * + * @return true if source change detection has been started again, false if it is already started or cannot start because JProxy is not enabled or initialized or scan period is not positive. + * @see #start() + */ + public static boolean start() { + return JProxyDefaultImpl.startStatic(); + } + + /** + * Stops source code periodic change detection. + *

Periodicity of change detection is defined by {@link JProxyConfig#setScanPeriod(long)}

+ * + * @return true if source change detection has been stopped, false if it is already stopped or JProxy is not enabled or initialized. + * @see #stop() + */ + public static boolean stop() { + return JProxyDefaultImpl.stopStatic(); + } +} diff --git a/src/main/java/com/sillelien/jas/jproxy/JProxyCompilerListener.java b/src/main/java/com/sillelien/jas/jproxy/JProxyCompilerListener.java new file mode 100644 index 0000000..8d86ccb --- /dev/null +++ b/src/main/java/com/sillelien/jas/jproxy/JProxyCompilerListener.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * Is the interface to monitor the files being compiled. + * + * @author Jose Maria Arranz Santamaria + * @see JProxyConfig#setJProxyCompilerListener(JProxyCompilerListener) + */ +public interface JProxyCompilerListener { + void beforeCompile(@NotNull File file); + + void afterCompile(@NotNull File file); +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyConfig.java b/src/main/java/com/sillelien/jas/jproxy/JProxyConfig.java similarity index 57% rename from relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyConfig.java rename to src/main/java/com/sillelien/jas/jproxy/JProxyConfig.java index 52d5b93..09f0abf 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyConfig.java +++ b/src/main/java/com/sillelien/jas/jproxy/JProxyConfig.java @@ -1,164 +1,187 @@ - -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.RelProxyOnReloadListener; - -/** - * Interface implemented by the configuration object needed to initialize JProxy and {@link JProxyScriptEngineFactory}. - * - * @see JProxy#init(JProxyConfig) - * @see JProxyScriptEngine#init(JProxyConfig) - * @author Jose Maria Arranz Santamaria - */ -public interface JProxyConfig -{ - /** - * Sets whether automatic detection of source code changes is enabled. - * - *

If set to false other configuration parameters are ignored, there is no automatic source code change detection/reload and original objects are returned - * instead of proxies, performance penalty is zero. Setting to false is recommended in production whether source code change detection/reload is not required.

- * - * @param enabled whether automatic source code change detection and reload is enabled. By default is true. - * @return this object for flow API use. - */ - public JProxyConfig setEnabled(boolean enabled); - - /** - * Sets the class reload listener. - * - * @param relListener the class reload listener. By default is null. - * @return this object for flow API use. - */ - public JProxyConfig setRelProxyOnReloadListener(RelProxyOnReloadListener relListener); - - /** - * Defines the folder root to locate source code Java files. - * - *

Structure of the source tree must be the same as a JavaSE application, the only difference is shell scripts, shell scripts must be - * located on the top level of the source tree (default package) and file extension is not required .

- * - *

Setting some input path is required.

- * - * @param inputPath the folder root to locate source code Java files. - * @return this object for flow API use. - * @see #setInputPaths(java.lang.String[]) - */ - public JProxyConfig setInputPath(String inputPath); - - /** - * Defines the folder roots to locate source code Java files. - * - *

Structure of the source tree must be the same as a JavaSE application, the only difference is shell scripts, shell scripts must be - * located on the top level of the source tree (default package).

- * - *

Setting some input path is required.

- * - * @param inputPaths the folder roots to locate source code Java files. - * @return this object for flow API use. - * @see #setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener) - * @see #setRequiredExtraJarPaths(String[]) - */ - public JProxyConfig setInputPaths(String[] inputPaths); - - /** - * Defines the extra required jars providing the absolute paths to them. - * - *

In some circunstances RelProxy is not able to find some required classes by the compiler because the jar containing them is not found in spite of existing in classpath, - * the reason may be due some conflictive configuration of META-INF/MANIFEST.MF of the jar. This problem can be fixed modifying accordingly the META-INF/MANIFEST.MF - * file, or alternatively providing the paths to the conflictive jars calling to this method, in this case RelProxy will find the required classes searching on them by using brute force - * avoiding the jar modification (not recommended). - *

- * - * @param inputJarPaths the paths of the required extra jars. - * @return this object for flow API use. - * @see #setInputPaths(String[]) - */ - public JProxyConfig setRequiredExtraJarPaths(String[] inputJarPaths); - - /** - * Registers the listener implementing excluding rules to filter source files not to be part of the hot reloading system in spite of included in input paths. - * - * @param listener the listener. By default is null. - * @return this object for flow API use. - */ - public JProxyConfig setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener listener); - - /** - * Registers the listener for monitoring files being compiled. - * - * @param listener the listener. By default is null. - * @return this object for flow API use. - */ - public JProxyConfig setJProxyCompilerListener(JProxyCompilerListener listener); - - - /** - * Sets the folder where to save .class files result of recompiling source code changed. - * - *

This setting is optional and the folder must be included in Java classpath because the objective is to avoid recompiling.

- * - *

Be careful when executing several Java scripts in the same time and source code has been changed, some file write collisions may happen.

- * - * @param classFolder the folder where to save .class files. By default is null (not defined, .class files are not saved). - * @return this object for flow API use. - */ - public JProxyConfig setClassFolder(String classFolder); - - /** - * Sets the delay between source code change checking. - * - *

If this value is set to 0 or negative, no periodic source code change detection is executed and only compilation on the fly happens in load time, - * this is valid for one shot scripts but it has no sense when using proxies. - * - * @param scanPeriod the delay between source code change checking. - * @return this object for flow API use. - */ - public JProxyConfig setScanPeriod(long scanPeriod); - - /** - * Sets the compilation options to be provided to the compiler built-in in JDK like JavaCompiler.getTask() method and the same you would provide to javac. - * - *

Example of compilation options:

- *

Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"});

- * - * @param compilationOptions compilation options passed to the internal compiler. By default is null (default compiler settings). - * @return this object for flow API use. - */ - public JProxyConfig setCompilationOptions(Iterable compilationOptions); - - /** - * Sets the diagnostic listener to capture compilation errors and warnings thrown by the internal compiler. - * - *

The following is an example similar to the default behavior when this listener is not specified:

- * -
-        JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener()
-        {
-            {@code @}Override
-            public void onDiagnostics(DiagnosticCollector{@code <}JavaFileObject> diagnostics)
-            {
-                List{@code <}Diagnostic{@code <}? extends JavaFileObject>> diagList = diagnostics.getDiagnostics();                
-                int i = 1;
-                for (Diagnostic diagnostic : diagList)
-                {
-                   System.err.println("Diagnostic " + i);
-                   System.err.println("  code: " + diagnostic.getCode());
-                   System.err.println("  kind: " + diagnostic.getKind());
-                   System.err.println("  line number: " + diagnostic.getLineNumber());                   
-                   System.err.println("  column number: " + diagnostic.getColumnNumber());
-                   System.err.println("  start position: " + diagnostic.getStartPosition());
-                   System.err.println("  position: " + diagnostic.getPosition());                   
-                   System.err.println("  end position: " + diagnostic.getEndPosition());
-                   System.err.println("  source: " + diagnostic.getSource());
-                   System.err.println("  message: " + diagnostic.getMessage(null));
-                   i++;
-                }
-            }
-        };   
-     
- * - * @param diagnosticsListener the diagnostic listener to capture compilation errors and warnings. By default is null, an internal listener is used logging to System.err. - * @return this object for flow API use. - */ - public JProxyConfig setJProxyDiagnosticsListener(JProxyDiagnosticsListener diagnosticsListener); -} + +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import com.sillelien.jas.RelProxyOnReloadListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Interface implemented by the configuration object needed to initialize JProxy and {@link JProxyScriptEngineFactory}. + * + * @author Jose Maria Arranz Santamaria + * @see JProxy#init(JProxyConfig) + * @see JProxyScriptEngine#init(JProxyConfig) + */ +public interface JProxyConfig { + /** + * Sets the folder where to save .class files result of recompiling source code changed. + *

This setting is optional and the folder must be included in Java classpath because the objective is to avoid recompiling.

+ *

Be careful when executing several Java scripts in the same time and source code has been changed, some file write collisions may happen.

+ * + * @param classFolder the folder where to save .class files. By default is null (not defined, .class files are not saved). + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setClassFolder(@Nullable String classFolder); + + /** + * Sets the compilation options to be provided to the compiler built-in in JDK like JavaCompiler.getTask() method and the same you would provide to javac. + *

Example of compilation options:

+ *

Iterable<String> compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"});

+ * + * @param compilationOptions compilation options passed to the internal compiler. By default is null (default compiler settings). + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setCompilationOptions(@NotNull Iterable compilationOptions); + + /** + * Sets whether automatic detection of source code changes is enabled. + *

If set to false other configuration parameters are ignored, there is no automatic source code change detection/reload and original objects are returned + * instead of proxies, performance penalty is zero. Setting to false is recommended in production whether source code change detection/reload is not required.

+ * + * @param enabled whether automatic source code change detection and reload is enabled. By default is true. + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setEnabled(boolean enabled); + + @NotNull + JProxyConfig setImports(@NotNull List imports); + + /** + * Defines the folder root to locate source code Java files. + *

Structure of the source tree must be the same as a JavaSE application, the only difference is shell scripts, shell scripts must be + * located on the top level of the source tree (default package) and file extension is not required .

+ *

Setting some input path is required.

+ * + * @param inputPath the folder root to locate source code Java files. + * @return this object for flow API use. + * @see #setInputPaths(java.lang.String[]) + */ + @NotNull + JProxyConfig setInputPath(@NotNull String inputPath); + + /** + * Defines the folder roots to locate source code Java files. + *

Structure of the source tree must be the same as a JavaSE application, the only difference is shell scripts, shell scripts must be + * located on the top level of the source tree (default package).

+ *

Setting some input path is required.

+ * + * @param inputPaths the folder roots to locate source code Java files. + * @return this object for flow API use. + * @see #setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener) + * @see #setRequiredExtraJarPaths(String[]) + */ + @NotNull + JProxyConfig setInputPaths(@NotNull String[] inputPaths); + + /** + * Registers the listener for monitoring files being compiled. + * + * @param listener the listener. By default is null. + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setJProxyCompilerListener(@NotNull JProxyCompilerListener listener); + + /** + * Sets the diagnostic listener to capture compilation errors and warnings thrown by the internal compiler. + *

The following is an example similar to the default behavior when this listener is not specified:

+ *
+     * JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener()
+     * {
+     * {@code @}Override
+     * public void onDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics)
+     * {
+     * List<Diagnostic<? extends JavaFileObject>> diagList = diagnostics.getDiagnostics();
+     * int i = 1;
+     * for (Diagnostic diagnostic : diagList)
+     * {
+     * System.err.println("Diagnostic " + i);
+     * System.err.println("  code: " + diagnostic.getCode());
+     * System.err.println("  kind: " + diagnostic.getKind());
+     * System.err.println("  line number: " + diagnostic.getLineNumber());
+     * System.err.println("  column number: " + diagnostic.getColumnNumber());
+     * System.err.println("  start position: " + diagnostic.getStartPosition());
+     * System.err.println("  position: " + diagnostic.getPosition());
+     * System.err.println("  end position: " + diagnostic.getEndPosition());
+     * System.err.println("  source: " + diagnostic.getSource());
+     * System.err.println("  message: " + diagnostic.getMessage(null));
+     * i++;
+     * }
+     * }
+     * };
+     * 
+ * + * @param diagnosticsListener the diagnostic listener to capture compilation errors and warnings. By default is null, an internal listener is used logging to System.err. + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setJProxyDiagnosticsListener(@NotNull JProxyDiagnosticsListener diagnosticsListener); + + /** + * Registers the listener implementing excluding rules to filter source files not to be part of the hot reloading system in spite of included in input paths. + * + * @param listener the listener. By default is null. + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setJProxyInputSourceFileExcludedListener(@Nullable JProxyInputSourceFileExcludedListener listener); + + /** + * Sets the class reload listener. + * + * @param relListener the class reload listener. By default is null. + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setRelProxyOnReloadListener(@NotNull RelProxyOnReloadListener relListener); + + /** + * Defines the extra required jars providing the absolute paths to them. + *

In some circunstances RelProxy is not able to find some required classes by the compiler because the jar containing them is not found in spite of existing in classpath, + * the reason may be due some conflictive configuration of META-INF/MANIFEST.MF of the jar. This problem can be fixed modifying accordingly the META-INF/MANIFEST.MF + * file, or alternatively providing the paths to the conflictive jars calling to this method, in this case RelProxy will find the required classes searching on them by using brute force + * avoiding the jar modification (not recommended). + *

+ * + * @param inputJarPaths the paths of the required extra jars. + * @return this object for flow API use. + * @see #setInputPaths(String[]) + */ + @NotNull + JProxyConfig setRequiredExtraJarPaths(@NotNull String[] inputJarPaths); + + /** + * Sets the delay between source code change checking. + *

If this value is set to 0 or negative, no periodic source code change detection is executed and only compilation on the fly happens in load time, + * this is valid for one shot scripts but it has no sense when using proxies. + * + * @param scanPeriod the delay between source code change checking. + * @return this object for flow API use. + */ + @NotNull + JProxyConfig setScanPeriod(long scanPeriod); + + @NotNull + JProxyConfig setStaticImports(@NotNull List staticImports); +} diff --git a/src/main/java/com/sillelien/jas/jproxy/JProxyDiagnosticsListener.java b/src/main/java/com/sillelien/jas/jproxy/JProxyDiagnosticsListener.java new file mode 100644 index 0000000..863ccad --- /dev/null +++ b/src/main/java/com/sillelien/jas/jproxy/JProxyDiagnosticsListener.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import org.jetbrains.annotations.NotNull; + +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; + +/** + * Is the interface to implement diagnostic listeners to capture compilation errors and warnings. + * + * @author Jose Maria Arranz Santamaria + * @see JProxyConfig#setJProxyDiagnosticsListener(JProxyDiagnosticsListener) + */ +public interface JProxyDiagnosticsListener { + /** + * This method is called when compilation Java code has generated diagnostics. + * + * @param diagnostics the list of diagnostics. + */ + void onDiagnostics(@NotNull DiagnosticCollector diagnostics); +} diff --git a/src/main/java/com/sillelien/jas/jproxy/JProxyInputSourceFileExcludedListener.java b/src/main/java/com/sillelien/jas/jproxy/JProxyInputSourceFileExcludedListener.java new file mode 100644 index 0000000..317ad88 --- /dev/null +++ b/src/main/java/com/sillelien/jas/jproxy/JProxyInputSourceFileExcludedListener.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * This interface is provided to developers to implement excluding rules to filter source files not to be part of the hot reloading system in spite of included in input paths + * + * @author Jose Maria Arranz Santamaria + * @see JProxyConfig#setJProxyInputSourceFileExcludedListener(JProxyInputSourceFileExcludedListener) + */ +public interface JProxyInputSourceFileExcludedListener { + /** + * This method is called per file when going to be managed by the hot reloading system. + * + * @param file the file to be managed. + * @param rootFolderOfSources the folder root of sources where this file is located. + * @return true whether the file must be ignored. + */ + boolean isExcluded(@NotNull File file, @NotNull File rootFolderOfSources); +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyScriptEngine.java b/src/main/java/com/sillelien/jas/jproxy/JProxyScriptEngine.java similarity index 59% rename from relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyScriptEngine.java rename to src/main/java/com/sillelien/jas/jproxy/JProxyScriptEngine.java index 10a5ef7..1a7b952 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyScriptEngine.java +++ b/src/main/java/com/sillelien/jas/jproxy/JProxyScriptEngine.java @@ -1,69 +1,87 @@ -package com.innowhere.relproxy.jproxy; +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.script.ScriptEngine; /** * Interface implemented by RelProxy to provide javax.script.ScriptEngine objects supporting Java as a scripting language. - * *

The method {@link JProxyScriptEngineFactory#getScriptEngine()} returns an implementation of this interface.

- * + * * @author Jose Maria Arranz Santamaria */ -public interface JProxyScriptEngine extends ScriptEngine -{ - /** - * Initializes this JProxyScriptEngine instance with the provided configuration object. - * - * @param config the configuration object. - */ - public void init(JProxyConfig config); - +public interface JProxyScriptEngine extends ScriptEngine { /** * This method is the same as {@link JProxy#create(java.lang.Object, java.lang.Class)} but applied to this JProxyScriptEngine - * - * @param the interface implemented by the original object and proxy object returned. - * @param obj the original object to proxy. + * + * @param the interface implemented by the original object and proxy object returned. + * @param obj the original object to proxy. * @param clasz the class of the interface implemented by the original object and proxy object returned. * @return the java.lang.reflect.Proxy object associated or the original object when is disabled. */ - public T create(T obj,Class clasz); + @Nullable T create(@NotNull T obj, @NotNull Class clasz); /** * This method is the same as {@link JProxy#create(java.lang.Object, java.lang.Class[])} but applied to this JProxyScriptEngine - * - * @param obj the original object to proxy. + * + * @param obj the original object to proxy. * @param classes the classes of the interfaces implemented by the original object and proxy object returned. * @return the java.lang.reflect.Proxy object associated or the original object when is disabled. - */ - public Object create(Object obj,Class[] classes); - + */ + @Nullable + Object create(@NotNull Object obj, @NotNull Class[] classes); + + /** + * Initializes this JProxyScriptEngine instance with the provided configuration object. + * + * @param config the configuration object. + */ + void init(@NotNull JProxyConfig config); + /** * This method is the same as {@link JProxy#isEnabled()} but applied to this JProxyScriptEngine - * - * @return true if enabled. - */ - public boolean isEnabled(); - + * + * @return true if enabled. + */ + boolean isEnabled(); + /** * This method is the same as {@link JProxy#isRunning()} but applied to this JProxyScriptEngine - * - * @return true if running. - */ - public boolean isRunning(); - - /** - * This method is the same as {@link JProxy#stop()} but applied to this JProxyScriptEngine - * - * @return true if source change detection has been stopped, false if it is already stopped or this JProxyScriptEngine is not enabled or initialized. - * @see #stop() - */ - public boolean stop(); + * + * @return true if running. + */ + boolean isRunning(); /** * This method is the same as {@link JProxy#start()} but applied to this JProxyScriptEngine - * + * * @return true if source change detection has been started again, false if it is already started or cannot start because this JProxyScriptEngine is not enabled or initialized or scan period is not positive. * @see #start() */ - public boolean start(); + boolean start(); + + /** + * This method is the same as {@link JProxy#stop()} but applied to this JProxyScriptEngine + * + * @return true if source change detection has been stopped, false if it is already stopped or this JProxyScriptEngine is not enabled or initialized. + * @see #stop() + */ + boolean stop(); } diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyScriptEngineFactory.java b/src/main/java/com/sillelien/jas/jproxy/JProxyScriptEngineFactory.java similarity index 65% rename from relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyScriptEngineFactory.java rename to src/main/java/com/sillelien/jas/jproxy/JProxyScriptEngineFactory.java index c04e34a..525bf0c 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/JProxyScriptEngineFactory.java +++ b/src/main/java/com/sillelien/jas/jproxy/JProxyScriptEngineFactory.java @@ -1,22 +1,21 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.impl.jproxy.screngine.JProxyScriptEngineFactoryImpl; -import javax.script.ScriptEngineFactory; - -/** - * Is the root class of JSR-223 Java Scripting API support. - * - * @author Jose Maria Arranz Santamaria - */ -public abstract class JProxyScriptEngineFactory implements ScriptEngineFactory -{ - /** - * Factory method to create a JProxyScriptEngineFactory implementing ScriptEngineFactory. - * - * @return the new factory. - */ - public static JProxyScriptEngineFactory create() - { - return JProxyScriptEngineFactoryImpl.create(); - } -} +package com.sillelien.jas.jproxy; + +import com.sillelien.jas.impl.jproxy.screngine.JProxyScriptEngineFactoryImpl; + +import javax.script.ScriptEngineFactory; + +/** + * Is the root class of JSR-223 Java Scripting API support. + * + * @author Jose Maria Arranz Santamaria + */ +public abstract class JProxyScriptEngineFactory implements ScriptEngineFactory { + /** + * Factory method to create a JProxyScriptEngineFactory implementing ScriptEngineFactory. + * + * @return the new factory. + */ + public static JProxyScriptEngineFactory create() { + return JProxyScriptEngineFactoryImpl.create(); + } +} diff --git a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/package.html b/src/main/java/com/sillelien/jas/jproxy/package.html similarity index 63% rename from relproxy/src/main/java/com/innowhere/relproxy/jproxy/package.html rename to src/main/java/com/sillelien/jas/jproxy/package.html index 372b30f..e8701f4 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/jproxy/package.html +++ b/src/main/java/com/sillelien/jas/jproxy/package.html @@ -1,13 +1,13 @@ - - - - - - - -Contains the classes related with Java support of RelProxy. - - - + + + + + + + +Contains the classes related with Java support of RelProxy. + + + diff --git a/relproxy/src/main/java/com/innowhere/relproxy/package.html b/src/main/java/com/sillelien/jas/package.html similarity index 70% rename from relproxy/src/main/java/com/innowhere/relproxy/package.html rename to src/main/java/com/sillelien/jas/package.html index 1ede5ea..01103dd 100644 --- a/relproxy/src/main/java/com/innowhere/relproxy/package.html +++ b/src/main/java/com/sillelien/jas/package.html @@ -1,13 +1,13 @@ - - - - - - - -Contains the root and shared interfaces and classes of RelProxy, shared between all different language based utilities. - - - + + + + + + + +Contains the root and shared interfaces and classes of RelProxy, shared between all different language based utilities. + + + diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaScriptEngineNoManagerTest.java b/src/test/java/com/sillelien/jas/jproxy/JProxyJavaScriptEngineNoManagerTest.java similarity index 72% rename from relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaScriptEngineNoManagerTest.java rename to src/test/java/com/sillelien/jas/jproxy/JProxyJavaScriptEngineNoManagerTest.java index 9ad97a7..8c481e0 100644 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaScriptEngineNoManagerTest.java +++ b/src/test/java/com/sillelien/jas/jproxy/JProxyJavaScriptEngineNoManagerTest.java @@ -1,186 +1,204 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.RelProxyOnReloadListener; -import static com.innowhere.relproxy.jproxy.util.JProxyTestUtil.RESOURCES_FOLDER; -import static com.innowhere.relproxy.jproxy.util.JProxyTestUtil.getProjectFolder; - - -import java.io.File; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import javax.script.Bindings; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; -import org.junit.After; -import org.junit.AfterClass; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * - * @author jmarranz - */ -public class JProxyJavaScriptEngineNoManagerTest -{ - - public JProxyJavaScriptEngineNoManagerTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - - } - - @After - public void tearDown() - { - - } - - - @Test - public void test_java_script_engine() - { - File projectFolder = getProjectFolder(); - - File inputFolderFile = new File(projectFolder,RESOURCES_FOLDER); - // File classFolderFile = new File(projectFolder,"tmp/java_shell_test_classes"); - String inputPath = inputFolderFile.getAbsolutePath(); - String classFolder = null; // Optional - Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - long scanPeriod = 300; - - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - @Override - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ - @Override - public void beforeCompile(File file) - { - System.out.println("Before compile: " + file); - } - - @Override - public void afterCompile(File file) - { - System.out.println("After compile: " + file); - } - }; - - JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() - { - @Override - public void onDiagnostics(DiagnosticCollector diagnostics) - { - List> diagList = diagnostics.getDiagnostics(); - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - }; - - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setJProxyInputSourceFileExcludedListener(null) - .setJProxyCompilerListener(compilerListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyDiagnosticsListener(diagnosticsListener); - - JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); - - ScriptEngine engine = factory.getScriptEngine(); - - ((JProxyScriptEngine)engine).init(jpConfig); - - assertNotNull(engine); - - try - { - - // El javax.script.ScriptContext.GLOBAL_SCOPE no está disponible porque no hemos usado el ScriptEngineManager para obtener el JProxyScriptEngine - // pero si podemos crear un javax.script.ScriptContext.ENGINE_SCOPE: - - Bindings bindings = engine.createBindings(); - bindings.put("msg","HELLO ENGINE SCOPE WORLD!"); - - - StringBuilder code = new StringBuilder(); - code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); - code.append( " String msg = (String)bindings.get(\"msg\"); \n"); - code.append( " System.out.println(msg); \n"); - code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); - code.append( " return \"SUCCESS\";"); - - String result = (String)engine.eval( code.toString() , bindings); - assertEquals("SUCCESS",result); - - bindings = engine.createBindings(); - bindings.put("msg","HELLO ENGINE SCOPE WORLD 2!"); - - code = new StringBuilder(); - code.append( "public class _jproxyMainClass_ { \n"); - code.append( " public static Object main(javax.script.ScriptEngine engine,javax.script.ScriptContext context) { \n"); - code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); - code.append( " String msg = (String)bindings.get(\"msg\"); \n"); - code.append( " System.out.println(msg); \n"); - code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); - code.append( " return \"SUCCESS 2\";"); - code.append( " }"); - code.append( "}"); - - result = (String)engine.eval( code.toString() , bindings); - assertEquals("SUCCESS 2",result); - } - catch(ScriptException ex) - { - ex.printStackTrace(); - assertTrue(false); - } - finally - { - boolean res = ((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined - assertTrue(res); - } - } -} +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import com.sillelien.jas.RelProxyOnReloadListener; +import com.sillelien.jas.jproxy.util.JProxyTestUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; +import java.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * + * @author jmarranz + */ +public class JProxyJavaScriptEngineNoManagerTest +{ + + @NotNull + private static final Logger log = LoggerFactory.getLogger("JProxyJavaScriptEngineNoManagerTest"); + + public JProxyJavaScriptEngineNoManagerTest() + { + } + + @BeforeClass + public static void setUpClass() + { + } + + @AfterClass + public static void tearDownClass() + { + } + + @Before + public void setUp() + { + + } + + @After + public void tearDown() + { + + } + + + @Test + public void test_java_script_engine() + { + File projectFolder = JProxyTestUtil.getProjectFolder(); + + File inputFolderFile = new File(projectFolder, JProxyTestUtil.RESOURCES_FOLDER); + // File classFolderFile = new File(projectFolder,"tmp/java_shell_test_classes"); + String inputPath = inputFolderFile.getAbsolutePath(); + String classFolder = null; // Optional + Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); + long scanPeriod = 300; + + RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { + @Override + public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { + System.out.println("Reloaded " + objNew + " Calling method: " + method); + } + }; + + JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ + @Override + public void beforeCompile(File file) + { + System.out.println("Before compile: " + file); + } + + @Override + public void afterCompile(File file) + { + System.out.println("After compile: " + file); + } + }; + + JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() + { + @Override + public void onDiagnostics(@NotNull DiagnosticCollector diagnostics) + { + List> diagList = diagnostics.getDiagnostics(); + int i = 1; + for (Diagnostic diagnostic : diagList) + { + log.debug("Diagnostic " + i); + log.debug(" code: " + diagnostic.getCode()); + log.debug(" kind: " + diagnostic.getKind()); + log.debug(" line number: " + diagnostic.getLineNumber()); + log.debug(" column number: " + diagnostic.getColumnNumber()); + log.debug(" start position: " + diagnostic.getStartPosition()); + log.debug(" position: " + diagnostic.getPosition()); + log.debug(" end position: " + diagnostic.getEndPosition()); + log.debug(" source: " + diagnostic.getSource()); + log.debug(" message: " + diagnostic.getMessage(null)); + i++; + } + } + }; + + JProxyConfig jpConfig = JProxy.createJProxyConfig(); + jpConfig.setEnabled(true) + .setRelProxyOnReloadListener(proxyListener) + .setInputPath(inputPath) + .setJProxyInputSourceFileExcludedListener(null) + .setJProxyCompilerListener(compilerListener) + .setScanPeriod(scanPeriod) +// .setClassFolder(classFolder) + .setCompilationOptions(compilationOptions) + .setJProxyDiagnosticsListener(diagnosticsListener); + + JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); + + ScriptEngine engine = factory.getScriptEngine(); + + ((JProxyScriptEngine)engine).init(jpConfig); + + assertNotNull(engine); + + try + { + + // El javax.script.ScriptContext.GLOBAL_SCOPE no está disponible porque no hemos usado el ScriptEngineManager para obtener el JProxyScriptEngine + // pero si podemos crear un javax.script.ScriptContext.ENGINE_SCOPE: + + Bindings bindings = engine.createBindings(); + bindings.put("msg","HELLO ENGINE SCOPE WORLD!"); + + + StringBuilder code = new StringBuilder(); + code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); + code.append( " String msg = (String)bindings.get(\"msg\"); \n"); + code.append( " System.out.println(msg); \n"); + code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); + code.append( " return \"SUCCESS\";"); + + String result = (String)engine.eval( code.toString() , bindings); + assertEquals("SUCCESS",result); + + bindings = engine.createBindings(); + bindings.put("msg","HELLO ENGINE SCOPE WORLD 2!"); + + code = new StringBuilder(); + code.append( "public class _jproxyMainClass_ { \n"); + code.append( " public static Object main(javax.script.ScriptEngine engine,javax.script.ScriptContext context) { \n"); + code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); + code.append( " String msg = (String)bindings.get(\"msg\"); \n"); + code.append( " System.out.println(msg); \n"); + code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); + code.append( " return \"SUCCESS 2\";"); + code.append( " }"); + code.append( "}"); + + result = (String)engine.eval( code.toString() , bindings); + assertEquals("SUCCESS 2",result); + } + catch(ScriptException ex) + { + ex.printStackTrace(); + assertTrue(false); + } + finally + { + boolean res = ((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined + assertTrue(res); + } + } +} diff --git a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaScriptEngineTest.java b/src/test/java/com/sillelien/jas/jproxy/JProxyJavaScriptEngineTest.java similarity index 73% rename from relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaScriptEngineTest.java rename to src/test/java/com/sillelien/jas/jproxy/JProxyJavaScriptEngineTest.java index d9812b6..d8f5dc7 100644 --- a/relproxy/src/test/java/com/innowhere/relproxy/jproxy/JProxyJavaScriptEngineTest.java +++ b/src/test/java/com/sillelien/jas/jproxy/JProxyJavaScriptEngineTest.java @@ -1,194 +1,216 @@ -package com.innowhere.relproxy.jproxy; - -import com.innowhere.relproxy.RelProxyOnReloadListener; -import static com.innowhere.relproxy.jproxy.util.JProxyTestUtil.RESOURCES_FOLDER; -import static com.innowhere.relproxy.jproxy.util.JProxyTestUtil.getProjectFolder; - - -import java.io.File; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import javax.script.Bindings; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; -import org.junit.After; -import org.junit.AfterClass; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * - * @author jmarranz - */ -public class JProxyJavaScriptEngineTest -{ - - public JProxyJavaScriptEngineTest() - { - } - - @BeforeClass - public static void setUpClass() - { - } - - @AfterClass - public static void tearDownClass() - { - } - - @Before - public void setUp() - { - - } - - @After - public void tearDown() - { - - } - - - @Test - public void test_java_script_engine() - { - File projectFolder = getProjectFolder(); - - File inputFolderFile = new File(projectFolder,RESOURCES_FOLDER); - // File classFolderFile = new File(projectFolder,"tmp/java_shell_test_classes"); - String inputPath = inputFolderFile.getAbsolutePath(); - String classFolder = null; // Optional - Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); - long scanPeriod = 300; - - RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { - @Override - public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { - System.out.println("Reloaded " + objNew + " Calling method: " + method); - } - }; - - JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ - @Override - public void beforeCompile(File file) - { - System.out.println("Before compile: " + file); - } - - @Override - public void afterCompile(File file) - { - System.out.println("After compile: " + file); - } - }; - - JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() - { - @Override - public void onDiagnostics(DiagnosticCollector diagnostics) - { - List> diagList = diagnostics.getDiagnostics(); - int i = 1; - for (Diagnostic diagnostic : diagList) - { - System.err.println("Diagnostic " + i); - System.err.println(" code: " + diagnostic.getCode()); - System.err.println(" kind: " + diagnostic.getKind()); - System.err.println(" line number: " + diagnostic.getLineNumber()); - System.err.println(" column number: " + diagnostic.getColumnNumber()); - System.err.println(" start position: " + diagnostic.getStartPosition()); - System.err.println(" position: " + diagnostic.getPosition()); - System.err.println(" end position: " + diagnostic.getEndPosition()); - System.err.println(" source: " + diagnostic.getSource()); - System.err.println(" message: " + diagnostic.getMessage(null)); - i++; - } - } - }; - - JProxyConfig jpConfig = JProxy.createJProxyConfig(); - jpConfig.setEnabled(true) - .setRelProxyOnReloadListener(proxyListener) - .setInputPath(inputPath) - .setJProxyInputSourceFileExcludedListener(null) - .setJProxyCompilerListener(compilerListener) - .setScanPeriod(scanPeriod) - .setClassFolder(classFolder) - .setCompilationOptions(compilationOptions) - .setJProxyDiagnosticsListener(diagnosticsListener); - - JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); - - ScriptEngineManager manager = new ScriptEngineManager(); - manager.registerEngineName("Java", factory); - - manager.getBindings().put("msg","HELLO GLOBAL WORLD!"); - - ScriptEngine engine = manager.getEngineByName("Java"); - - ((JProxyScriptEngine)engine).init(jpConfig); - - assertNotNull(engine); - - try - { - - Bindings bindings = engine.createBindings(); - bindings.put("msg","HELLO ENGINE SCOPE WORLD!"); - - - StringBuilder code = new StringBuilder(); - code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); - code.append( " String msg = (String)bindings.get(\"msg\"); \n"); - code.append( " System.out.println(msg); \n"); - code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n"); - code.append( " msg = (String)bindings.get(\"msg\"); \n"); - code.append( " System.out.println(msg); \n"); - code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); - code.append( " return \"SUCCESS\";"); - - String result = (String)engine.eval( code.toString() , bindings); - assertEquals("SUCCESS",result); - - bindings = engine.createBindings(); - bindings.put("msg","HELLO ENGINE SCOPE WORLD 2!"); - - code = new StringBuilder(); - code.append( "public class _jproxyMainClass_ { \n"); - code.append( " public static Object main(javax.script.ScriptEngine engine,javax.script.ScriptContext context) { \n"); - code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); - code.append( " String msg = (String)bindings.get(\"msg\"); \n"); - code.append( " System.out.println(msg); \n"); - code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n"); - code.append( " msg = (String)bindings.get(\"msg\"); \n"); - code.append( " System.out.println(msg); \n"); - code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); - code.append( " return \"SUCCESS 2\";"); - code.append( " }"); - code.append( "}"); - - result = (String)engine.eval( code.toString() , bindings); - assertEquals("SUCCESS 2",result); - } - catch(ScriptException ex) - { - ex.printStackTrace(); - assertTrue(false); - } - finally - { - boolean res = ((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined - assertTrue(res); - } - } -} +/* + * Copyright (c) 2014-2017 Neil Ellis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sillelien.jas.jproxy; + +import com.sillelien.jas.RelProxyOnReloadListener; +import com.sillelien.jas.jproxy.util.JProxyTestUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; +import java.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * + * @author jmarranz + */ +public class JProxyJavaScriptEngineTest +{ + + @NotNull + private static final Logger log = LoggerFactory.getLogger("JProxyJavaScriptEngineTest"); + + + public JProxyJavaScriptEngineTest() + { + } + + @BeforeClass + public static void setUpClass() + { + } + + @AfterClass + public static void tearDownClass() + { + } + + @Before + public void setUp() + { + + } + + @After + public void tearDown() + { + + } + + + @Test + public void test_java_script_engine() + { + File projectFolder = JProxyTestUtil.getProjectFolder(); + + File inputFolderFile = new File(projectFolder, JProxyTestUtil.RESOURCES_FOLDER); + // File classFolderFile = new File(projectFolder,"tmp/java_shell_test_classes"); + String inputPath = inputFolderFile.getAbsolutePath(); + String classFolder = null; // Optional + Iterable compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); + long scanPeriod = 300; + + RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { + @Override + public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { + System.out.println("Reloaded " + objNew + " Calling method: " + method); + } + }; + + JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ + @Override + public void beforeCompile(File file) + { + System.out.println("Before compile: " + file); + } + + @Override + public void afterCompile(File file) + { + System.out.println("After compile: " + file); + } + }; + + JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() + { + @Override + public void onDiagnostics(@NotNull DiagnosticCollector diagnostics) + { + List> diagList = diagnostics.getDiagnostics(); + int i = 1; + for (Diagnostic diagnostic : diagList) + { + log.debug("Diagnostic " + i); + log.debug(" code: " + diagnostic.getCode()); + log.debug(" kind: " + diagnostic.getKind()); + log.debug(" line number: " + diagnostic.getLineNumber()); + log.debug(" column number: " + diagnostic.getColumnNumber()); + log.debug(" start position: " + diagnostic.getStartPosition()); + log.debug(" position: " + diagnostic.getPosition()); + log.debug(" end position: " + diagnostic.getEndPosition()); + log.debug(" source: " + diagnostic.getSource()); + log.debug(" message: " + diagnostic.getMessage(null)); + i++; + } + } + }; + + JProxyConfig jpConfig = JProxy.createJProxyConfig(); + jpConfig.setEnabled(true) + .setRelProxyOnReloadListener(proxyListener) + .setInputPath(inputPath) + .setJProxyInputSourceFileExcludedListener(null) + .setJProxyCompilerListener(compilerListener) + .setScanPeriod(scanPeriod) +// .setClassFolder(classFolder) + .setCompilationOptions(compilationOptions) + .setJProxyDiagnosticsListener(diagnosticsListener); + + JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(); + + ScriptEngineManager manager = new ScriptEngineManager(); + manager.registerEngineName("Java", factory); + + manager.getBindings().put("msg","HELLO GLOBAL WORLD!"); + + ScriptEngine engine = manager.getEngineByName("Java"); + + assert engine != null; + + ((JProxyScriptEngine)engine).init(jpConfig); + + assertNotNull(engine); + + try + { + + Bindings bindings = engine.createBindings(); + bindings.put("msg","HELLO ENGINE SCOPE WORLD!"); + + + StringBuilder code = new StringBuilder(); + code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); + code.append( " String msg = (String)bindings.get(\"msg\"); \n"); + code.append( " System.out.println(msg); \n"); + code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n"); + code.append( " msg = (String)bindings.get(\"msg\"); \n"); + code.append( " System.out.println(msg); \n"); + code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); + code.append( " return \"SUCCESS\";"); + + String result = (String)engine.eval( code.toString() , bindings); + assertEquals("SUCCESS",result); + + bindings = engine.createBindings(); + bindings.put("msg","HELLO ENGINE SCOPE WORLD 2!"); + + code = new StringBuilder(); + code.append( "public class _jproxyMainClass_ { \n"); + code.append( " public static Object main(javax.script.ScriptEngine engine,javax.script.ScriptContext context) { \n"); + code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n"); + code.append( " String msg = (String)bindings.get(\"msg\"); \n"); + code.append( " System.out.println(msg); \n"); + code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n"); + code.append( " msg = (String)bindings.get(\"msg\"); \n"); + code.append( " System.out.println(msg); \n"); + code.append( " example.javashellex.JProxyShellExample.exec(engine); \n"); + code.append( " return \"SUCCESS 2\";"); + code.append( " }"); + code.append( "}"); + + result = (String)engine.eval( code.toString() , bindings); + assertEquals("SUCCESS 2",result); + } + catch(ScriptException ex) + { + ex.printStackTrace(); + assertTrue(false); + } + finally + { + boolean res = ((JProxyScriptEngine)engine).stop(); // Necessary if scanPeriod > 0 was defined + assertTrue(res); + } + } +} diff --git a/src/test/java/com/sillelien/jas/jproxy/util/JProxyTestUtil.java b/src/test/java/com/sillelien/jas/jproxy/util/JProxyTestUtil.java new file mode 100644 index 0000000..22b88b5 --- /dev/null +++ b/src/test/java/com/sillelien/jas/jproxy/util/JProxyTestUtil.java @@ -0,0 +1,16 @@ +package com.sillelien.jas.jproxy.util; + +import java.io.File; + +/** + * @author jmarranz + */ +public class JProxyTestUtil { + public static final String RESOURCES_FOLDER = "src/test/resources"; + public static final String CACHE_CLASS_FOLDER = "tmp/java_shell_test_classes"; + + public static File getProjectFolder() { + + return new File(".").getAbsoluteFile(); + } +} diff --git a/relproxy/src/test/resources/example/javashellex/JProxyShellExample.java b/src/test/resources/example/javashellex/JProxyShellExample.java similarity index 86% rename from relproxy/src/test/resources/example/javashellex/JProxyShellExample.java rename to src/test/resources/example/javashellex/JProxyShellExample.java index f538370..fc77a4f 100644 --- a/relproxy/src/test/resources/example/javashellex/JProxyShellExample.java +++ b/src/test/resources/example/javashellex/JProxyShellExample.java @@ -1,22 +1,22 @@ -package example.javashellex; - -import javax.script.ScriptEngine; -import com.innowhere.relproxy.jproxy.JProxyScriptEngine; - -/** - * - * @author jmarranz - */ -public class JProxyShellExample -{ - public static void exec() - { - System.out.println("JProxyShellExample exec() 1 "); - } - - public static void exec(ScriptEngine engine) - { - JProxyScriptEngine jengine = ((JProxyScriptEngine)engine); // Just to show that ScriptEngine is a JProxyScriptEngine object - System.out.println("JProxyShellExample exec(ScriptEngine) 1 "); - } -} +package example.javashellex; + +import javax.script.ScriptEngine; +import com.sillelien.jas.jproxy.JProxyScriptEngine; + +/** + * + * @author jmarranz + */ +public class JProxyShellExample +{ + public static void exec() + { + System.out.println("JProxyShellExample exec() 1 "); + } + + public static void exec(ScriptEngine engine) + { + JProxyScriptEngine jengine = ((JProxyScriptEngine)engine); // Just to show that ScriptEngine is a JProxyScriptEngine object + System.out.println("JProxyShellExample exec(ScriptEngine) 1 "); + } +} diff --git a/relproxy/src/test/resources/example_java_shell b/src/test/resources/example_java_shell similarity index 100% rename from relproxy/src/test/resources/example_java_shell rename to src/test/resources/example_java_shell diff --git a/relproxy/src/test/resources/example_java_shell_complete_class b/src/test/resources/example_java_shell_complete_class similarity index 100% rename from relproxy/src/test/resources/example_java_shell_complete_class rename to src/test/resources/example_java_shell_complete_class diff --git a/relproxy/src/test/resources/example_normal_class.java b/src/test/resources/example_normal_class.java similarity index 100% rename from relproxy/src/test/resources/example_normal_class.java rename to src/test/resources/example_normal_class.java