@@ -112,9 +112,11 @@ public String varName() {
112112 }
113113
114114 /**
115- * Calls a method on the remote object.
115+ * Calls a method on the remote object by name .
116116 * <p>
117117 * This is a blocking operation that waits for the remote method to complete.
118+ * Uses {@link ScriptSyntax#invokeMethod(String, String, java.util.List)} to
119+ * generate the method invocation script.
118120 * </p>
119121 *
120122 * @param methodName The name of the method to invoke.
@@ -144,6 +146,106 @@ public Object call(String methodName, Object... args) throws InterruptedExceptio
144146 return task .result ();
145147 }
146148
149+ /**
150+ * Invokes this remote object as a callable (function, method reference, or closure).
151+ * <p>
152+ * This is used when this WorkerObject represents a callable object, such as:
153+ * </p>
154+ * <ul>
155+ * <li>A method reference obtained via {@link #getAttribute(String)}</li>
156+ * <li>A function or closure</li>
157+ * <li>Any object with a {@code __call__} method (Python) or {@code call} method (Groovy)</li>
158+ * </ul>
159+ * <p>
160+ * This is a blocking operation that waits for the remote invocation to complete.
161+ * Uses {@link ScriptSyntax#call(String, java.util.List)} to generate the call script.
162+ * </p>
163+ * <p>
164+ * Example:
165+ * </p>
166+ * <pre>
167+ * WorkerObject obj = ...;
168+ * WorkerObject methodRef = (WorkerObject) obj.getAttribute("compute");
169+ * Object result = methodRef.invoke(arg1, arg2); // Invokes the method reference
170+ * </pre>
171+ *
172+ * @param args The arguments to pass to the callable.
173+ * @return The result of invoking the remote object.
174+ * @throws InterruptedException If the calling thread is interrupted while waiting.
175+ * @throws TaskException If the invocation fails in the worker process.
176+ */
177+ public Object invoke (Object ... args ) throws InterruptedException , TaskException {
178+ // Build the call script using the service's syntax.
179+ java .util .Map <String , Object > inputs = new java .util .HashMap <>();
180+ java .util .List <String > argNames = new java .util .ArrayList <>();
181+ for (int i = 0 ; i < args .length ; i ++) {
182+ String argName = "arg" + i ;
183+ inputs .put (argName , args [i ]);
184+ argNames .add (argName );
185+ }
186+
187+ org .apposed .appose .syntax .Syntaxes .validate (service );
188+ String script = service .syntax ().call (varName , argNames );
189+
190+ Service .Task task = service .task (script , inputs , queue );
191+ task .waitFor ();
192+ if (task .status != Service .TaskStatus .COMPLETE ) {
193+ throw new TaskException ("Task failed: " + task .error , task );
194+ }
195+ return task .result ();
196+ }
197+
198+ /**
199+ * Retrieves an attribute from the remote object.
200+ * <p>
201+ * The behavior depends on the worker language:
202+ * </p>
203+ * <ul>
204+ * <li><strong>Python:</strong> Returns the attribute value (field) or bound method object.</li>
205+ * <li><strong>Groovy:</strong> Tries field access first; if no such field exists,
206+ * returns a method reference (closure).</li>
207+ * </ul>
208+ * <p>
209+ * If the result is a method reference or other non-JSON-serializable object,
210+ * it will be auto-proxied as a {@code WorkerObject} that can be called using
211+ * {@link #call(Object...)}.
212+ * </p>
213+ * <p>
214+ * This is a blocking operation that waits for the remote operation to complete.
215+ * Uses {@link ScriptSyntax#getAttribute(String, String)} to generate the attribute
216+ * access script.
217+ * </p>
218+ * <p>
219+ * Example:
220+ * </p>
221+ * <pre>
222+ * WorkerObject obj = ...;
223+ *
224+ * // Field access - returns value directly
225+ * String label = (String) obj.getAttribute("label");
226+ *
227+ * // Method reference - returns WorkerObject that can be invoked
228+ * WorkerObject methodRef = (WorkerObject) obj.getAttribute("compute");
229+ * Object result = methodRef.invoke(arg1, arg2);
230+ * </pre>
231+ *
232+ * @param attributeName The name of the attribute to retrieve.
233+ * @return The attribute value (field) or a WorkerObject (method reference).
234+ * @throws InterruptedException If the calling thread is interrupted while waiting.
235+ * @throws TaskException If the attribute access fails in the worker process.
236+ */
237+ public Object getAttribute (String attributeName ) throws InterruptedException , TaskException {
238+ org .apposed .appose .syntax .Syntaxes .validate (service );
239+ String script = service .syntax ().getAttribute (varName , attributeName );
240+
241+ Service .Task task = service .task (script , queue );
242+ task .waitFor ();
243+ if (task .status != Service .TaskStatus .COMPLETE ) {
244+ throw new TaskException ("Task failed: " + task .error , task );
245+ }
246+ return task .result ();
247+ }
248+
147249 /**
148250 * Creates a strongly-typed proxy for this remote object.
149251 * <p>
0 commit comments