source: gs3-extensions/i-greenstone-server/trunk/src/CUSTOMIZATION/src/org/mortbay/ijetty/IJetty.java@ 26474

Last change on this file since 26474 was 26474, checked in by davidb, 11 years ago

A Greeonstone3 extension for branding an Android install of i-jetty with gsdl images and name

File size: 22.3 KB
Line 
1//========================================================================
2//$Id: IJetty.java 474 2012-01-23 03:07:14Z janb.webtide $
3//Copyright 2008 Mort Bay Consulting Pty. Ltd.
4//------------------------------------------------------------------------
5//Licensed under the Apache License, Version 2.0 (the "License");
6//you may not use this file except in compliance with the License.
7//You may obtain a copy of the License at
8//http://www.apache.org/licenses/LICENSE-2.0
9//Unless required by applicable law or agreed to in writing, software
10//distributed under the License is distributed on an "AS IS" BASIS,
11//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//See the License for the specific language governing permissions and
13//limitations under the License.
14//========================================================================
15
16package org.mortbay.ijetty;
17
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.FileOutputStream;
21import java.io.InputStream;
22import java.io.ObjectInputStream;
23import java.io.ObjectOutputStream;
24import java.io.OutputStream;
25import java.net.InetAddress;
26import java.net.NetworkInterface;
27import java.net.SocketException;
28import java.util.Collections;
29import java.util.Date;
30import java.util.Enumeration;
31
32import org.eclipse.jetty.util.IO;
33import org.mortbay.ijetty.log.AndroidLog;
34import org.mortbay.ijetty.util.AndroidInfo;
35import org.mortbay.ijetty.util.IJettyToast;
36
37import android.app.Activity;
38import android.app.Dialog;
39import android.app.ProgressDialog;
40import android.content.BroadcastReceiver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
44import android.content.pm.PackageInfo;
45import android.content.pm.PackageManager.NameNotFoundException;
46import android.os.Bundle;
47import android.os.Environment;
48import android.os.Handler;
49import android.os.Message;
50import android.text.Html;
51import android.util.Log;
52import android.view.View;
53import android.view.View.OnClickListener;
54import android.widget.Button;
55import android.widget.ScrollView;
56import android.widget.TextView;
57
58/**
59 * IJetty
60 *
61 * Main Jetty activity. Can start other activities: + configure + download
62 *
63 * Can start/stop services: + IJettyService
64 */
65public class IJetty extends Activity
66{
67
68 private static final String TAG = "Jetty";
69
70 public static final String __START_ACTION = "org.mortbay.ijetty.start";
71 public static final String __STOP_ACTION = "org.mortbay.ijetty.stop";
72
73 public static final String __PORT = "org.mortbay.ijetty.port";
74 public static final String __NIO = "org.mortbay.ijetty.nio";
75 public static final String __SSL = "org.mortbay.ijetty.ssl";
76
77 public static final String __CONSOLE_PWD = "org.mortbay.ijetty.console";
78 public static final String __PORT_DEFAULT = "8080";
79 public static final boolean __NIO_DEFAULT = true;
80 public static final boolean __SSL_DEFAULT = false;
81
82 public static final String __CONSOLE_PWD_DEFAULT = "admin";
83
84 public static final String __WEBAPP_DIR = "webapps";
85 public static final String __ETC_DIR = "etc";
86 public static final String __CONTEXTS_DIR = "contexts";
87
88 public static final String __TMP_DIR = "tmp";
89 public static final String __WORK_DIR = "work";
90 public static final int __SETUP_PROGRESS_DIALOG = 0;
91 public static final int __SETUP_DONE = 2;
92 public static final int __SETUP_RUNNING = 1;
93 public static final int __SETUP_NOTDONE = 0;
94
95
96 public static final File __JETTY_DIR;
97 private Button startButton;
98 private Button stopButton;
99 private Button configButton;
100 private TextView footer;
101 private TextView info;
102 private TextView console;
103 private ScrollView consoleScroller;
104 private StringBuilder consoleBuffer = new StringBuilder();
105 private Runnable scrollTask;
106 private ProgressDialog progressDialog;
107 private Thread progressThread;
108 private Handler handler;
109 private BroadcastReceiver bcastReceiver;
110
111 class ConsoleScrollTask implements Runnable
112 {
113 public void run()
114 {
115 consoleScroller.fullScroll(View.FOCUS_DOWN);
116 }
117 }
118
119 /**
120 * ProgressThread
121 *
122 * Handles finishing install tasks for Jetty.
123 */
124 class ProgressThread extends Thread
125 {
126 private Handler _handler;
127
128 public ProgressThread(Handler h) {
129 _handler = h;
130 }
131
132 public void sendProgressUpdate (int prog)
133 {
134 Message msg = _handler.obtainMessage();
135 Bundle b = new Bundle();
136 b.putInt("prog", prog);
137 msg.setData(b);
138 _handler.sendMessage(msg);
139 }
140
141 public void run ()
142 {
143 boolean updateNeeded = isUpdateNeeded();
144
145 //create the jetty dir structure
146 File jettyDir = __JETTY_DIR;
147 if (!jettyDir.exists())
148 {
149 boolean made = jettyDir.mkdirs();
150 Log.i(TAG,"Made " + __JETTY_DIR + ": " + made);
151 }
152
153 sendProgressUpdate(10);
154
155
156 //Do not make a work directory to preserve unpacked
157 //webapps - this seems to clash with Android when
158 //out-of-date webapps are deleted and then re-unpacked
159 //on a jetty restart: Android remembers where the dex
160 //file of the old webapp was installed, but it's now
161 //been replaced by a new file of the same name. Strangely,
162 //this does not seem to affect webapps unpacked to tmp?
163 //Original versions of i-jetty created a work directory. So
164 //we will delete it here if found to ensure webapps can be
165 //updated successfully.
166 File workDir = new File(jettyDir, __WORK_DIR);
167 if (workDir.exists())
168 {
169 Installer.delete(workDir);
170 Log.i(TAG, "removed work dir");
171 }
172
173
174 //make jetty/tmp
175 File tmpDir = new File(jettyDir,__TMP_DIR);
176 if (!tmpDir.exists())
177 {
178 boolean made = tmpDir.mkdirs();
179 Log.i(TAG,"Made " + tmpDir + ": " + made);
180 }
181 else
182 {
183 Log.i(TAG,tmpDir + " exists");
184 }
185
186 //make jetty/webapps
187 File webappsDir = new File(jettyDir,__WEBAPP_DIR);
188 if (!webappsDir.exists())
189 {
190 boolean made = webappsDir.mkdirs();
191 Log.i(TAG,"Made " + webappsDir + ": " + made);
192 }
193 else
194 {
195 Log.i(TAG,webappsDir + " exists");
196 }
197
198 //make jetty/etc
199 File etcDir = new File(jettyDir,__ETC_DIR);
200 if (!etcDir.exists())
201 {
202 boolean made = etcDir.mkdirs();
203 Log.i(TAG,"Made " + etcDir + ": " + made);
204 }
205 else
206 {
207 Log.i(TAG,etcDir + " exists");
208 }
209 sendProgressUpdate(30);
210
211
212 File webdefaults = new File(etcDir,"webdefault.xml");
213 if (!webdefaults.exists() || updateNeeded)
214 {
215 //get the webdefaults.xml file out of resources
216 try
217 {
218 InputStream is = getResources().openRawResource(R.raw.webdefault);
219 OutputStream os = new FileOutputStream(webdefaults);
220 IO.copy(is,os);
221 Log.i(TAG,"Loaded webdefault.xml");
222 }
223 catch (Exception e)
224 {
225 Log.e(TAG,"Error loading webdefault.xml",e);
226 }
227 }
228 sendProgressUpdate(40);
229
230 File realm = new File(etcDir,"realm.properties");
231 if (!realm.exists() || updateNeeded)
232 {
233 try
234 {
235 //get the realm.properties file out resources
236 InputStream is = getResources().openRawResource(R.raw.realm_properties);
237 OutputStream os = new FileOutputStream(realm);
238 IO.copy(is,os);
239 Log.i(TAG,"Loaded realm.properties");
240 }
241 catch (Exception e)
242 {
243 Log.e(TAG,"Error loading realm.properties",e);
244 }
245 }
246 sendProgressUpdate(50);
247
248 File keystore = new File(etcDir,"keystore");
249 if (!keystore.exists() || updateNeeded)
250 {
251 try
252 {
253 //get the keystore out of resources
254 InputStream is = getResources().openRawResource(R.raw.keystore);
255 OutputStream os = new FileOutputStream(keystore);
256 IO.copy(is,os);
257 Log.i(TAG,"Loaded keystore");
258 }
259 catch (Exception e)
260 {
261 Log.e(TAG,"Error loading keystore",e);
262 }
263 }
264 sendProgressUpdate(60);
265
266 //make jetty/contexts
267 File contextsDir = new File(jettyDir,__CONTEXTS_DIR);
268 if (!contextsDir.exists())
269 {
270 boolean made = contextsDir.mkdirs();
271 Log.i(TAG,"Made " + contextsDir + ": " + made);
272 }
273 else
274 {
275 Log.i(TAG,contextsDir + " exists");
276 }
277 sendProgressUpdate(70);
278
279 try
280 {
281 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(),0);
282 if (pi != null)
283 {
284 setStoredJettyVersion(pi.versionCode);
285 }
286 }
287 catch (Exception e)
288 {
289 Log.w(TAG, "Unable to get PackageInfo for i-jetty");
290 }
291
292 //if there was a .update file indicating an update was needed, remove it now we've updated
293 File update = new File(__JETTY_DIR, ".update");
294 if (update.exists())
295 update.delete();
296
297 sendProgressUpdate(100);
298 }
299 };
300
301 static
302 {
303 __JETTY_DIR = new File(Environment.getExternalStorageDirectory(),"jetty");
304 // Ensure parsing is not validating - does not work with android
305 System.setProperty("org.eclipse.jetty.xml.XmlParser.Validating","false");
306
307 // Bridge Jetty logging to Android logging
308 System.setProperty("org.eclipse.jetty.util.log.class","org.mortbay.ijetty.AndroidLog");
309 org.eclipse.jetty.util.log.Log.setLog(new AndroidLog());
310 }
311
312 public IJetty ()
313 {
314 super();
315
316 handler = new Handler ()
317 {
318 public void handleMessage(Message msg) {
319 int total = msg.getData().getInt("prog");
320 progressDialog.setProgress(total);
321 if (total >= 100){
322 dismissDialog(__SETUP_PROGRESS_DIALOG);
323 }
324 }
325
326 };
327 }
328
329 public String formatJettyInfoLine (String format, Object ... args)
330 {
331 String ms = "";
332 if (format != null)
333 ms = String.format(format, args);
334 return ms+"<br/>";
335 }
336
337
338
339 public void consolePrint(String format, Object... args)
340 {
341 String msg = String.format(format,args);
342 if (msg.length() > 0)
343 {
344 consoleBuffer.append(msg).append("<br/>");
345 console.setText(Html.fromHtml(consoleBuffer.toString()));
346 Log.i(TAG,msg); // Only interested in non-empty lines being output to Log
347 }
348 else
349 {
350 consoleBuffer.append(msg).append("<br/>");
351 console.setText(Html.fromHtml(consoleBuffer.toString()));
352 }
353
354 if (scrollTask == null)
355 {
356 scrollTask = new ConsoleScrollTask();
357 }
358
359 consoleScroller.post(scrollTask);
360 }
361
362
363
364 protected int getStoredJettyVersion()
365 {
366 File jettyDir = __JETTY_DIR;
367 if (!jettyDir.exists())
368 {
369 return -1;
370 }
371 File versionFile = new File(jettyDir,"version.code");
372 if (!versionFile.exists())
373 {
374 return -1;
375 }
376 int val = -1;
377 ObjectInputStream ois = null;
378 try
379 {
380 ois = new ObjectInputStream(new FileInputStream(versionFile));
381 val = ois.readInt();
382 return val;
383 }
384 catch (Exception e)
385 {
386 Log.e(TAG,"Problem reading version.code",e);
387 return -1;
388 }
389 finally
390 {
391 if (ois != null)
392 {
393 try
394 {
395 ois.close();
396 }
397 catch (Exception e)
398 {
399 Log.d(TAG,"Error closing version.code input stream",e);
400 }
401 }
402 }
403 }
404
405
406
407
408 @Override
409 protected void onDestroy()
410 {
411 if (bcastReceiver != null)
412 unregisterReceiver(bcastReceiver);
413 super.onDestroy();
414 }
415
416
417
418 @Override
419 public void onCreate(Bundle icicle)
420 {
421 super.onCreate(icicle);
422
423 setContentView(R.layout.jetty_controller);
424
425 startButton = (Button)findViewById(R.id.start);
426 stopButton = (Button)findViewById(R.id.stop);
427 configButton = (Button)findViewById(R.id.config);
428 final Button downloadButton = (Button)findViewById(R.id.download);
429
430
431 IntentFilter filter = new IntentFilter();
432 filter.addAction(__START_ACTION);
433 filter.addAction(__STOP_ACTION);
434 filter.addCategory("default");
435
436 bcastReceiver =
437 new BroadcastReceiver()
438 {
439
440 public void onReceive(Context context, Intent intent)
441 {
442 if (__START_ACTION.equalsIgnoreCase(intent.getAction()))
443 {
444 startButton.setEnabled(false);
445 configButton.setEnabled(false);
446 stopButton.setEnabled(true);
447 consolePrint("<br/>Started Jetty at %s", new Date());
448 String[] connectors = intent.getExtras().getStringArray("connectors");
449 if (null != connectors)
450 {
451 for (int i=0;i<connectors.length;i++)
452 consolePrint(connectors[i]);
453 }
454
455 printNetworkInterfaces();
456
457 if (AndroidInfo.isOnEmulator(IJetty.this))
458 consolePrint("Set up port forwarding to see i-jetty outside of the emulator.");
459 }
460 else if (__STOP_ACTION.equalsIgnoreCase(intent.getAction()))
461 {
462 startButton.setEnabled(true);
463 configButton.setEnabled(true);
464 stopButton.setEnabled(false);
465 consolePrint("<br/> Jetty stopped at %s",new Date());
466 }
467 }
468
469 };
470
471 registerReceiver(bcastReceiver, filter);
472
473
474 // Watch for button clicks.
475 startButton.setOnClickListener(new OnClickListener()
476 {
477 public void onClick(View v)
478 {
479 if (isUpdateNeeded())
480 IJettyToast.showQuickToast(IJetty.this,R.string.loading);
481 else
482 {
483 //TODO get these values from editable UI elements
484 Intent intent = new Intent(IJetty.this,IJettyService.class);
485 intent.putExtra(__PORT,__PORT_DEFAULT);
486 intent.putExtra(__NIO,__NIO_DEFAULT);
487 intent.putExtra(__SSL,__SSL_DEFAULT);
488 intent.putExtra(__CONSOLE_PWD,__CONSOLE_PWD_DEFAULT);
489 startService(intent);
490 }
491 }
492 });
493
494 stopButton.setOnClickListener(new OnClickListener()
495 {
496 public void onClick(View v)
497 {
498 stopService(new Intent(IJetty.this,IJettyService.class));
499 }
500 });
501
502
503 configButton.setOnClickListener(new OnClickListener()
504 {
505 public void onClick(View v)
506 {
507 IJettyEditor.show(IJetty.this);
508 }
509 });
510
511
512 downloadButton.setOnClickListener(new OnClickListener()
513 {
514 public void onClick(View v)
515 {
516 IJettyDownloader.show(IJetty.this);
517 }
518 });
519
520 info = (TextView)findViewById(R.id.info);
521 footer = (TextView)findViewById(R.id.footer);
522 console = (TextView)findViewById(R.id.console);
523 consoleScroller = (ScrollView)findViewById(R.id.consoleScroller);
524
525 StringBuilder infoBuffer = new StringBuilder();
526 try
527 {
528 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(),0);
529 infoBuffer.append(formatJettyInfoLine ("i-jetty version %s (%s)",pi.versionName,pi.versionCode));
530 }
531 catch (NameNotFoundException e)
532 {
533 infoBuffer.append(formatJettyInfoLine ("i-jetty version unknown"));
534 }
535 infoBuffer.append(formatJettyInfoLine("On %s using Android version %s",AndroidInfo.getDeviceModel(), AndroidInfo.getOSVersion()));
536 info.setText(Html.fromHtml(infoBuffer.toString()));
537 /*
538 StringBuilder footerBuffer = new StringBuilder();
539 footerBuffer.append("<b>Project:</b> <a href=\"http://code.google.com/p/i-jetty\">http://code.google.com/p/i-jetty</a> <br/>");
540 footerBuffer.append("<b>Server:</b> http://www.eclipse.org/jetty <br/>");
541 footerBuffer.append("<b>Support:</b> http://www.intalio.com/jetty/services <br/>");
542 footer.setText(Html.fromHtml(footerBuffer.toString()));
543 */
544
545 }
546
547 public static void show(Context context)
548 {
549 final Intent intent = new Intent(context,IJetty.class);
550 context.startActivity(intent);
551 }
552
553 @Override
554 protected void onResume()
555 {
556 if (!SdCardUnavailableActivity.isExternalStorageAvailable())
557 {
558 SdCardUnavailableActivity.show(this);
559 }
560 else
561 {
562 //work out if we need to do the installation finish step
563 //or not. We do it iff:
564 // - there is no previous jetty version on disk
565 // - the previous version does not match the current version
566 // - we're not already doing the update
567
568 if (isUpdateNeeded())
569 {
570 setupJetty();
571 }
572 }
573
574
575 if (IJettyService.isRunning())
576 {
577 startButton.setEnabled(false);
578 configButton.setEnabled(false);
579 stopButton.setEnabled(true);
580 }
581 else
582 {
583 startButton.setEnabled(true);
584 configButton.setEnabled(true);
585 stopButton.setEnabled(false);
586 }
587 super.onResume();
588 }
589
590
591
592 @Override
593 protected Dialog onCreateDialog(int id)
594 {
595 switch(id)
596 {
597 case __SETUP_PROGRESS_DIALOG:
598 {
599 progressDialog = new ProgressDialog(IJetty.this);
600 progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
601 progressDialog.setMessage("Finishing initial install ...");
602
603 return progressDialog;
604 }
605 default:
606 return null;
607 }
608 }
609
610 private void printNetworkInterfaces()
611 {
612 try
613 {
614 Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
615 for (NetworkInterface ni : Collections.list(nis))
616 {
617 Enumeration<InetAddress> iis = ni.getInetAddresses();
618 for (InetAddress ia : Collections.list(iis))
619 {
620 consoleBuffer.append(formatJettyInfoLine("Network interface: %s: %s",ni.getDisplayName(),ia.getHostAddress()));
621 }
622 }
623 }
624 catch (SocketException e)
625 {
626 Log.w(TAG, e);
627 }
628 }
629
630
631
632 protected void setStoredJettyVersion(int version)
633 {
634 File jettyDir = __JETTY_DIR;
635 if (!jettyDir.exists())
636 {
637 return;
638 }
639 File versionFile = new File(jettyDir,"version.code");
640 ObjectOutputStream oos = null;
641 try
642 {
643 FileOutputStream fos = new FileOutputStream(versionFile);
644 oos = new ObjectOutputStream(fos);
645 oos.writeInt(version);
646 oos.flush();
647 }
648 catch (Exception e)
649 {
650 Log.e(TAG,"Problem writing jetty version",e);
651 }
652 finally
653 {
654 if (oos != null)
655 {
656 try
657 {
658 oos.close();
659 }
660 catch (Exception e)
661 {
662 Log.d(TAG,"Error closing version.code output stream",e);
663 }
664 }
665 }
666 }
667
668 /**
669 * We need to an update iff we don't know the current
670 * jetty version or it is different to the last version
671 * that was installed.
672 *
673 * @return
674 */
675 public boolean isUpdateNeeded ()
676 {
677 //if no previous version file, assume update is required
678 int storedVersion = getStoredJettyVersion();
679 if (storedVersion <= 0)
680 return true;
681
682 try
683 {
684 //if different previous version, update is required
685 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(),0);
686 if (pi == null)
687 return true;
688 if (pi.versionCode != storedVersion)
689 return true;
690
691 //if /sdcard/jetty/.update file exists, then update is required
692 File alwaysUpdate = new File(__JETTY_DIR,".update");
693 if (alwaysUpdate.exists())
694 {
695 Log.i(TAG,"Always Update tag found " + alwaysUpdate);
696 return true;
697 }
698 }
699 catch (Exception e)
700 {
701 //if any of these tests go wrong, best to assume update is true?
702 return true;
703 }
704
705 return false;
706 }
707
708 public void setupJetty()
709 {
710 showDialog(__SETUP_PROGRESS_DIALOG);
711 progressThread = new ProgressThread(handler);
712 progressThread.start();
713 };
714
715}
Note: See TracBrowser for help on using the repository browser.