1 /*************************************************************************
2  *
3  *  The Contents of this file are made available subject to the terms of
4  *  the BSD license.
5  *
6  *  Copyright 2000, 2010 Oracle and/or its affiliates.
7  *  All rights reserved.
8  *
9  *  Redistribution and use in source and binary forms, with or without
10  *  modification, are permitted provided that the following conditions
11  *  are met:
12  *  1. Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *  2. Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *  3. Neither the name of Sun Microsystems, Inc. nor the names of its
18  *     contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
31  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *************************************************************************/
34 
35 //_______________________________________________
36 // imports
37 import com.sun.star.uno.XComponentContext;
38 import com.sun.star.lib.uno.helper.Factory;
39 import com.sun.star.lib.uno.helper.WeakBase;
40 import com.sun.star.lang.XServiceInfo;
41 import com.sun.star.awt.*;
42 import com.sun.star.beans.*;
43 import com.sun.star.task.*;
44 import com.sun.star.uno.*;
45 import java.lang.*;
46 import javax.swing.*;
47 
48 //_______________________________________________
49 // implementation
50 
51 /** it implements a simple job component.
52  *
53  *  Such jobs are executable in different ways:
54  *  <ul>
55  *      <li>registered for a special URL schema "vnd.sun.star.jobs:*" and used from the generic dispatch framework</li>
56  *      <li>the global com.sun.star.task.JobExecutor service and registered for special events.</li>
57  *  </ul>
58  */
59 public class AsyncJob extends    WeakBase implements XServiceInfo, XAsyncJob
60 {
61     //___________________________________________
62     // const
63     public final XComponentContext m_xCmpCtx;
64 
65     /** the const list of supported uno service names. */
66     public static final java.lang.String[] SERVICENAMES = {"com.sun.star.task.AsyncJob"};
67 
68     /** the const uno implementation name.
69      *  It must be an unique value! The best naming schema seams to use
70      *  a registered domain in reverse order ...
71      */
72     public static final java.lang.String IMPLEMENTATIONNAME = "com.sun.star.comp.framework.java.services.AsyncJob";
73 
74     //___________________________________________
75     // interface
76 
77     /** initialize a new instance of this class with default values. */
78     public AsyncJob( XComponentContext xCompContext )
79     {
80         m_xCmpCtx = xCompContext;
81     }
82 
83     //___________________________________________
84 
85     /** starts execution of this job.
86      *
87      *  @param  lArgs
88      *          list which contains:
89      *          <ul>
90      *              <li>generic job configuration data</li>
91      *              <li>job specific configuration data</li>
92      *              <li>some environment informations</li>
93      *              <li>may optional arguments of a corresponding dispatch request</li>
94      *          </ul>
95      *
96      *  @params xListener
97      *          callback to the executor of this job, which control our life time
98      *
99      *  @throws com.sun.star.lang.IllegalArgumentException
100      *          if given argument list seams to be wrong
101      */
102     public synchronized void executeAsync(com.sun.star.beans.NamedValue[] lArgs    ,
103                                           com.sun.star.task.XJobListener  xListener)
104         throws com.sun.star.lang.IllegalArgumentException
105     {
106         // For asynchronous jobs a valid listener reference is guranteed normaly ...
107         if (xListener == null)
108             throw new com.sun.star.lang.IllegalArgumentException("invalid listener");
109 
110         // extract all possible sub list of given argument list
111         com.sun.star.beans.NamedValue[] lGenericConfig = null;
112         com.sun.star.beans.NamedValue[] lJobConfig     = null;
113         com.sun.star.beans.NamedValue[] lEnvironment   = null;
114         com.sun.star.beans.NamedValue[] lDynamicData   = null;
115 
116         int c = lArgs.length;
117         for (int i=0; i<c; ++i)
118         {
119             if (lArgs[i].Name.equals("Config"))
120                 lGenericConfig = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(lArgs[i].Value);
121             else
122             if (lArgs[i].Name.equals("JobConfig"))
123                 lJobConfig = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(lArgs[i].Value);
124             else
125             if (lArgs[i].Name.equals("Environment"))
126                 lEnvironment = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(lArgs[i].Value);
127             else
128             if (lArgs[i].Name.equals("DynamicData"))
129                 lDynamicData = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(lArgs[i].Value);
130         }
131 
132         // Analyze the environment info. This sub list is the only guarenteed one!
133         if (lEnvironment == null)
134             throw new com.sun.star.lang.IllegalArgumentException("no environment");
135 
136         java.lang.String          sEnvType   = null;
137         java.lang.String          sEventName = null;
138         com.sun.star.frame.XFrame xFrame     = null;
139         c = lEnvironment.length;
140         for (int i=0; i<c; ++i)
141         {
142             if (lEnvironment[i].Name.equals("EnvType"))
143                 sEnvType = com.sun.star.uno.AnyConverter.toString(lEnvironment[i].Value);
144             else
145             if (lEnvironment[i].Name.equals("EventName"))
146                 sEventName = com.sun.star.uno.AnyConverter.toString(lEnvironment[i].Value);
147             else
148             if (lEnvironment[i].Name.equals("Frame"))
149                 xFrame = (com.sun.star.frame.XFrame)com.sun.star.uno.AnyConverter.toObject(
150                             new com.sun.star.uno.Type(com.sun.star.frame.XFrame.class),
151                             lEnvironment[i].Value);
152         }
153 
154         // Further the environment property "EnvType" is required as minimum.
155         if (
156             (sEnvType==null) ||
157             (
158              (!sEnvType.equals("EXECUTOR")) &&
159              (!sEnvType.equals("DISPATCH"))
160             )
161            )
162         {
163             java.lang.String sMessage = "\"" + sEnvType + "\" isn't a valid value for EnvType";
164             throw new com.sun.star.lang.IllegalArgumentException(sMessage);
165         }
166 
167         // Analyze the set of shared config data.
168         java.lang.String sAlias = null;
169         if (lGenericConfig!=null)
170         {
171             c = lGenericConfig.length;
172             for (int i=0; i<c; ++i)
173             {
174                 if (lGenericConfig[i].Name.equals("Alias"))
175                     sAlias = com.sun.star.uno.AnyConverter.toString(lGenericConfig[i].Value);
176             }
177         }
178 
179         // do your job ...
180         // Here we print out all found arguments.
181         java.lang.String sOut = formatOutArgs(lGenericConfig, lJobConfig, lEnvironment, lDynamicData);
182         if (xFrame != null)
183             showInfoModal(xFrame.getContainerWindow(), "Arguments of AsyncJob initialization ...", sOut);
184         else
185             showInfoNonModal("Arguments of AsyncJob initialization ...", sOut);
186 
187         // use return value to start different actions
188         // But look for the right environment. Some options make no sense inside the wrong env.
189         com.sun.star.beans.NamedValue aDeactivation   = null;
190         com.sun.star.beans.NamedValue aDispatchResult = null;
191         com.sun.star.beans.NamedValue aSaveRequest    = null;
192 
193         // SaveArguments will be made everytimes!
194         c = 1;
195 
196         if (lJobConfig==null)
197             lJobConfig = new com.sun.star.beans.NamedValue[1];
198         lJobConfig[0] = new com.sun.star.beans.NamedValue();
199         lJobConfig[0].Name  = "arg_1";
200         lJobConfig[0].Value = "val_1";
201 
202         aSaveRequest = new com.sun.star.beans.NamedValue();
203         aSaveRequest.Name  = "SaveArguments";
204         aSaveRequest.Value = lJobConfig;
205 
206         // Deactivation is usefull inside EXECUTOR environment only
207         if (sEnvType.equals("EXECUTOR"))
208         {
209             ++c;
210             aDeactivation       = new com.sun.star.beans.NamedValue();
211             aDeactivation.Name  = "Deactivate";
212             aDeactivation.Value = java.lang.Boolean.TRUE;
213         }
214 
215         // Sending of result events is usefull inside DISPATCH environment only
216         if (sEnvType.equals("DISPATCH"))
217         {
218             ++c;
219             aDispatchResult       = new com.sun.star.beans.NamedValue();
220             aDispatchResult.Name  = "SendDispatchResult";
221             aDispatchResult.Value = new com.sun.star.frame.DispatchResultEvent(this, com.sun.star.frame.DispatchResultState.SUCCESS, null);
222         }
223 
224         // pack it together for return
225         int i=0;
226         com.sun.star.beans.NamedValue[] lReturn = new com.sun.star.beans.NamedValue[c];
227         lReturn[i++] = aSaveRequest;
228         if (aDeactivation!=null)
229             lReturn[i++] = aDeactivation;
230         if (aDispatchResult!=null)
231             lReturn[i++] = aDispatchResult;
232 
233         xListener.jobFinished(this, lReturn);
234     }
235 
236     //___________________________________________
237 
238     /** show an info box with the UNO based toolkit.
239      *
240      *  It tries to use the container window of a may well know
241      *  office frame as parent. If such parent window could be located,
242      *  the info box can be shown in modal mode. If a parent is missing
243      *  (because this job is called inside an EXECUTOR environment, which
244      *  does not set any frame context here) the info box can't be created!
245      *  Because the toolkit needs parents for non top level windows ...
246      *  In that case the only way is to implement this info box
247      *  native or make it non modal using java dialogs inside it's own thread ...
248      *  (see showInfoNonModal() too)
249      *
250      *  @param  xParent
251      *          used as parent window of the shown info box.
252      *
253      *  @param  sTitle
254      *          is shown as title of the info box.
255      *
256      *  @param  sMessage
257      *          inclused the message body, which is shown as info.
258      */
259 
260     private void showInfoModal( com.sun.star.awt.XWindow xParent  ,
261                                 java.lang.String         sTitle   ,
262                                 java.lang.String         sMessage )
263     {
264         try
265         {
266             // get access to the office toolkit environment
267             com.sun.star.awt.XToolkit xKit = (com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(
268                  com.sun.star.awt.XToolkit.class,
269                  m_xCmpCtx.getServiceManager().createInstanceWithContext("com.sun.star.awt.Toolkit",
270                                                                          m_xCmpCtx));
271 
272             // describe the info box ini it's parameters
273             com.sun.star.awt.WindowDescriptor aDescriptor = new com.sun.star.awt.WindowDescriptor();
274             aDescriptor.WindowServiceName = "infobox";
275             aDescriptor.Bounds            = new com.sun.star.awt.Rectangle(0,0,300,200);
276             aDescriptor.WindowAttributes  = com.sun.star.awt.WindowAttribute.BORDER   |
277                                             com.sun.star.awt.WindowAttribute.MOVEABLE |
278                                             com.sun.star.awt.WindowAttribute.CLOSEABLE;
279             aDescriptor.Type              = com.sun.star.awt.WindowClass.MODALTOP;
280             aDescriptor.ParentIndex       = 1;
281             aDescriptor.Parent            = (com.sun.star.awt.XWindowPeer)UnoRuntime.queryInterface(
282                                                 com.sun.star.awt.XWindowPeer.class,
283                                                 xParent);
284 
285             // create the info box window
286             com.sun.star.awt.XWindowPeer xPeer    = xKit.createWindow(aDescriptor);
287             com.sun.star.awt.XMessageBox xInfoBox = (com.sun.star.awt.XMessageBox)UnoRuntime.queryInterface(
288                                                         com.sun.star.awt.XMessageBox.class,
289                                                         xPeer);
290             if (xInfoBox == null)
291                 return;
292 
293             // fill it with all given informations and show it
294             xInfoBox.setCaptionText(sTitle);
295             xInfoBox.setMessageText(sMessage);
296             xInfoBox.execute();
297         }
298         catch(java.lang.Throwable exIgnore)
299         {
300             // ignore any problem, which can occure here.
301             // It's not realy a bug for this example job, if
302             // it's message could not be printed out!
303         }
304     }
305 
306     //___________________________________________
307 
308     private void showInfoNonModal( java.lang.String sTitle   ,
309                                    java.lang.String sMessage )
310     {
311         // Couldnt be implemented realy using the toolkit ...
312         // Because we need a parent anytime.
313         // And showing e.g. a java dialog can make some trouble
314         // inside office ... but we have no chance here.
315 	final java.lang.String sFinalTitle = sTitle;
316 	final java.lang.String sFinalMessage = sMessage;
317 
318 	// On Mac OS X, AWT/Swing must not be accessed from the AppKit thread, so call
319 	// SwingUtilities.invokeLater always on a fresh thread to avoid that problem
320 	// (also, the current thread must not wait for that fresh thread to terminate,
321 	// as that would cause a deadlock if this thread is the AppKit thread):
322 	final Runnable doRun = new Runnable() {
323 		public void run() {
324 		    javax.swing.JOptionPane.showMessageDialog(null, sFinalMessage, sFinalTitle, javax.swing.JOptionPane.INFORMATION_MESSAGE);
325 		}
326 	    };
327 
328 	 new Thread( doRun ) {
329 	     public void run() { javax.swing.SwingUtilities.invokeLater(doRun); }
330 	 }.start();
331     }
332 
333     //___________________________________________
334 
335     /** helper to print out the given argument list.
336      *
337      *  @param  lGenericConfig
338      *          contains all shared configuration items for a job
339      *
340      *  @param  lJobConfig
341      *          contains all job sepcific configuration items
342      *
343      *  @param  lEnvironment
344      *          contains some environment informations
345      *
346      *  @param  lDynamicData
347      *          contains optional data of a might corresponding dispatch() request
348      */
349 
350     private java.lang.String formatOutArgs(com.sun.star.beans.NamedValue[] lGenericConfig,
351                                            com.sun.star.beans.NamedValue[] lJobConfig    ,
352                                            com.sun.star.beans.NamedValue[] lEnvironment  ,
353                                            com.sun.star.beans.NamedValue[] lDynamicData )
354     {
355         java.lang.StringBuffer sOut = new java.lang.StringBuffer(1024);
356 
357         sOut.append("list \"Config\": ");
358         if (lGenericConfig==null)
359             sOut.append("0 items\n");
360         else
361         {
362             int c = lGenericConfig.length;
363             sOut.append(c+" items\n");
364             for (int i=0; i<c; ++i)
365                 sOut.append("\t["+i+"] \""+lGenericConfig[i].Name+"\" = {"+lGenericConfig[i].Value+"}\n");
366         }
367         sOut.append("list \"JobConfig\": ");
368         if (lJobConfig==null)
369             sOut.append("0 items\n");
370         else
371         {
372             int c = lJobConfig.length;
373             sOut.append(c+" items\n");
374             for (int i=0; i<c; ++i)
375                 sOut.append("\t["+i+"] \""+lJobConfig[i].Name+"\" = {"+lJobConfig[i].Value+"}\n");
376         }
377         sOut.append("list \"Environment\": ");
378         if (lEnvironment==null)
379             sOut.append("0 items\n");
380         else
381         {
382             int c = lEnvironment.length;
383             sOut.append(c+" items\n");
384             for (int i=0; i<c; ++i)
385                 sOut.append("\t["+i+"] \""+lEnvironment[i].Name+"\" = {"+lEnvironment[i].Value+"}\n");
386         }
387         sOut.append("list \"DynamicData\": ");
388         if (lDynamicData==null)
389             sOut.append("0 items\n");
390         else
391         {
392             int c = lDynamicData.length;
393             sOut.append(c+" items\n");
394             for (int i=0; i<c; ++i)
395                 sOut.append("\t["+i+"] \""+lDynamicData[i].Name+"\" = {"+lDynamicData[i].Value+"}\n");
396         }
397 
398         return sOut.toString();
399     }
400 
401     public String[] getSupportedServiceNames() {
402         return SERVICENAMES;
403     }
404 
405     public boolean supportsService( String sService ) {
406         int len = SERVICENAMES.length;
407 
408         for( int i=0; i < len; i++) {
409             if ( sService.equals( SERVICENAMES[i] ) )
410                 return true;
411         }
412 
413         return false;
414     }
415 
416     public String getImplementationName() {
417         return( AsyncJob.class.getName() );
418     }
419 
420 
421     //___________________________________________
422 
423     public synchronized static com.sun.star.lang.XSingleComponentFactory __getComponentFactory(java.lang.String sImplName)
424     {
425         com.sun.star.lang.XSingleComponentFactory xFactory = null;
426         if (sImplName.equals(AsyncJob.IMPLEMENTATIONNAME))
427             xFactory = Factory.createComponentFactory(AsyncJob.class, SERVICENAMES);
428 
429         return xFactory;
430     }
431 
432     //___________________________________________
433     // This method not longer necessary since OOo 3.4 where the component registration
434     // was changed to passive component registration. For more details see
435     // http://wiki.services.openoffice.org/wiki/Passive_Component_Registration
436 
437 //     public synchronized static boolean __writeRegistryServiceInfo(com.sun.star.registry.XRegistryKey xRegKey)
438 //     {
439 //         return Factory.writeRegistryServiceInfo(
440 //             AsyncJob.IMPLEMENTATIONNAME,
441 //             AsyncJob.SERVICENAMES,
442 //             xRegKey);
443 //     }
444 }
445