diff --git a/RmiGriffonAddon.groovy b/RmiGriffonAddon.groovy index d8ca008..0ea38b2 100644 --- a/RmiGriffonAddon.groovy +++ b/RmiGriffonAddon.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2012 the original author or authors. + * Copyright 2009-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ import griffon.core.GriffonClass import griffon.plugins.rmi.RmiEnhancer +import griffon.plugins.rmi.RmiContributionHandler /** * @author Andres Almiray @@ -25,8 +26,9 @@ class RmiGriffonAddon { def types = app.config.griffon?.rmi?.injectInto ?: ['controller'] for(String type : types) { for(GriffonClass gc : app.artifactManager.getClassesOfType(type)) { + if (RmiContributionHandler.isAssignableFrom(gc.clazz)) continue RmiEnhancer.enhance(gc.metaClass) } } } -} +} \ No newline at end of file diff --git a/RmiGriffonPlugin.groovy b/RmiGriffonPlugin.groovy index cbec4ec..773930d 100644 --- a/RmiGriffonPlugin.groovy +++ b/RmiGriffonPlugin.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 the original author or authors. + * Copyright 2009-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. @@ -19,11 +19,11 @@ */ class RmiGriffonPlugin { // the plugin version - String version = '0.7' + String version = '1.0.0' // the version or versions of Griffon the plugin is designed for - String griffonVersion = '0.9.5 > *' + String griffonVersion = '1.2.0 > *' // the other plugins this plugin depends on - Map dependsOn = [:] + Map dependsOn = [lombok: '0.4'] // resources that are included in plugin packaging List pluginIncludes = [] // the plugin license @@ -49,13 +49,16 @@ class RmiGriffonPlugin { String title = 'RMI client' String description = ''' -The RMI plugin adds a remoting client that uses the [Java RMI protocol][1]. It is compatible with [Grails' Remoting plugin 1.0][2]. +The Rmi plugin adds a remoting client that uses the [Java RMI protocol][1]. It +is compatible with [Grails' Remoting plugin 1.3][2]. Usage ----- + The plugin will inject the following dynamic methods: -* `withRmi(Map params, Closure stmts)` - executes stmts using the Rmi protocol + * ` R withRmi(Map params, Closure stmts)` - executes stmts using a RmiClient + * ` R withRmi(Map params, CallableWithArgs stmts)` - executes stmts using a RmiClient Where params may contain @@ -65,26 +68,40 @@ Where params may contain | port | int | 1199 | | lazy | boolean | true | -All dynamic methods will create a new client when invoked unless you define an `id:` attribute. -When this attribute is supplied the client will be stored in a cache managed by the `RmiProvider` that -handled the call. The plugin will attempt to locate the default RmiRegistry when the `lazy:` property is set to false. +All dynamic methods will create a new client when invoked unless you define an +`id:` attribute. When this attribute is supplied the client will be stored in +a cache managed by the `RmiProvider` that handled the call. The plugin will +attempt to locate the default RmiRegistry when the `lazy:` property is set to false. -These methods are also accessible to any component through the singleton `griffon.plugins.rmi.RmiEnhancer`. -You can inject these methods to non-artifacts via metaclasses. Simply grab hold of a particular metaclass and call -`RmiEnhancer.enhance(metaClassInstance)`. +These methods are also accessible to any component through the singleton +`griffon.plugins.rmi.RmiEnhancer`. You can inject these methods to +non-artifacts via metaclasses. Simply grab hold of a particular metaclass and +call `RmiEnhancer.enhance(metaClassInstance)`. Configuration ------------- -### Dynamic method injection + +### RmiAware AST Transformation + +The preferred way to mark a class for method injection is by annotating it with +`@griffon.plugins.rmi.RmiAware`. This transformation injects the +`griffon.plugins.rmi.RmiContributionHandler` interface and default behavior +that fulfills the contract. + +### Dynamic Method Injection Dynamic methods will be added to controllers by default. You can change this setting by adding a configuration flag in `griffon-app/conf/Config.groovy` griffon.rmi.injectInto = ['controller', 'service'] +Dynamic method injection wil skipped for classes implementing +`griffon.plugins.rmi.RmiContributionHandler`. + ### Example -This example relies on [Grails][3] as the service provider. Follow these steps to configure the service on the Grails side: +This example relies on [Grails][3] as the service provider. Follow these steps +to configure the service on the Grails side: 1. Download a copy of [Grails][4] and install it. 2. Create a new Grails application. We'll pick 'exporter' as the application name. @@ -95,7 +112,8 @@ This example relies on [Grails][3] as the service provider. Follow these steps t grails install-plugin remoting -4. Create the following interface in `src/groovy/exporter/Calculator.groovy`. This interface will be used on the Griffon side too. +4. Create the following interface in `src/groovy/exporter/Calculator.groovy`. + This interface will be used on the Griffon side too. package exporter import java.rmi.Remote @@ -103,19 +121,19 @@ This example relies on [Grails][3] as the service provider. Follow these steps t interface Calculator extends Remote { double add(double a, double b) throws RemoteException } - + 5. Create an implementation of the `Calculator` interface as a service grails create-service calculator - + 6. Paste the following code in `grails-app/services/exporter/CalculatorService.groovy` package exporter class CalculatorService implements Calculator { boolean transactional = false static expose = ['rmi'] - - double add(double a, double b){ + + double add(double a, double b) { println "add($a, $b)" // good old println() for quick debugging return a + b } @@ -124,13 +142,14 @@ This example relies on [Grails][3] as the service provider. Follow these steps t 7. Run the application grails run-app - + Now we're ready to build the Griffon application -1. Create a new Griffon application. We'll pick `calculator` as the application name +1. Create a new Griffon application. We'll pick `calculator` as the application + name griffon create-app calculator - + 2. Install the rmi plugin griffon install-plugin rmi @@ -152,7 +171,7 @@ Now we're ready to build the Griffon application textField(columns: 20, text: bind(target: model, targetProperty: 'num2')) label('Result:') label(text: bind{model.result}) - button('Calculate', enabled: bind{model.enabled}, actionPerformed: controller.calculate) + button(calculateAction, enabled: bind{model.enabled}) } 4. Let's add required properties to the model @@ -160,19 +179,21 @@ Now we're ready to build the Griffon application package calculator @Bindable class CalculatorModel { - String num1 - String num2 - String result - boolean enabled = true + String num1 + String num2 + String result + boolean enabled = true } -5. Now for the controller code. Notice that there is minimal error handling in place. If the user -types something that is not a number the client will surely break, but the code is sufficient for now. +5. Now for the controller code. Notice that there is minimal error handling in + place. If the user types something that is not a number the client will + surely break, but the code is sufficient for now. package calculator + @griffon.plugins.rmi.RmiAware class CalculatorController { def model - + def calculate = { evt = null -> double a = model.num1.toDouble() double b = model.num2.toDouble() @@ -189,63 +210,99 @@ types something that is not a number the client will surely break, but the code } } } - -6. Locate the compiled classes from Grails; jar the calculator interface and place it in the lib directory -of the Griffon application. Assume $grailsProject points to the directory of the exporter application and -$griffonProject points to the calculator application + +6. Locate the compiled classes from Grails; jar the calculator interface and + place it in the lib directory of the Griffon application. Assume `$grailsProject` + points to the directory of the exporter application and `$griffonProject` + points to the calculator application cd $grailsProject/target/classes jar cvf $griffonProject/lib/exporter-api.jar exporter/Calculator.class - + 7. Run the application griffon run-app - -The first argument to `service()` may be a String with the full qualified classname or a Class. - -### Java API -Here's how the above service call may be written in Java +The plugin exposes a Java friendly API to make the exact same calls from Java, +or any other JVM language for that matter. Here's for example the previous code +rewritten in Java. Note the usage of @RmiWare on a Java class - import static griffon.util.CollectionUtils.map; - import griffon.plugins.rmi.RmiConnector; - import griffon.plugins.rmi.RMIClient; + package calculator; import griffon.util.CallableWithArgs; + import griffon.util.CollectionUtils; + import griffon.plugins.rmi.RmiClient; + import java.awt.event.ActionEvent; import java.util.Map; - import exporter.Calculator; - - final double a = Double.parseDouble(model.getNum1()); - final double b = Double.parseDouble(model.getNum2()); - Map params = map().e("host", "localhost") + import org.codehaus.griffon.runtime.core.AbstractGriffonController; + @griffon.plugins.rmi.RmiAware + public class CalculatorController extends AbstractGriffonController { + private CalculatorModel model; + + public void setModel(CalculatorModel model) { + this.model = model; + } + + public void calculate(ActionEvent event) { + final double a = Double.parseDouble(model.getNum1()); + final double b = Double.parseDouble(model.getNum2()); + enableModel(false); + try { + Map params = CollectionUtils. map() + .e("host", "localhost") .e("port", 1199); - Double result = RmiConnector.getInstance().withRmi(params, new CallableWithArgs() { - public Double call(Object[] args) { - RMIClient client = (RMIClient) args[0]; - return (Double) client.service("Calculator", new CallableWithArgs() { - public Double call(Object[] args2) { - return ((Calculator) args2[0]).add(a, b); + final Double result = withRmi(params, + new CallableWithArgs() { + public Double call(Object[] args) { + RmiClient client = (RmiClient) args[0]; + return (Double) client.service("Calculator", + new CallableWithArgs() { + public Double call(Object[] args2) { + return ((Calculator) args2[0]).add(a, b); + } + }); + } + }); + execInsideUIAsync(new Runnable() { + public void run() { + model.setResult(String.valueOf(result)); + } + }); + } finally { + enableModel(true); + } + } + + private void enableModel(final boolean enabled) { + execInsideUIAsync(new Runnable() { + public void run() { + model.setEnabled(enabled); } }); } - }); + } + Testing ------- -Dynamic methods will not be automatically injected during unit testing, because addons are simply not initialized -for this kind of tests. However you can use `RmiEnhancer.enhance(metaClassInstance, rmiProviderInstance)` where -`rmiProviderInstance` is of type `griffon.plugins.rmi.RmiProvider`. The contract for this interface looks like this + +Dynamic methods will not be automatically injected during unit testing, because +addons are simply not initialized for this kind of tests. However you can use +`RmiEnhancer.enhance(metaClassInstance, rmiProviderInstance)` where +`rmiProviderInstance` is of type `griffon.plugins.rmi.RmiProvider`. +The contract for this interface looks like this public interface RmiProvider { - Object withRmi(Map params, Closure closure); - T withRmi(Map params, CallableWithArgs callable); + R withRmi(Map params, Closure closure); + R withRmi(Map params, CallableWithArgs callable); } -It's up to you define how these methods need to be implemented for your tests. For example, here's an implementation that never -fails regardless of the arguments it receives +It's up to you define how these methods need to be implemented for your tests. +For example, here's an implementation that never fails regardless of the +arguments it receives class MyRmiProvider implements RmiProvider { - Object withRmi(Map params, Closure closure) { null } - public T withRmi(Map params, CallableWithArgs callable) { null } + public R withRmi(Map params, Closure closure) { null } + public R withRmi(Map params, CallableWithArgs callable) { null } } This implementation may be used in the following way @@ -258,10 +315,97 @@ This implementation may be used in the following way } } +On the other hand, if the service is annotated with `@RmiAware` then usage +of `RmiEnhancer` should be avoided at all costs. Simply set +`rmiProviderInstance` on the service instance directly, like so, first the +service definition + + @griffon.plugins.rmi.RmiAware + class MyService { + def serviceMethod() { ... } + } + +Next is the test + + class MyServiceTests extends GriffonUnitTestCase { + void testSmokeAndMirrors() { + MyService service = new MyService() + service.rmiProvider = new MyRmiProvider() + // exercise service methods + } + } + +Tool Support +------------ + +### DSL Descriptors + +This plugin provides DSL descriptors for Intellij IDEA and Eclipse (provided +you have the Groovy Eclipse plugin installed). These descriptors are found +inside the `griffon-rmi-compile-x.y.z.jar`, with locations + + * dsdl/rmi.dsld + * gdsl/rmi.gdsl + +### Lombok Support + +Rewriting Java AST in a similar fashion to Groovy AST transformations is +posisble thanks to the [lombok][5] plugin. + +#### JavaC + +Support for this compiler is provided out-of-the-box by the command line tools. +There's no additional configuration required. + +#### Eclipse + +Follow the steps found in the [Lombok][5] plugin for setting up Eclipse up to +number 5. + + 6. Go to the path where the `lombok.jar` was copied. This path is either found + inside the Eclipse installation directory or in your local settings. Copy + the following file from the project's working directory + + $ cp $USER_HOME/.griffon//projects//plugins/rmi-/dist/griffon-rmi-compile-.jar . + + 6. Edit the launch script for Eclipse and tweak the boothclasspath entry so + that includes the file you just copied + + -Xbootclasspath/a:lombok.jar:lombok-pg-.jar:\ + griffon-lombok-compile-.jar:griffon-rmi-compile-.jar + + 7. Launch Eclipse once more. Eclipse should be able to provide content assist + for Java classes annotated with `@RmiAware`. + +#### NetBeans + +Follow the instructions found in [Annotation Processors Support in the NetBeans +IDE, Part I: Using Project Lombok][6]. You may need to specify +`lombok.core.AnnotationProcessor` in the list of Annotation Processors. + +NetBeans should be able to provide code suggestions on Java classes annotated +with `@RmiAware`. + +#### Intellij IDEA + +Follow the steps found in the [Lombok][5] plugin for setting up Intellij IDEA +up to number 5. + + 6. Copy `griffon-rmi-compile-.jar` to the `lib` directory + + $ pwd + $USER_HOME/Library/Application Support/IntelliJIdea11/lombok-plugin + $ cp $USER_HOME/.griffon//projects//plugins/rmi-/dist/griffon-rmi-compile-.jar lib + + 7. Launch IntelliJ IDEA once more. Code completion should work now for Java + classes annotated with `@RmiAware`. + [1]: http://en.wikipedia.org/wiki/Java_remote_method_invocation [2]: http://grails.org/plugin/remoting [3]: http://grails.org [4]: http://grails.org/Download +[5]: /plugin/lombok +[6]: http://netbeans.org/kb/docs/java/annotations-lombok.html ''' } diff --git a/application.properties b/application.properties index 7e279f8..7044208 100644 --- a/application.properties +++ b/application.properties @@ -1,2 +1,5 @@ -app.griffon.version=0.9.5-rc2 +#Griffon Metadata file +#Thu Jan 10 15:09:38 CET 2013 +app.griffon.version=1.2.0-SNAPSHOT app.name=rmi +plugins.lombok=0.4 diff --git a/griffon-app/conf/BuildConfig.groovy b/griffon-app/conf/BuildConfig.groovy index 4cd35d8..cd5a5e8 100644 --- a/griffon-app/conf/BuildConfig.groovy +++ b/griffon-app/conf/BuildConfig.groovy @@ -1,18 +1,33 @@ griffon.project.dependency.resolution = { - inherits("global") + inherits "global" log "warn" repositories { griffonHome() + mavenCentral() + mavenLocal() } dependencies { - } -} - -griffon { - doc { - logo = 'The Griffon Framework' - sponsorLogo = "
" - footer = "

Made with Griffon (@griffon.version@)" + build('org.eclipse.jdt:org.eclipse.jdt.core:3.6.0.v_A58') { + export = false + } + String lombokIdea = '0.5' + build("de.plushnikov.lombok-intellij-plugin:processor-api:$lombokIdea", + "de.plushnikov.lombok-intellij-plugin:processor-core:$lombokIdea", + "de.plushnikov.lombok-intellij-plugin:intellij-facade-factory:$lombokIdea", + "de.plushnikov.lombok-intellij-plugin:intellij-facade-api:$lombokIdea", + "de.plushnikov.lombok-intellij-plugin:intellij-facade-9:$lombokIdea", + "de.plushnikov.lombok-intellij-plugin:intellij-facade-10:$lombokIdea", + "de.plushnikov.lombok-intellij-plugin:intellij-facade-11:$lombokIdea") { + export = false + transitive = false + } + String ideaVersion = '11.1.4' + build("org.jetbrains.idea:idea-openapi:$ideaVersion", + "org.jetbrains.idea:extensions:$ideaVersion", + "org.jetbrains.idea:util:$ideaVersion", + "org.jetbrains.idea:annotations:$ideaVersion") { + export = false + } } } diff --git a/src/cli/META-INF/services/de.plushnikov.intellij.lombok.processor.LombokProcessor b/src/cli/META-INF/services/de.plushnikov.intellij.lombok.processor.LombokProcessor new file mode 100644 index 0000000..9def10b --- /dev/null +++ b/src/cli/META-INF/services/de.plushnikov.intellij.lombok.processor.LombokProcessor @@ -0,0 +1 @@ +lombok.intellij.processor.clazz.RmiAwareProcessor \ No newline at end of file diff --git a/src/cli/META-INF/services/de.plushnikov.intellij.lombok.processor.clazz.LombokClassProcessor b/src/cli/META-INF/services/de.plushnikov.intellij.lombok.processor.clazz.LombokClassProcessor new file mode 100644 index 0000000..9def10b --- /dev/null +++ b/src/cli/META-INF/services/de.plushnikov.intellij.lombok.processor.clazz.LombokClassProcessor @@ -0,0 +1 @@ +lombok.intellij.processor.clazz.RmiAwareProcessor \ No newline at end of file diff --git a/src/cli/META-INF/services/lombok.eclipse.EclipseAnnotationHandler b/src/cli/META-INF/services/lombok.eclipse.EclipseAnnotationHandler new file mode 100644 index 0000000..9789512 --- /dev/null +++ b/src/cli/META-INF/services/lombok.eclipse.EclipseAnnotationHandler @@ -0,0 +1 @@ +lombok.eclipse.handlers.HandleRmiAware \ No newline at end of file diff --git a/src/cli/META-INF/services/lombok.javac.JavacAnnotationHandler b/src/cli/META-INF/services/lombok.javac.JavacAnnotationHandler new file mode 100644 index 0000000..cd4e9fd --- /dev/null +++ b/src/cli/META-INF/services/lombok.javac.JavacAnnotationHandler @@ -0,0 +1 @@ +lombok.javac.handlers.HandleRmiAware \ No newline at end of file diff --git a/src/cli/dsld/rmi.dsld b/src/cli/dsld/rmi.dsld new file mode 100644 index 0000000..0730e83 --- /dev/null +++ b/src/cli/dsld/rmi.dsld @@ -0,0 +1,26 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dsld + +/** + * @author Andres Almiray + */ + +contribute(enclosingClass(annotatedBy('griffon.plugins.rmi.RmiAware'))) { + provider = "Rmi DSL" + delegatesTo "griffon.plugins.rmi.RmiContributionHandler" +} \ No newline at end of file diff --git a/src/cli/gdsl/rmi.gdsl b/src/cli/gdsl/rmi.gdsl new file mode 100644 index 0000000..83df8d6 --- /dev/null +++ b/src/cli/gdsl/rmi.gdsl @@ -0,0 +1,25 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gdsl + +/** + * @author Andres Almiray + */ + +contributor(scope: annotatedScope(ctype: 'griffon.plugins.rmi.RmiAware')) { + delegatesTo(findClass('griffon.plugins.rmi.RmiContributionHandler')) +} \ No newline at end of file diff --git a/src/cli/griffon/plugins/rmi/RmiAware.java b/src/cli/griffon/plugins/rmi/RmiAware.java new file mode 100644 index 0000000..e47552a --- /dev/null +++ b/src/cli/griffon/plugins/rmi/RmiAware.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package griffon.plugins.rmi; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

Annotates a class.

+ * + * @author Andres Almiray + * @see org.codehaus.griffon.ast.RmiAwareASTTransformation + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +@GroovyASTTransformationClass("org.codehaus.griffon.ast.RmiAwareASTTransformation") +public @interface RmiAware { +} \ No newline at end of file diff --git a/src/cli/lombok/core/handlers/RmiAwareConstants.java b/src/cli/lombok/core/handlers/RmiAwareConstants.java new file mode 100644 index 0000000..270f6e3 --- /dev/null +++ b/src/cli/lombok/core/handlers/RmiAwareConstants.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lombok.core.handlers; + +import lombok.core.BaseConstants; +import lombok.core.util.MethodDescriptor; + +import static lombok.core.util.MethodDescriptor.args; +import static lombok.core.util.MethodDescriptor.type; +import static lombok.core.util.MethodDescriptor.typeParams; + +/** + * @author Andres Almiray + */ +public interface RmiAwareConstants extends BaseConstants { + String RMI_PROVIDER_TYPE = "griffon.plugins.rmi.RmiProvider"; + String DEFAULT_RMI_PROVIDER_TYPE = "griffon.plugins.rmi.DefaultRmiProvider"; + String RMI_CONTRIBUTION_HANDLER_TYPE = "griffon.plugins.rmi.RmiContributionHandler"; + String RMI_PROVIDER_FIELD_NAME = "this$rmiProvider"; + String METHOD_GET_RMI_PROVIDER = "getRmiProvider"; + String METHOD_SET_RMI_PROVIDER = "setRmiProvider"; + String METHOD_WITH_RMI = "withRmi"; + String PROVIDER = "provider"; + + MethodDescriptor[] METHODS = new MethodDescriptor[]{ + MethodDescriptor.method( + type(R), + typeParams(R), + METHOD_WITH_RMI, + args( + type(JAVA_UTIL_MAP, JAVA_LANG_STRING, JAVA_LANG_OBJECT), + type(GROOVY_LANG_CLOSURE, R)) + ), + MethodDescriptor.method( + type(R), + typeParams(R), + METHOD_WITH_RMI, + args( + type(JAVA_UTIL_MAP, JAVA_LANG_STRING, JAVA_LANG_OBJECT), + type(GRIFFON_UTIL_CALLABLEWITHARGS, R)) + ) + }; +} diff --git a/src/cli/lombok/core/handlers/RmiAwareHandler.java b/src/cli/lombok/core/handlers/RmiAwareHandler.java new file mode 100644 index 0000000..0a88cf0 --- /dev/null +++ b/src/cli/lombok/core/handlers/RmiAwareHandler.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lombok.core.handlers; + +import lombok.ast.Expression; +import lombok.ast.IMethod; +import lombok.ast.IType; + +import static lombok.ast.AST.*; + +/** + * @author Andres Almiray + */ +public abstract class RmiAwareHandler, ?, ?, ?, ?, ?>> extends AbstractHandler implements RmiAwareConstants { + private Expression defaultRmiProviderInstance() { + return Call(Name(DEFAULT_RMI_PROVIDER_TYPE), "getInstance"); + } + + public void addRmiProviderField(final TYPE_TYPE type) { + addField(type, RMI_PROVIDER_TYPE, RMI_PROVIDER_FIELD_NAME, defaultRmiProviderInstance()); + } + + public void addRmiProviderAccessors(final TYPE_TYPE type) { + type.editor().injectMethod( + MethodDecl(Type(VOID), METHOD_SET_RMI_PROVIDER) + .makePublic() + .withArgument(Arg(Type(RMI_PROVIDER_TYPE), PROVIDER)) + .withStatement( + If(Equal(Name(PROVIDER), Null())) + .Then(Block() + .withStatement(Assign(Field(RMI_PROVIDER_FIELD_NAME), defaultRmiProviderInstance()))) + .Else(Block() + .withStatement(Assign(Field(RMI_PROVIDER_FIELD_NAME), Name(PROVIDER))))) + ); + + type.editor().injectMethod( + MethodDecl(Type(RMI_PROVIDER_TYPE), METHOD_GET_RMI_PROVIDER) + .makePublic() + .withStatement(Return(Field(RMI_PROVIDER_FIELD_NAME))) + ); + } + + public void addRmiContributionMethods(final TYPE_TYPE type) { + delegateMethodsTo(type, METHODS, Field(RMI_PROVIDER_FIELD_NAME)); + } +} diff --git a/src/cli/lombok/eclipse/handlers/HandleRmiAware.java b/src/cli/lombok/eclipse/handlers/HandleRmiAware.java new file mode 100644 index 0000000..35a0dbb --- /dev/null +++ b/src/cli/lombok/eclipse/handlers/HandleRmiAware.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lombok.eclipse.handlers; + +import griffon.plugins.rmi.RmiAware; +import lombok.core.AnnotationValues; +import lombok.core.handlers.RmiAwareConstants; +import lombok.core.handlers.RmiAwareHandler; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.ast.EclipseType; +import org.eclipse.jdt.internal.compiler.ast.Annotation; + +import static lombok.core.util.ErrorMessages.canBeUsedOnClassAndEnumOnly; + +/** + * @author Andres Almiray + */ +public class HandleRmiAware extends EclipseAnnotationHandler { + private final EclipseRmiAwareHandler handler = new EclipseRmiAwareHandler(); + + @Override + public void handle(AnnotationValues annotation, Annotation source, EclipseNode annotationNode) { + EclipseType type = EclipseType.typeOf(annotationNode, source); + if (type.isAnnotation() || type.isInterface()) { + annotationNode.addError(canBeUsedOnClassAndEnumOnly(RmiAware.class)); + return; + } + + EclipseUtil.addInterface(type.get(), RmiAwareConstants.RMI_CONTRIBUTION_HANDLER_TYPE, source); + handler.addRmiProviderField(type); + handler.addRmiProviderAccessors(type); + handler.addRmiContributionMethods(type); + type.editor().rebuild(); + } + + private static class EclipseRmiAwareHandler extends RmiAwareHandler { + } +} diff --git a/src/cli/lombok/intellij/processor/clazz/RmiAwareProcessor.java b/src/cli/lombok/intellij/processor/clazz/RmiAwareProcessor.java new file mode 100644 index 0000000..6206146 --- /dev/null +++ b/src/cli/lombok/intellij/processor/clazz/RmiAwareProcessor.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lombok.intellij.processor.clazz; + +import com.intellij.psi.*; +import de.plushnikov.intellij.lombok.psi.LombokLightFieldBuilder; +import de.plushnikov.intellij.lombok.psi.LombokLightMethodBuilder; +import de.plushnikov.intellij.lombok.psi.LombokPsiElementFactory; +import de.plushnikov.intellij.lombok.util.PsiMethodUtil; +import de.plushnikov.intellij.lombok.util.PsiPrimitiveTypeFactory; +import griffon.plugins.rmi.RmiAware; +import lombok.core.handlers.RmiAwareConstants; +import lombok.core.util.MethodDescriptor; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Andres Almiray + */ +public class RmiAwareProcessor extends AbstractGriffonLombokClassProcessor implements RmiAwareConstants { + private static final String RMI_PROVIDER_FIELD_INITIALIZER = DEFAULT_RMI_PROVIDER_TYPE + ".getInstance()"; + + public RmiAwareProcessor() { + super(RmiAware.class, PsiMethod.class); + } + + protected void processIntern(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List target) { + PsiElementFactory psiElementFactory = psiElementFactory(psiClass); + PsiManager manager = psiClass.getContainingFile().getManager(); + + PsiType psiProviderType = psiElementFactory.createTypeFromText(RMI_PROVIDER_TYPE, psiClass); + LombokLightFieldBuilder providerField = LombokPsiElementFactory.getInstance().createLightField(manager, RMI_PROVIDER_FIELD_NAME, psiProviderType) + .withContainingClass(psiClass) + .withModifier(PsiModifier.PRIVATE) + .withNavigationElement(psiAnnotation); + PsiExpression initializer = psiElementFactory.createExpressionFromText(String.format(RMI_PROVIDER_FIELD_INITIALIZER), psiClass); + providerField.setInitializer(initializer); + + LombokLightMethodBuilder method = LombokPsiElementFactory.getInstance().createLightMethod(psiClass.getManager(), METHOD_GET_RMI_PROVIDER) + .withMethodReturnType(psiProviderType) + .withContainingClass(psiClass) + .withModifier(PsiModifier.PUBLIC) + .withNavigationElement(psiAnnotation); + target.add((Psi) method); + + method = LombokPsiElementFactory.getInstance().createLightMethod(psiClass.getManager(), METHOD_SET_RMI_PROVIDER) + .withMethodReturnType(PsiPrimitiveTypeFactory.getInstance().getVoidType()) + .withContainingClass(psiClass) + .withParameter(PROVIDER, psiProviderType) + .withModifier(PsiModifier.PUBLIC) + .withNavigationElement(psiAnnotation); + target.add((Psi) method); + + for (MethodDescriptor methodDesc : METHODS) { + target.add((Psi) PsiMethodUtil.createMethod(psiClass, methodDesc.signature, psiAnnotation)); + } + } +} diff --git a/src/cli/lombok/javac/handlers/HandleRmiAware.java b/src/cli/lombok/javac/handlers/HandleRmiAware.java new file mode 100644 index 0000000..eaeedd4 --- /dev/null +++ b/src/cli/lombok/javac/handlers/HandleRmiAware.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lombok.javac.handlers; + +import com.sun.tools.javac.tree.JCTree; +import griffon.plugins.rmi.RmiAware; +import lombok.core.AnnotationValues; +import lombok.core.handlers.RmiAwareConstants; +import lombok.core.handlers.RmiAwareHandler; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.handlers.ast.JavacType; + +import static lombok.core.util.ErrorMessages.canBeUsedOnClassAndEnumOnly; +import static lombok.javac.handlers.JavacHandlerUtil.deleteAnnotationIfNeccessary; + +/** + * @author Andres Almiray + */ +public class HandleRmiAware extends JavacAnnotationHandler { + private final JavacRmiAwareHandler handler = new JavacRmiAwareHandler(); + + @Override + public void handle(final AnnotationValues annotation, final JCTree.JCAnnotation source, final JavacNode annotationNode) { + deleteAnnotationIfNeccessary(annotationNode, RmiAware.class); + + JavacType type = JavacType.typeOf(annotationNode, source); + if (type.isAnnotation() || type.isInterface()) { + annotationNode.addError(canBeUsedOnClassAndEnumOnly(RmiAware.class)); + return; + } + + JavacUtil.addInterface(type.node(), RmiAwareConstants.RMI_CONTRIBUTION_HANDLER_TYPE); + handler.addRmiProviderField(type); + handler.addRmiProviderAccessors(type); + handler.addRmiContributionMethods(type); + type.editor().rebuild(); + } + + private static class JavacRmiAwareHandler extends RmiAwareHandler { + } +} diff --git a/src/cli/org/codehaus/griffon/ast/RmiAwareASTTransformation.java b/src/cli/org/codehaus/griffon/ast/RmiAwareASTTransformation.java new file mode 100644 index 0000000..3fdf5de --- /dev/null +++ b/src/cli/org/codehaus/griffon/ast/RmiAwareASTTransformation.java @@ -0,0 +1,201 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.codehaus.griffon.ast; + +import griffon.plugins.rmi.DefaultRmiProvider; +import griffon.plugins.rmi.RmiAware; +import griffon.plugins.rmi.RmiContributionHandler; +import griffon.plugins.rmi.RmiProvider; +import lombok.core.handlers.RmiAwareConstants; +import org.codehaus.groovy.ast.*; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SimpleMessage; +import org.codehaus.groovy.transform.GroovyASTTransformation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.codehaus.griffon.ast.GriffonASTUtils.*; + +/** + * Handles generation of code for the {@code @RmiAware} annotation. + *

+ * + * @author Andres Almiray + */ +@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) +public class RmiAwareASTTransformation extends AbstractASTTransformation implements RmiAwareConstants { + private static final Logger LOG = LoggerFactory.getLogger(RmiAwareASTTransformation.class); + private static final ClassNode RMI_CONTRIBUTION_HANDLER_CNODE = makeClassSafe(RmiContributionHandler.class); + private static final ClassNode RMI_AWARE_CNODE = makeClassSafe(RmiAware.class); + private static final ClassNode RMI_PROVIDER_CNODE = makeClassSafe(RmiProvider.class); + private static final ClassNode DEFAULT_RMI_PROVIDER_CNODE = makeClassSafe(DefaultRmiProvider.class); + + private static final String[] DELEGATING_METHODS = new String[] { + METHOD_WITH_RMI + }; + + static { + Arrays.sort(DELEGATING_METHODS); + } + + /** + * Convenience method to see if an annotated node is {@code @RmiAware}. + * + * @param node the node to check + * @return true if the node is an event publisher + */ + public static boolean hasRmiAwareAnnotation(AnnotatedNode node) { + for (AnnotationNode annotation : node.getAnnotations()) { + if (RMI_AWARE_CNODE.equals(annotation.getClassNode())) { + return true; + } + } + return false; + } + + /** + * Handles the bulk of the processing, mostly delegating to other methods. + * + * @param nodes the ast nodes + * @param source the source unit for the nodes + */ + public void visit(ASTNode[] nodes, SourceUnit source) { + checkNodesForAnnotationAndType(nodes[0], nodes[1]); + addRmiContributionIfNeeded(source, (ClassNode) nodes[1]); + } + + public static void addRmiContributionIfNeeded(SourceUnit source, ClassNode classNode) { + if (needsRmiContribution(classNode, source)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Injecting " + RmiContributionHandler.class.getName() + " into " + classNode.getName()); + } + apply(classNode); + } + } + + protected static boolean needsRmiContribution(ClassNode declaringClass, SourceUnit sourceUnit) { + boolean found1 = false, found2 = false, found3 = false; + ClassNode consideredClass = declaringClass; + while (consideredClass != null) { + for (MethodNode method : consideredClass.getMethods()) { + // just check length, MOP will match it up + found1 = method.getName().equals(METHOD_WITH_RMI) && method.getParameters().length == 2; + found2 = method.getName().equals(METHOD_SET_RMI_PROVIDER) && method.getParameters().length == 1; + found3 = method.getName().equals(METHOD_GET_RMI_PROVIDER) && method.getParameters().length == 0; + if (found1 && found2 && found3) { + return false; + } + } + consideredClass = consideredClass.getSuperClass(); + } + if (found1 || found2 || found3) { + sourceUnit.getErrorCollector().addErrorAndContinue( + new SimpleMessage("@RmiAware cannot be processed on " + + declaringClass.getName() + + " because some but not all of methods from " + RmiContributionHandler.class.getName() + " were declared in the current class or super classes.", + sourceUnit) + ); + return false; + } + return true; + } + + public static void apply(ClassNode declaringClass) { + injectInterface(declaringClass, RMI_CONTRIBUTION_HANDLER_CNODE); + + // add field: + // protected RmiProvider this$rmiProvider = DefaultRmiProvider.instance + FieldNode providerField = declaringClass.addField( + RMI_PROVIDER_FIELD_NAME, + ACC_PRIVATE | ACC_SYNTHETIC, + RMI_PROVIDER_CNODE, + defaultRmiProviderInstance()); + + // add method: + // RmiProvider getRmiProvider() { + // return this$rmiProvider + // } + injectMethod(declaringClass, new MethodNode( + METHOD_GET_RMI_PROVIDER, + ACC_PUBLIC, + RMI_PROVIDER_CNODE, + Parameter.EMPTY_ARRAY, + NO_EXCEPTIONS, + returns(field(providerField)) + )); + + // add method: + // void setRmiProvider(RmiProvider provider) { + // this$rmiProvider = provider ?: RmiClientHolder.instance + // } + injectMethod(declaringClass, new MethodNode( + METHOD_SET_RMI_PROVIDER, + ACC_PUBLIC, + ClassHelper.VOID_TYPE, + params( + param(RMI_PROVIDER_CNODE, PROVIDER)), + NO_EXCEPTIONS, + block( + ifs_no_return( + cmp(var(PROVIDER), ConstantExpression.NULL), + assigns(field(providerField), defaultRmiProviderInstance()), + assigns(field(providerField), var(PROVIDER)) + ) + ) + )); + + for (MethodNode method : RMI_CONTRIBUTION_HANDLER_CNODE.getMethods()) { + if (Arrays.binarySearch(DELEGATING_METHODS, method.getName()) < 0) continue; + List variables = new ArrayList(); + Parameter[] parameters = new Parameter[method.getParameters().length]; + for (int i = 0; i < method.getParameters().length; i++) { + Parameter p = method.getParameters()[i]; + parameters[i] = new Parameter(makeClassSafe(p.getType()), p.getName()); + parameters[i].getType().setGenericsTypes(p.getType().getGenericsTypes()); + variables.add(var(p.getName())); + } + ClassNode returnType = makeClassSafe(method.getReturnType()); + returnType.setGenericsTypes(method.getReturnType().getGenericsTypes()); + returnType.setGenericsPlaceHolder(method.getReturnType().isGenericsPlaceHolder()); + + MethodNode newMethod = new MethodNode( + method.getName(), + ACC_PUBLIC, + returnType, + parameters, + NO_EXCEPTIONS, + returns(call( + field(providerField), + method.getName(), + args(variables))) + ); + newMethod.setGenericsTypes(method.getGenericsTypes()); + injectMethod(declaringClass, newMethod); + } + } + + private static Expression defaultRmiProviderInstance() { + return call(DEFAULT_RMI_PROVIDER_CNODE, "getInstance", NO_ARGS); + } +} diff --git a/src/main/griffon/plugins/rmi/AbstractRmiProvider.java b/src/main/griffon/plugins/rmi/AbstractRmiProvider.java new file mode 100644 index 0000000..94cdc00 --- /dev/null +++ b/src/main/griffon/plugins/rmi/AbstractRmiProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package griffon.plugins.rmi; + +import griffon.util.CallableWithArgs; +import groovy.lang.Closure; + +import java.util.Map; + +/** + * @author Andres Almiray + */ +public abstract class AbstractRmiProvider implements RmiProvider { + public R withRmi(Map params, Closure closure) { + if (closure != null) { + closure.setDelegate(getRmiClient(params)); + closure.setResolveStrategy(Closure.DELEGATE_FIRST); + return closure.call(); + } + return null; + } + + public R withRmi(Map params, CallableWithArgs callable) { + if (callable != null) { + callable.setArgs(new Object[]{getRmiClient(params)}); + return callable.call(); + } + return null; + } + + protected abstract RmiClient getRmiClient(Map params); +} diff --git a/src/main/griffon/plugins/rmi/DefaultRmiProvider.java b/src/main/griffon/plugins/rmi/DefaultRmiProvider.java new file mode 100644 index 0000000..c634739 --- /dev/null +++ b/src/main/griffon/plugins/rmi/DefaultRmiProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package griffon.plugins.rmi; + +import java.util.Map; + +/** + * @author Andres Almiray + */ +public class DefaultRmiProvider extends AbstractRmiProvider { + private static final DefaultRmiProvider INSTANCE; + + static { + INSTANCE = new DefaultRmiProvider(); + } + + public static DefaultRmiProvider getInstance() { + return INSTANCE; + } + + @Override + protected RmiClient getRmiClient(Map params) { + return RmiClientHolder.getInstance().fetchRmiClient(params); + } +} diff --git a/src/main/griffon/plugins/rmi/RMIClient.groovy b/src/main/griffon/plugins/rmi/RMIClient.groovy index ddb73bf..745b38f 100644 --- a/src/main/griffon/plugins/rmi/RMIClient.groovy +++ b/src/main/griffon/plugins/rmi/RMIClient.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 the original author or authors. + * Copyright 2009-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,13 @@ import java.rmi.registry.LocateRegistry /** * @author Andres Almiray */ -class RMIClient { +class RmiClient { private final Map services = [:] private final String host private final int port private Registry registry - RMIClient(String host, int port, boolean lazy) { + RmiClient(String host, int port, boolean lazy) { this.host = host this.port = port if(!lazy) { diff --git a/src/main/griffon/plugins/rmi/RmiClientHolder.groovy b/src/main/griffon/plugins/rmi/RmiClientHolder.groovy new file mode 100644 index 0000000..537a64e --- /dev/null +++ b/src/main/griffon/plugins/rmi/RmiClientHolder.groovy @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package griffon.plugins.rmi + +import java.util.concurrent.ConcurrentHashMap + +/** + * @author Andres Almiray + */ +class RmiClientHolder { + private static final RmiClientHolder INSTANCE + + static { + INSTANCE = new RmiClientHolder() + } + + static RmiClientHolder getInstance() { + INSTANCE + } + + private final Map CLIENTS = new ConcurrentHashMap() + + private RmiClientHolder() { + + } + + String[] getRmiClientIds() { + List ids = [] + ids.addAll(CLIENTS.keySet()) + ids.toArray(new String[ids.size()]) + } + + RmiClient getRmiClient(String id) { + CLIENTS[id] + } + + void setRmiClient(String id, RmiClient client) { + CLIENTS[id] = client + } + + // ====================================================== + + RmiClient fetchRmiClient(Map params) { + RmiClient client = CLIENTS[(params.id).toString()] + if (client == null) { + String id = params.id ? params.remove('id').toString() : '' + client = RmiConnector.instance.createClient(params) + if (id != '') CLIENTS[id] = client + } + client + } +} diff --git a/src/main/griffon/plugins/rmi/RmiConnector.groovy b/src/main/griffon/plugins/rmi/RmiConnector.groovy index 3ba38ff..99857c2 100644 --- a/src/main/griffon/plugins/rmi/RmiConnector.groovy +++ b/src/main/griffon/plugins/rmi/RmiConnector.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 the original author or authors. + * Copyright 2012-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,68 +16,17 @@ package griffon.plugins.rmi -import griffon.util.CallableWithArgs -import java.util.concurrent.ConcurrentHashMap - /** * @author Andres Almiray */ @Singleton -class RmiConnector implements RmiProvider { - private final Map CLIENTS = new ConcurrentHashMap() - - Object withRmi(Map params, Closure closure) { - return doWithClient(params, closure) - } - - public T withRmi(Map params, CallableWithArgs callable) { - return doWithClient(params, callable) - } - - // ====================================================== - - private doWithClient(Map params, Closure closure) { - def client = configureClient(params) - - if (closure) { - closure.delegate = client - closure.resolveStrategy = Closure.DELEGATE_FIRST - return closure() - } - return null - } - - private T doWithClient(Map params, CallableWithArgs callable) { - def client = configureClient(params) - - if (callable) { - callable.args = [client] as Object[] - return callable.run() - } - return null - } - - private configureClient(Map params) { - def client = null - if (params.id) { - String id = params.remove('id').toString() - client = CLIENTS[id] - if(client == null) { - client = makeClient(params) - CLIENTS[id] = client - } - } else { - client = makeClient(params) - } - client - } - - private makeClient(Map params) { +class RmiConnector { + public RmiClient createClient(Map params) { def host = params.remove('host') ?: 'localhost' def port = params.remove('port') ?: 1199 def lazy = params.remove('lazy') ?: true try { - return new RMIClient(host, port as int, lazy) + return new RmiClient(host, port as int, lazy) } catch(Exception e) { throw new RuntimeException("Failed to create RMI client, reason: $e", e) } diff --git a/src/main/griffon/plugins/rmi/RmiContributionAdapter.java b/src/main/griffon/plugins/rmi/RmiContributionAdapter.java new file mode 100644 index 0000000..3780c0b --- /dev/null +++ b/src/main/griffon/plugins/rmi/RmiContributionAdapter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package griffon.plugins.rmi; + +import griffon.util.CallableWithArgs; +import groovy.lang.Closure; + +import java.util.Map; + +/** + * @author Andres Almiray + */ +public class RmiContributionAdapter implements RmiContributionHandler { + private RmiProvider provider = DefaultRmiProvider.getInstance(); + + public void setRmiProvider(RmiProvider provider) { + this.provider = provider != null ? provider : DefaultRmiProvider.getInstance(); + } + + public RmiProvider getRmiProvider() { + return provider; + } + + public R withRmi(Map params, Closure closure) { + return provider.withRmi(params, closure); + } + + public R withRmi(Map params, CallableWithArgs callable) { + return provider.withRmi(params, callable); + } +} diff --git a/src/main/griffon/plugins/rmi/RmiContributionHandler.java b/src/main/griffon/plugins/rmi/RmiContributionHandler.java new file mode 100644 index 0000000..a236e75 --- /dev/null +++ b/src/main/griffon/plugins/rmi/RmiContributionHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package griffon.plugins.rmi; + +import griffon.util.CallableWithArgs; +import groovy.lang.Closure; + +import java.util.Map; + +/** + * @author Andres Almiray + */ +public interface RmiContributionHandler { + void setRmiProvider(RmiProvider provider); + + RmiProvider getRmiProvider(); + + R withRmi(Map params, Closure closure); + + R withRmi(Map params, CallableWithArgs callable); +} diff --git a/src/main/griffon/plugins/rmi/RmiEnhancer.groovy b/src/main/griffon/plugins/rmi/RmiEnhancer.groovy index 1f5959b..93a23d4 100644 --- a/src/main/griffon/plugins/rmi/RmiEnhancer.groovy +++ b/src/main/griffon/plugins/rmi/RmiEnhancer.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package griffon.plugins.rmi import griffon.util.CallableWithArgs +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * @author Andres Almiray */ final class RmiEnhancer { + private static final Logger LOG = LoggerFactory.getLogger(RmiEnhancer) + private RmiEnhancer() {} - static void enhance(MetaClass mc, RmiProvider provider = RmiConnector.instance) { + static void enhance(MetaClass mc, RmiProvider provider = DefaultRmiProvider.instance) { + if(LOG.debugEnabled) LOG.debug("Enhancing $mc with $provider") mc.withRmi = {Map params, Closure closure -> provider.withRmi(params, closure) } diff --git a/src/main/griffon/plugins/rmi/RmiProvider.java b/src/main/griffon/plugins/rmi/RmiProvider.java index 5c532f5..f2755a2 100644 --- a/src/main/griffon/plugins/rmi/RmiProvider.java +++ b/src/main/griffon/plugins/rmi/RmiProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package griffon.plugins.rmi; -import java.util.Map; -import groovy.lang.Closure; import griffon.util.CallableWithArgs; +import groovy.lang.Closure; + +import java.util.Map; /** * @author Andres Almiray */ public interface RmiProvider { - Object withRmi(Map params, Closure closure); - - T withRmi(Map params, CallableWithArgs callable); + R withRmi(Map params, Closure closure); + + R withRmi(Map params, CallableWithArgs callable); }