001    /*
002      Copyright (C) 2002-2004 Renaud Pawlak <renaud@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.aspects.gui;
020    
021    import java.util.Arrays;
022    import java.util.Iterator;
023    import java.util.List;
024    import org.apache.log4j.Logger;
025    import org.objectweb.jac.core.Collaboration;
026    import org.objectweb.jac.core.rtti.AbstractMethodItem;
027    import org.objectweb.jac.core.rtti.ConstructorItem;
028    import org.objectweb.jac.core.rtti.MethodItem;
029    import org.objectweb.jac.util.Exceptions;
030    
031    /**
032     * This class allows the programmer to invoke a given method in a new
033     * thread.
034     *
035     * <p>JAC programmers should use JAC to pass some attibutes of the
036     * current thread to the new thread.
037     *
038     * <p>Typical use:
039     * <ul><pre>
040     * InvokeThread.run( myObject, "myMethodName",
041     *                   new Object[] { arg0, ...},
042     *                   new String[] { attrName0, ... },
043     *                   new Object[] { attrValue0, ... },
044     *                   new String[] { lattrName0, ... },
045     *                   new Object[] { lattrValue0, ... } );
046     * </pre></ul> */
047    
048    public class InvokeThread extends Thread {
049        static Logger logger = Logger.getLogger("gui.threads");
050        static Logger loggerInput = Logger.getLogger("gui.input");
051    
052        InvokeEvent invoke;
053        Object returnValue;
054        String[] attrNames;
055        Object[] attrValues; 
056        String[] lattrNames;
057        Object[] lattrValues;
058        Collaboration parentCollaboration;
059        boolean showResult = true;
060    
061        /**
062         * Runs a method in a new thread and sets a display for this
063         * thread.
064         *
065         * @param object the object that contains the method to call
066         * @param method the method to call
067         * @param parameters the parameters to pass to the method
068         * @param attrNames the attribute names to set into the new thread
069         * collaboration
070         * @param attrValues the values of these attributes
071         * @param lattrNames the local attribute names to set into the new
072         * thread collaboration
073         * @param lattrValues the values of these local attributes */
074    
075    
076        public static InvokeThread run(InvokeEvent invoke,                                   
077                                       String[] attrNames, Object[] attrValues,
078                                       String[] lattrNames, Object[] lattrValues) {
079            InvokeThread ret = new InvokeThread(invoke);
080            ret.parentCollaboration = Collaboration.get();
081            ret.attrNames = attrNames;
082            ret.attrValues = attrValues;
083            ret.lattrNames = lattrNames;
084            ret.lattrValues = lattrValues;
085            ret.start();
086            return ret;
087        }
088    
089        /**
090         * Runs a method in a new thread and sets a display for this
091         * thread. Do not show any results on the display.
092         *
093         * @param object the object that contains the method to call
094         * @param method the method to call
095         * @param parameters the parameters to pass to the method
096         * @param attrNames the attribute names to set into the new thread
097         * collaboration
098         * @param attrValues the values of these attributes
099         * @param lattrNames the local attribute names to set into the new
100         * thread collaboration
101         * @param lattrValues the values of these local attributes */
102    
103        public static 
104        InvokeThread quietRun(InvokeEvent invoke,
105                              String[] attrNames, Object[] attrValues,
106                              String[] lattrNames, Object[] lattrValues) 
107        {
108            InvokeThread ret = new InvokeThread(invoke);
109            ret.parentCollaboration = Collaboration.get();
110            ret.attrNames = attrNames;
111            ret.attrValues = attrValues;
112            ret.lattrNames = lattrNames;
113            ret.lattrValues = lattrValues;
114            //        ret.showResult = false;
115            ret.start();
116            return ret;
117        }
118    
119        /**
120         * Runs a method in a new thread.
121         *
122         * @param object the object that contains the method to call
123         * @param method the method to call
124         * @param parameters the parameters to pass to the method */
125    
126        public static InvokeThread run(InvokeEvent invoke) {
127            InvokeThread ret = new InvokeThread(invoke);
128            ret.start();
129            return ret;
130        }
131    
132        /**
133         * Creates a new thread that will invoke a method when started.
134         *
135         * <p>The programmer should use the static <code>run</code>
136         * methods.
137         *
138         * @param object the object that contains the method to invoke
139         * @param method the method to invoke when started
140         * @param parameters the parameters to pass to the method */
141    
142        public InvokeThread(InvokeEvent invoke) {
143            this.invoke = invoke;
144        }
145    
146    
147        public InvokeThread(InvokeEvent invoke,
148                            String[] attrNames, Object[] attrValues,
149                            String[] lattrNames, Object[] lattrValues) {
150            this.invoke = invoke;
151            this.parentCollaboration = Collaboration.get();
152            this.attrNames = attrNames;
153            this.attrValues = attrValues;
154            this.lattrNames = lattrNames;
155            this.lattrValues = lattrValues;
156            this.showResult = false;
157        }
158    
159        /**
160         * Runs the thread (and invoke the method that was given to the
161         * constructor with the right display in the collaboration).
162         *
163         * <p>Do not call this method directly. 
164         */
165        public void run() {
166            Collaboration collab = Collaboration.get();
167            Object[] parameters = invoke.getParameters();
168            AbstractMethodItem method = invoke.getMethod();
169            Object substance = invoke.getSubstance();
170    
171            logger.debug("invokeThread "+this+": "+invoke);
172            if (parentCollaboration!=null) {
173                Iterator it = parentCollaboration.attributeNames().iterator();
174                while (it.hasNext()) {
175                    String attrName = (String)it.next();
176                    collab.addAttribute(
177                        attrName,parentCollaboration.getAttribute(attrName));
178                }         
179                logger.debug("application = "+parentCollaboration.getCurApp());
180                collab.setCurApp(parentCollaboration.getCurApp());
181            }
182            if (attrNames != null) {
183                for (int i=0; i<attrNames.length; i++) {
184                    logger.debug("setting attribute " + attrNames[i] +
185                              " to " + attrValues[i]);
186                    collab.addAttribute( 
187                        attrNames[i], attrValues[i]);
188                }
189            }
190            if (lattrNames != null) {
191                for (int i=0; i<lattrNames.length; i++) {
192                    logger.debug("setting local attribute " + 
193                              lattrNames[i] + " to " + lattrValues[i]);
194                    collab.addAttribute( 
195                        lattrNames[i], lattrValues[i]);
196                }
197            }
198            DisplayContext context = (DisplayContext)collab
199                .getAttribute(GuiAC.DISPLAY_CONTEXT);
200    
201            CustomizedDisplay display = context.getDisplay();
202            try {
203                logger.debug("InvokeThread " + invoke);
204             
205                Class[] paramTypes = method.getParameterTypes();
206                if (paramTypes.length != invoke.getParameters().length) 
207                    throw new RuntimeException("Wrong number of parameters ("+
208                                               parameters.length+") for "+method);
209                for (int i=0; i<parameters.length; i++) {
210                    if (parameters[i]==null) {
211                        if (paramTypes[i] == float.class) {
212                            method.setParameter(parameters,i,new Float(0.0));
213                        } else if (paramTypes[i] == long.class) {
214                            method.setParameter(parameters,i,new Long(0));
215                        } else if (paramTypes[i] == double.class) {
216                            method.setParameter(parameters,i,new Double(0.0));
217                        } else if (paramTypes[i] == byte.class) {
218                            method.setParameter(parameters,i,new Byte((byte)0));
219                        } else if (paramTypes[i] == char.class) {
220                            method.setParameter(parameters,i,new Character(' '));
221                        } else if (paramTypes[i] == short.class) {
222                            method.setParameter(parameters,i,new Short((short)0));
223                        } else if (paramTypes[i] == int.class) {
224                            method.setParameter(parameters,i,new Integer(0));
225                        } else if (paramTypes[i] == boolean.class) {
226                            method.setParameter(parameters,i,Boolean.FALSE);
227                        }
228                    }
229                }
230    
231                if (method instanceof ConstructorItem) {
232                    returnValue = ((ConstructorItem)method).newInstance(parameters);
233                } else {
234                    returnValue = ((MethodItem)method).invoke(substance, parameters);
235                }
236    
237                if (display != null) {
238                    display.onInvocationReturn(substance,method);
239                }
240                List hooks = (List)method.getAttribute(GuiAC.POST_INVOKE_HOOKS);
241                if (hooks!=null) {
242                    Iterator i = hooks.iterator();
243                    while (i.hasNext()) {
244                        AbstractMethodItem hook = (AbstractMethodItem)i.next();
245                        try {
246                            loggerInput.debug("Invoking post hook "+hook.getName());
247                            hook.invoke(
248                                null,
249                                new Object[] {invoke});
250                        } catch (Exception e) {
251                            loggerInput.error("Post invoke hook for "+
252                                              substance+"."+
253                                              method.getFullName()+" failed",e);
254                        }
255                    }
256                }
257    
258                if (method.getType() != void.class) {
259                    if (display != null && showResult) {
260    
261                        if (collab.getAttribute(GuiAC.OPEN_VIEW)!=null) {
262                            display.openView(returnValue);
263                        } else {
264                            if(method.getAttribute(GuiAC.SMALL_TARGET_CONTAINER)!=null) {
265                                EventHandler.get().onSelection(context,method,returnValue,null,null,false);
266                            } else if (returnValue instanceof HandlerResult) {
267                                EventHandler.get().handleResult(context,(HandlerResult)returnValue);
268                            } else {
269                                display.show(returnValue);
270                            }
271                        }
272                    }
273                } else {
274                    if (display != null && showResult) 
275                        display.refresh();
276                }
277            } catch (Exception e) {
278                Throwable te = Exceptions.getTargetException(e);
279                logger.debug(this+" TargetException is "+te);
280                if (te instanceof TimeoutException) {
281                    logger.debug(this+" Timeout");
282                    DialogView dialog = ((TimeoutException)te).getDialog();
283                    display.closeWindow(dialog,false);
284                    display.addTimedoutDialog(dialog);
285                } else if (display != null && showResult) {
286                    if (!(te instanceof org.objectweb.jac.util.VoidException))
287                        display.showModal(te,"Error","",context.getWindow(),false,false,true);
288                    else
289                        display.refresh();
290                }
291            }
292            logger.debug("invokeThread done "+this+": "+
293                         substance+"."+method+Arrays.asList(parameters));
294        }
295    
296        Object getReturnValue() {
297            return returnValue;
298        }
299    }
300    
301    class NotAvailableException extends Exception {}