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

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

Changed webapps_greenstone.jar to greenstone3.war

File size: 22.7 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 try
194 {
195 InputStream is = getAssets().open("greenstone3.war");
196 Installer.install(is, "/greenstone3", webappsDir, "greenstone3", false);
197 Log.i("Jetty", "Loaded console webapp");
198 }
199 catch(Exception ex)
200 {
201 ex.printStackTrace();
202 }
203 }
204 else
205 {
206 Log.i(TAG,webappsDir + " exists");
207 }
208
209 //make jetty/etc
210 File etcDir = new File(jettyDir,__ETC_DIR);
211 if (!etcDir.exists())
212 {
213 boolean made = etcDir.mkdirs();
214 Log.i(TAG,"Made " + etcDir + ": " + made);
215 }
216 else
217 {
218 Log.i(TAG,etcDir + " exists");
219 }
220 sendProgressUpdate(30);
221
222
223 File webdefaults = new File(etcDir,"webdefault.xml");
224 if (!webdefaults.exists() || updateNeeded)
225 {
226 //get the webdefaults.xml file out of resources
227 try
228 {
229 InputStream is = getResources().openRawResource(R.raw.webdefault);
230 OutputStream os = new FileOutputStream(webdefaults);
231 IO.copy(is,os);
232 Log.i(TAG,"Loaded webdefault.xml");
233 }
234 catch (Exception e)
235 {
236 Log.e(TAG,"Error loading webdefault.xml",e);
237 }
238 }
239 sendProgressUpdate(40);
240
241 File realm = new File(etcDir,"realm.properties");
242 if (!realm.exists() || updateNeeded)
243 {
244 try
245 {
246 //get the realm.properties file out resources
247 InputStream is = getResources().openRawResource(R.raw.realm_properties);
248 OutputStream os = new FileOutputStream(realm);
249 IO.copy(is,os);
250 Log.i(TAG,"Loaded realm.properties");
251 }
252 catch (Exception e)
253 {
254 Log.e(TAG,"Error loading realm.properties",e);
255 }
256 }
257 sendProgressUpdate(50);
258
259 File keystore = new File(etcDir,"keystore");
260 if (!keystore.exists() || updateNeeded)
261 {
262 try
263 {
264 //get the keystore out of resources
265 InputStream is = getResources().openRawResource(R.raw.keystore);
266 OutputStream os = new FileOutputStream(keystore);
267 IO.copy(is,os);
268 Log.i(TAG,"Loaded keystore");
269 }
270 catch (Exception e)
271 {
272 Log.e(TAG,"Error loading keystore",e);
273 }
274 }
275 sendProgressUpdate(60);
276
277 //make jetty/contexts
278 File contextsDir = new File(jettyDir,__CONTEXTS_DIR);
279 if (!contextsDir.exists())
280 {
281 boolean made = contextsDir.mkdirs();
282 Log.i(TAG,"Made " + contextsDir + ": " + made);
283 }
284 else
285 {
286 Log.i(TAG,contextsDir + " exists");
287 }
288 sendProgressUpdate(70);
289
290 try
291 {
292 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(),0);
293 if (pi != null)
294 {
295 setStoredJettyVersion(pi.versionCode);
296 }
297 }
298 catch (Exception e)
299 {
300 Log.w(TAG, "Unable to get PackageInfo for i-jetty");
301 }
302
303 //if there was a .update file indicating an update was needed, remove it now we've updated
304 File update = new File(__JETTY_DIR, ".update");
305 if (update.exists())
306 update.delete();
307
308 sendProgressUpdate(100);
309 }
310 };
311
312 static
313 {
314 __JETTY_DIR = new File(Environment.getExternalStorageDirectory(),"jetty");
315 // Ensure parsing is not validating - does not work with android
316 System.setProperty("org.eclipse.jetty.xml.XmlParser.Validating","false");
317
318 // Bridge Jetty logging to Android logging
319 System.setProperty("org.eclipse.jetty.util.log.class","org.mortbay.ijetty.AndroidLog");
320 org.eclipse.jetty.util.log.Log.setLog(new AndroidLog());
321 }
322
323 public IJetty ()
324 {
325 super();
326
327 handler = new Handler ()
328 {
329 public void handleMessage(Message msg) {
330 int total = msg.getData().getInt("prog");
331 progressDialog.setProgress(total);
332 if (total >= 100){
333 dismissDialog(__SETUP_PROGRESS_DIALOG);
334 }
335 }
336
337 };
338 }
339
340 public String formatJettyInfoLine (String format, Object ... args)
341 {
342 String ms = "";
343 if (format != null)
344 ms = String.format(format, args);
345 return ms+"<br/>";
346 }
347
348
349
350 public void consolePrint(String format, Object... args)
351 {
352 String msg = String.format(format,args);
353 if (msg.length() > 0)
354 {
355 consoleBuffer.append(msg).append("<br/>");
356 console.setText(Html.fromHtml(consoleBuffer.toString()));
357 Log.i(TAG,msg); // Only interested in non-empty lines being output to Log
358 }
359 else
360 {
361 consoleBuffer.append(msg).append("<br/>");
362 console.setText(Html.fromHtml(consoleBuffer.toString()));
363 }
364
365 if (scrollTask == null)
366 {
367 scrollTask = new ConsoleScrollTask();
368 }
369
370 consoleScroller.post(scrollTask);
371 }
372
373
374
375 protected int getStoredJettyVersion()
376 {
377 File jettyDir = __JETTY_DIR;
378 if (!jettyDir.exists())
379 {
380 return -1;
381 }
382 File versionFile = new File(jettyDir,"version.code");
383 if (!versionFile.exists())
384 {
385 return -1;
386 }
387 int val = -1;
388 ObjectInputStream ois = null;
389 try
390 {
391 ois = new ObjectInputStream(new FileInputStream(versionFile));
392 val = ois.readInt();
393 return val;
394 }
395 catch (Exception e)
396 {
397 Log.e(TAG,"Problem reading version.code",e);
398 return -1;
399 }
400 finally
401 {
402 if (ois != null)
403 {
404 try
405 {
406 ois.close();
407 }
408 catch (Exception e)
409 {
410 Log.d(TAG,"Error closing version.code input stream",e);
411 }
412 }
413 }
414 }
415
416
417
418
419 @Override
420 protected void onDestroy()
421 {
422 if (bcastReceiver != null)
423 unregisterReceiver(bcastReceiver);
424 super.onDestroy();
425 }
426
427
428
429 @Override
430 public void onCreate(Bundle icicle)
431 {
432 super.onCreate(icicle);
433
434 setContentView(R.layout.jetty_controller);
435
436 startButton = (Button)findViewById(R.id.start);
437 stopButton = (Button)findViewById(R.id.stop);
438 configButton = (Button)findViewById(R.id.config);
439 final Button downloadButton = (Button)findViewById(R.id.download);
440
441
442 IntentFilter filter = new IntentFilter();
443 filter.addAction(__START_ACTION);
444 filter.addAction(__STOP_ACTION);
445 filter.addCategory("default");
446
447 bcastReceiver =
448 new BroadcastReceiver()
449 {
450
451 public void onReceive(Context context, Intent intent)
452 {
453 if (__START_ACTION.equalsIgnoreCase(intent.getAction()))
454 {
455 startButton.setEnabled(false);
456 configButton.setEnabled(false);
457 stopButton.setEnabled(true);
458 consolePrint("<br/>Started Jetty at %s", new Date());
459 String[] connectors = intent.getExtras().getStringArray("connectors");
460 if (null != connectors)
461 {
462 for (int i=0;i<connectors.length;i++)
463 consolePrint(connectors[i]);
464 }
465
466 printNetworkInterfaces();
467
468 if (AndroidInfo.isOnEmulator(IJetty.this))
469 consolePrint("Set up port forwarding to see i-jetty outside of the emulator.");
470 }
471 else if (__STOP_ACTION.equalsIgnoreCase(intent.getAction()))
472 {
473 startButton.setEnabled(true);
474 configButton.setEnabled(true);
475 stopButton.setEnabled(false);
476 consolePrint("<br/> Jetty stopped at %s",new Date());
477 }
478 }
479
480 };
481
482 registerReceiver(bcastReceiver, filter);
483
484
485 // Watch for button clicks.
486 startButton.setOnClickListener(new OnClickListener()
487 {
488 public void onClick(View v)
489 {
490 if (isUpdateNeeded())
491 IJettyToast.showQuickToast(IJetty.this,R.string.loading);
492 else
493 {
494 //TODO get these values from editable UI elements
495 Intent intent = new Intent(IJetty.this,IJettyService.class);
496 intent.putExtra(__PORT,__PORT_DEFAULT);
497 intent.putExtra(__NIO,__NIO_DEFAULT);
498 intent.putExtra(__SSL,__SSL_DEFAULT);
499 intent.putExtra(__CONSOLE_PWD,__CONSOLE_PWD_DEFAULT);
500 startService(intent);
501 }
502 }
503 });
504
505 stopButton.setOnClickListener(new OnClickListener()
506 {
507 public void onClick(View v)
508 {
509 stopService(new Intent(IJetty.this,IJettyService.class));
510 }
511 });
512
513
514 configButton.setOnClickListener(new OnClickListener()
515 {
516 public void onClick(View v)
517 {
518 IJettyEditor.show(IJetty.this);
519 }
520 });
521
522
523 downloadButton.setOnClickListener(new OnClickListener()
524 {
525 public void onClick(View v)
526 {
527 IJettyDownloader.show(IJetty.this);
528 }
529 });
530
531 info = (TextView)findViewById(R.id.info);
532 footer = (TextView)findViewById(R.id.footer);
533 console = (TextView)findViewById(R.id.console);
534 consoleScroller = (ScrollView)findViewById(R.id.consoleScroller);
535
536 StringBuilder infoBuffer = new StringBuilder();
537 try
538 {
539 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(),0);
540 infoBuffer.append(formatJettyInfoLine ("i-jetty version %s (%s)",pi.versionName,pi.versionCode));
541 }
542 catch (NameNotFoundException e)
543 {
544 infoBuffer.append(formatJettyInfoLine ("i-jetty version unknown"));
545 }
546 infoBuffer.append(formatJettyInfoLine("On %s using Android version %s",AndroidInfo.getDeviceModel(), AndroidInfo.getOSVersion()));
547 info.setText(Html.fromHtml(infoBuffer.toString()));
548 /*
549 StringBuilder footerBuffer = new StringBuilder();
550 footerBuffer.append("<b>Project:</b> <a href=\"http://code.google.com/p/i-jetty\">http://code.google.com/p/i-jetty</a> <br/>");
551 footerBuffer.append("<b>Server:</b> http://www.eclipse.org/jetty <br/>");
552 footerBuffer.append("<b>Support:</b> http://www.intalio.com/jetty/services <br/>");
553 footer.setText(Html.fromHtml(footerBuffer.toString()));
554 */
555
556 }
557
558 public static void show(Context context)
559 {
560 final Intent intent = new Intent(context,IJetty.class);
561 context.startActivity(intent);
562 }
563
564 @Override
565 protected void onResume()
566 {
567 if (!SdCardUnavailableActivity.isExternalStorageAvailable())
568 {
569 SdCardUnavailableActivity.show(this);
570 }
571 else
572 {
573 //work out if we need to do the installation finish step
574 //or not. We do it iff:
575 // - there is no previous jetty version on disk
576 // - the previous version does not match the current version
577 // - we're not already doing the update
578
579 if (isUpdateNeeded())
580 {
581 setupJetty();
582 }
583 }
584
585
586 if (IJettyService.isRunning())
587 {
588 startButton.setEnabled(false);
589 configButton.setEnabled(false);
590 stopButton.setEnabled(true);
591 }
592 else
593 {
594 startButton.setEnabled(true);
595 configButton.setEnabled(true);
596 stopButton.setEnabled(false);
597 }
598 super.onResume();
599 }
600
601
602
603 @Override
604 protected Dialog onCreateDialog(int id)
605 {
606 switch(id)
607 {
608 case __SETUP_PROGRESS_DIALOG:
609 {
610 progressDialog = new ProgressDialog(IJetty.this);
611 progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
612 progressDialog.setMessage("Finishing initial install ...");
613
614 return progressDialog;
615 }
616 default:
617 return null;
618 }
619 }
620
621 private void printNetworkInterfaces()
622 {
623 try
624 {
625 Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
626 for (NetworkInterface ni : Collections.list(nis))
627 {
628 Enumeration<InetAddress> iis = ni.getInetAddresses();
629 for (InetAddress ia : Collections.list(iis))
630 {
631 consoleBuffer.append(formatJettyInfoLine("Network interface: %s: %s",ni.getDisplayName(),ia.getHostAddress()));
632 }
633 }
634 }
635 catch (SocketException e)
636 {
637 Log.w(TAG, e);
638 }
639 }
640
641
642
643 protected void setStoredJettyVersion(int version)
644 {
645 File jettyDir = __JETTY_DIR;
646 if (!jettyDir.exists())
647 {
648 return;
649 }
650 File versionFile = new File(jettyDir,"version.code");
651 ObjectOutputStream oos = null;
652 try
653 {
654 FileOutputStream fos = new FileOutputStream(versionFile);
655 oos = new ObjectOutputStream(fos);
656 oos.writeInt(version);
657 oos.flush();
658 }
659 catch (Exception e)
660 {
661 Log.e(TAG,"Problem writing jetty version",e);
662 }
663 finally
664 {
665 if (oos != null)
666 {
667 try
668 {
669 oos.close();
670 }
671 catch (Exception e)
672 {
673 Log.d(TAG,"Error closing version.code output stream",e);
674 }
675 }
676 }
677 }
678
679 /**
680 * We need to an update iff we don't know the current
681 * jetty version or it is different to the last version
682 * that was installed.
683 *
684 * @return
685 */
686 public boolean isUpdateNeeded ()
687 {
688 //if no previous version file, assume update is required
689 int storedVersion = getStoredJettyVersion();
690 if (storedVersion <= 0)
691 return true;
692
693 try
694 {
695 //if different previous version, update is required
696 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(),0);
697 if (pi == null)
698 return true;
699 if (pi.versionCode != storedVersion)
700 return true;
701
702 //if /sdcard/jetty/.update file exists, then update is required
703 File alwaysUpdate = new File(__JETTY_DIR,".update");
704 if (alwaysUpdate.exists())
705 {
706 Log.i(TAG,"Always Update tag found " + alwaysUpdate);
707 return true;
708 }
709 }
710 catch (Exception e)
711 {
712 //if any of these tests go wrong, best to assume update is true?
713 return true;
714 }
715
716 return false;
717 }
718
719 public void setupJetty()
720 {
721 showDialog(__SETUP_PROGRESS_DIALOG);
722 progressThread = new ProgressThread(handler);
723 progressThread.start();
724 };
725
726}
Note: See TracBrowser for help on using the repository browser.