1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 package com.zeroc.testcontroller;
6 
7 import java.io.*;
8 import java.util.*;
9 
10 import com.zeroc.Ice.Logger;
11 import com.zeroc.Ice.Communicator;
12 import com.zeroc.IceInternal.Time;
13 
14 import android.os.Build;
15 import android.util.Log;
16 import android.app.Application;
17 
18 import Test.Common.ProcessControllerRegistryPrx;
19 import Test.Common.ProcessControllerPrx;
20 
21 public class ControllerApp extends Application
22 {
23     private final String TAG = "ControllerApp";
24     private ControllerI _controllerI;
25     private ControllerActivity _activity;
26     private String _ipv4Address;
27     private String _ipv6Address;
28 
29     static private class TestSuiteBundle
30     {
31         @SuppressWarnings("unchecked")
TestSuiteBundle(String name, ClassLoader loader)32         TestSuiteBundle(String name, ClassLoader loader) throws ClassNotFoundException
33         {
34             _loader = loader;
35             _class = (Class<? extends test.TestHelper>)_loader.loadClass(name);
36         }
37 
newInstance()38         test.TestHelper newInstance()
39                 throws IllegalAccessException, InstantiationException
40         {
41             if(_class == null)
42             {
43                 return null;
44             }
45             return _class.newInstance();
46         }
47 
getClassLoader()48         ClassLoader getClassLoader()
49         {
50             return  _loader;
51         }
52 
53         private String _name;
54         private ClassLoader _loader;
55         private Class<? extends test.TestHelper> _class;
56     }
57 
58     class AndroidLogger implements Logger
59     {
60         private final String _prefix;
61 
AndroidLogger(String prefix)62         AndroidLogger(String prefix)
63         {
64             _prefix = prefix;
65         }
66 
67         @Override
print(String message)68         public void print(String message)
69         {
70             Log.d(TAG, message);
71         }
72 
73         @Override
trace(String category, String message)74         public void trace(String category, String message)
75         {
76             Log.v(category, message);
77         }
78 
79         @Override
warning(String message)80         public void warning(String message)
81         {
82             Log.w(TAG, message);
83         }
84 
85         @Override
error(String message)86         public void error(String message)
87         {
88             Log.e(TAG, message);
89         }
90 
91         @Override
getPrefix()92         public String getPrefix()
93         {
94             return _prefix;
95         }
96 
97         @Override
cloneWithPrefix(String s)98         public Logger cloneWithPrefix(String s)
99         {
100             return new AndroidLogger(s);
101         }
102     }
103 
104     @Override
onCreate()105     public void onCreate()
106     {
107         super.onCreate();
108         com.zeroc.Ice.Util.setProcessLogger(new AndroidLogger(""));
109     }
110 
setIpv4Address(String address)111     synchronized public void setIpv4Address(String address)
112     {
113         _ipv4Address = address;
114     }
115 
setIpv6Address(String address)116     synchronized public void setIpv6Address(String address)
117     {
118         int i = address.indexOf("%");
119         _ipv6Address = i == -1 ? address : address.substring(i);
120     }
121 
getAddresses(boolean ipv6)122     public List<String> getAddresses(boolean ipv6)
123     {
124         List<String> addresses = new java.util.ArrayList<String>();
125         try
126         {
127             java.util.Enumeration<java.net.NetworkInterface> ifaces = java.net.NetworkInterface.getNetworkInterfaces();
128             while(ifaces.hasMoreElements())
129             {
130                 java.net.NetworkInterface iface = ifaces.nextElement();
131                 java.util.Enumeration<java.net.InetAddress> addrs = iface.getInetAddresses();
132                 while(addrs.hasMoreElements())
133                 {
134                     java.net.InetAddress addr = addrs.nextElement();
135                     if((ipv6 && addr instanceof java.net.Inet6Address) ||
136                        (!ipv6 && !(addr instanceof java.net.Inet6Address)))
137                     {
138                         addresses.add(addr.getHostAddress());
139                     }
140                 }
141             }
142         }
143         catch(java.net.SocketException ex)
144         {
145         }
146         return addresses;
147     }
148 
startController(ControllerActivity activity, boolean bluetooth)149     public synchronized void startController(ControllerActivity activity, boolean bluetooth)
150     {
151         _activity = activity;
152         if(_controllerI == null)
153         {
154             _controllerI = new ControllerI(bluetooth);
155         }
156     }
157 
println(final String data)158     public synchronized void println(final String data)
159     {
160         _activity.runOnUiThread(new Runnable()
161                                 {
162                                     @Override
163                                     public void run()
164                                     {
165                                         synchronized(ControllerApp.this)
166                                         {
167                                             _activity.println(data);
168                                         }
169                                     }
170                                 });
171     }
172 
isEmulator()173     public static boolean isEmulator()
174     {
175         return Build.FINGERPRINT.startsWith("generic") ||
176                Build.FINGERPRINT.startsWith("unknown") ||
177                Build.MODEL.contains("google_sdk") ||
178                Build.MODEL.contains("Emulator") ||
179                Build.MODEL.contains("Android SDK built for x86") ||
180                Build.MANUFACTURER.contains("Genymotion") ||
181                (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
182                Build.PRODUCT.equals("google_sdk");
183     }
184 
185     class ControllerI
186     {
ControllerI(boolean bluetooth)187         public ControllerI(boolean bluetooth)
188         {
189             com.zeroc.Ice.InitializationData initData = new com.zeroc.Ice.InitializationData();
190             initData.properties = com.zeroc.Ice.Util.createProperties();
191             initData.properties.setProperty("Ice.ThreadPool.Server.SizeMax", "10");
192             initData.properties.setProperty("ControllerAdapter.Endpoints", "tcp");
193             //initData.properties.setProperty("Ice.Trace.Network", "3");
194             //initData.properties.setProperty("Ice.Trace.Protocol", "1");
195             initData.properties.setProperty("ControllerAdapter.AdapterId", java.util.UUID.randomUUID().toString());
196             initData.properties.setProperty("Ice.Override.ConnectTimeout", "1000");
197             if(!isEmulator())
198             {
199                 if(bluetooth)
200                 {
201                     initData.properties.setProperty("Ice.Plugin.IceBT", "com.zeroc.IceBT.PluginFactory");
202                 }
203                 initData.properties.setProperty("Ice.Plugin.IceDiscovery", "com.zeroc.IceDiscovery.PluginFactory");
204                 initData.properties.setProperty("IceDiscovery.DomainId", "TestController");
205             }
206             _communicator = com.zeroc.Ice.Util.initialize(initData);
207             com.zeroc.Ice.ObjectAdapter adapter = _communicator.createObjectAdapter("ControllerAdapter");
208             ProcessControllerPrx processController = ProcessControllerPrx.uncheckedCast(
209                     adapter.add(new ProcessControllerI(),
210                                 com.zeroc.Ice.Util.stringToIdentity("Android/ProcessController")));
211             adapter.activate();
212             ProcessControllerRegistryPrx registry;
213             if(isEmulator())
214             {
215                 registry = ProcessControllerRegistryPrx.uncheckedCast(
216                         _communicator.stringToProxy("Util/ProcessControllerRegistry:tcp -h 10.0.2.2 -p 15001"));
217             }
218             else
219             {
220                 // Use IceDiscovery to find a process controller registry
221                 registry = ProcessControllerRegistryPrx.uncheckedCast(
222                         _communicator.stringToProxy("Util/ProcessControllerRegistry"));
223             }
224             registerProcessController(adapter, registry, processController);
225             println("Android/ProcessController");
226         }
227 
228         public void
registerProcessController(final com.zeroc.Ice.ObjectAdapter adapter, final ProcessControllerRegistryPrx registry, final ProcessControllerPrx processController)229         registerProcessController(final com.zeroc.Ice.ObjectAdapter adapter,
230                                   final ProcessControllerRegistryPrx registry,
231                                   final ProcessControllerPrx processController)
232         {
233             registry.ice_pingAsync().whenCompleteAsync(
234                 (r1, e1) ->
235                 {
236                     if(e1 != null)
237                     {
238                         handleException(e1, adapter, registry, processController);
239                     }
240                     else
241                     {
242                         com.zeroc.Ice.Connection connection = registry.ice_getConnection();
243                         connection.setAdapter(adapter);
244                         connection.setACM(OptionalInt.of(5),
245                                 Optional.of(com.zeroc.Ice.ACMClose.CloseOff),
246                                 Optional.of(com.zeroc.Ice.ACMHeartbeat.HeartbeatAlways));
247                         connection.setCloseCallback(
248                                 con ->
249                                 {
250                                     println("connection with process controller registry closed");
251                                     while (true) {
252                                         try
253                                         {
254                                             Thread.sleep(500);
255                                             break;
256                                         }
257                                         catch(InterruptedException e)
258                                         {
259                                         }
260                                     }
261                                     registerProcessController(adapter, registry, processController);
262                                 });
263 
264                         registry.setProcessControllerAsync(processController).whenCompleteAsync(
265                                 (r2, e2) ->
266                                 {
267                                     if(e2 != null)
268                                     {
269                                         handleException(e2, adapter, registry, processController);
270                                     }
271                                 });
272                     }
273                 });
274         }
275 
handleException(Throwable ex, final com.zeroc.Ice.ObjectAdapter adapter, final ProcessControllerRegistryPrx registry, final ProcessControllerPrx processController)276         public void handleException(Throwable ex,
277                                     final com.zeroc.Ice.ObjectAdapter adapter,
278                                     final ProcessControllerRegistryPrx registry,
279                                     final ProcessControllerPrx processController)
280         {
281             if(ex instanceof com.zeroc.Ice.ConnectFailedException || ex instanceof com.zeroc.Ice.TimeoutException)
282             {
283                 while(true)
284                 {
285                     try
286                     {
287                         Thread.sleep(500);
288                         break;
289                     }
290                     catch(InterruptedException e)
291                     {
292                     }
293                 }
294                 registerProcessController(adapter, registry, processController);
295             }
296             else
297             {
298                 println(ex.toString());
299             }
300         }
301 
destroy()302         public void destroy()
303         {
304             _communicator.destroy();
305         }
306 
307         private ProcessControllerRegistryPrx _registry;
308         private com.zeroc.Ice.Communicator _communicator;
309     }
310 
311     class ControllerHelperI extends Thread implements test.TestHelper.ControllerHelper
312     {
ControllerHelperI(TestSuiteBundle bundle, String[] args, String exe)313         public ControllerHelperI(TestSuiteBundle bundle, String[] args, String exe)
314         {
315             _bundle = bundle;
316             _args = args;
317             _exe = exe;
318         }
319 
communicatorInitialized(Communicator communicator)320         public void communicatorInitialized(Communicator communicator)
321         {
322             com.zeroc.Ice.Properties properties = communicator.getProperties();
323             if(properties.getProperty("Ice.Plugin.IceSSL").equals("com.zeroc.IceSSL.PluginFactory"))
324             {
325                 com.zeroc.IceSSL.Plugin plugin =
326                         (com.zeroc.IceSSL.Plugin)communicator.getPluginManager().getPlugin("IceSSL");
327                 String keystore = communicator.getProperties().getProperty("IceSSL.Keystore");
328                 properties.setProperty("IceSSL.Keystore", "");
329                 int resource = keystore.equals("client.bks") ? R.raw.client : R.raw.server;
330                 java.io.InputStream certs = getResources().openRawResource(resource);
331                 plugin.setKeystoreStream(certs);
332                 plugin.setTruststoreStream(certs);
333                 communicator.getPluginManager().initializePlugins();
334             }
335         }
336 
run()337         public void run()
338         {
339             try
340             {
341                 _helper = _bundle.newInstance();
342                 _helper.setClassLoader(_bundle.getClassLoader());
343                 _helper.setControllerHelper(this);
344 
345                 _helper.setWriter(new Writer()
346                     {
347                         @Override
348                         public void close() throws IOException
349                         {
350                         }
351 
352                         @Override
353                         public void flush() throws IOException
354                         {
355                         }
356 
357                         @Override
358                         public void write(char[] buf, int offset, int count)
359                             throws IOException
360                         {
361                             _out.append(buf, offset, count);
362                         }
363                     });
364 
365                 _helper.run(_args);
366                 completed(0);
367             }
368             catch(Exception ex)
369             {
370                 ex.printStackTrace(_helper.getWriter());
371                 completed(-1);
372             }
373         }
374 
shutdown()375         public void shutdown()
376         {
377             if(_helper != null)
378             {
379                 _helper.shutdown();
380             }
381         }
382 
getOutput()383         public String getOutput()
384         {
385             return _out.toString();
386         }
387 
serverReady()388         synchronized public void serverReady()
389         {
390             _ready = true;
391             notifyAll();
392         }
393 
completed(int status)394         synchronized private void completed(int status)
395         {
396             _completed = true;
397             _status = status;
398             notifyAll();
399         }
400 
waitReady(int timeout)401         synchronized private void waitReady(int timeout)
402             throws Test.Common.ProcessFailedException
403         {
404             long now = Time.currentMonotonicTimeMillis();
405             while(!_ready && !_completed)
406             {
407                 try
408                 {
409                     wait(timeout * 1000);
410                     if(Time.currentMonotonicTimeMillis() - now > timeout * 1000)
411                     {
412                         throw new Test.Common.ProcessFailedException("timed out waiting for the process to be ready");
413                     }
414                 }
415                 catch(java.lang.InterruptedException ex)
416                 {
417                 }
418             }
419 
420             if(_completed && _status != 0)
421             {
422                 throw new Test.Common.ProcessFailedException(_out.toString());
423             }
424         }
425 
waitSuccess(int timeout)426         synchronized private int waitSuccess(int timeout)
427             throws Test.Common.ProcessFailedException
428         {
429             long now = Time.currentMonotonicTimeMillis();
430             while(!_completed)
431             {
432                 try
433                 {
434                     wait(timeout * 1000);
435                     if(Time.currentMonotonicTimeMillis() - now > timeout * 1000)
436                     {
437                         throw new Test.Common.ProcessFailedException("timed out waiting for the process to be ready");
438                     }
439                 }
440                 catch(java.lang.InterruptedException ex)
441                 {
442                 }
443             }
444             return _status;
445         }
446 
447         private TestSuiteBundle _bundle;
448         private String[] _args;
449         private String _exe;
450         private test.TestHelper _helper;
451         private boolean _ready = false;
452         private boolean _completed = false;
453         private int _status = 0;
454         private final StringBuffer _out = new StringBuffer();
455     }
456 
457     class ProcessControllerI implements Test.Common.ProcessController
458     {
start(final String testsuite, final String exe, String[] args, com.zeroc.Ice.Current current)459         public Test.Common.ProcessPrx start(final String testsuite, final String exe, String[] args,
460                                             com.zeroc.Ice.Current current)
461             throws Test.Common.ProcessFailedException
462         {
463             println("starting " + testsuite + " " + exe + "... ");
464             String className = "test." + testsuite.replace("/", ".") + "." +
465                 exe.substring(0, 1).toUpperCase(Locale.ROOT) + exe.substring(1);
466             try
467             {
468                 TestSuiteBundle bundle = new TestSuiteBundle(className, getClassLoader());
469                 ControllerHelperI mainHelper = new ControllerHelperI(bundle, args, exe);
470                 mainHelper.start();
471                 return Test.Common.ProcessPrx.uncheckedCast(current.adapter.addWithUUID(new ProcessI(mainHelper)));
472             }
473             catch(ClassNotFoundException ex)
474             {
475                 throw new Test.Common.ProcessFailedException(
476                     "testsuite `" + testsuite + "' exe ` " + exe + "' start failed:\n" + ex.toString());
477             }
478         }
479 
getHost(String protocol, boolean ipv6, com.zeroc.Ice.Current current)480         public String getHost(String protocol, boolean ipv6, com.zeroc.Ice.Current current)
481         {
482             if(isEmulator())
483             {
484                 return  "127.0.0.1";
485             }
486             else
487             {
488                 synchronized(ControllerApp.this)
489                 {
490                     return ipv6 ? _ipv6Address : _ipv4Address;
491                 }
492             }
493         }
494     }
495 
496     class ProcessI implements Test.Common.Process
497     {
ProcessI(ControllerHelperI controllerHelper)498         public ProcessI(ControllerHelperI controllerHelper)
499         {
500             _controllerHelper = controllerHelper;
501         }
502 
waitReady(int timeout, com.zeroc.Ice.Current current)503         public void waitReady(int timeout, com.zeroc.Ice.Current current)
504             throws Test.Common.ProcessFailedException
505         {
506             _controllerHelper.waitReady(timeout);
507         }
508 
waitSuccess(int timeout, com.zeroc.Ice.Current current)509         public int waitSuccess(int timeout, com.zeroc.Ice.Current current)
510             throws Test.Common.ProcessFailedException
511         {
512             return _controllerHelper.waitSuccess(timeout);
513         }
514 
terminate(com.zeroc.Ice.Current current)515         public String terminate(com.zeroc.Ice.Current current)
516         {
517             _controllerHelper.shutdown();
518             current.adapter.remove(current.id);
519             while(true)
520             {
521                 try
522                 {
523                     _controllerHelper.join();
524                     break;
525                 }
526                 catch(InterruptedException ex)
527                 {
528                 }
529             }
530             return _controllerHelper.getOutput();
531         }
532 
533         private ControllerHelperI _controllerHelper;
534     }
535 }
536