source: main/trunk/greenstone3/src/java/org/greenstone/applet/GsdlCollageApplet/GsdlCollageApplet.java@ 38936

Last change on this file since 38936 was 38936, checked in by anupama, 2 months ago

(When the PC ran out of memory and tomcat wouldn't shutdown, it got into a state thereafter where it could neither restart nor stop nor start. A machine reboot fixed the problem, but the problem distracted me for a while and prevented the initial commit and further cleanup of code.) This commit: getting the GsdlCollageApplet to successfully work as a *webswing* *applet*. Dr Bainbridge worked out: 1. How to get the GsdlCollageApplet to work with correct, hardcoded applet parameters in webswing.config.in. The applet wasn't showing yet on init() when run with webswing, and only shows when the GsdlCollageApplet Java Component receives the resize event (not even on its first Shown event). 2. How to pass in applet parameters use customArgs as a value to a applet param key in webswing.config, then set the value of customArgs in JavaScript to be string of key-value pairs with custom separators that will successfully make it into Java and can be parsed out. 3. How to get the webswing kill() JavaScript function to stop the GsdlCollageApplet when run *as an applet* when the user navigates away from the page (the kill() function had successfully stopped GsdlCollageApplet when it had been run as an application. The problem was that unless we implement a webswing shutdown handler, by default, webswing's kill() just generates a windowClosing event and it's done. This works out for the collage application as it has been written to respond to the windowClosing event, but it didn't work for applets as they don't respond to windowClosing events. Instead, the webswing API docs online mentioned the signature of the shutdown handler that we could add to do our own thing on shutdown, which the GsdlCollageApplet now does when run as an applet (it calls stop and destroy, which ensure the threads GsdlCollageApplet launched stop running, just as it does when it exits as an application). The hint of the issue of why kill() wasn't responding was in the tomcat/bin/logs/webswing.log file where webswing added a message that no custom shutdown listeners were registered so it was just going to follow the default shutdown procedure (which from the webswing source code turned to be generating a windowClosing Event). 4. Finally JPhind needed an adjustment around the isWebswing parameter since it stopped being called isWebswingApplication (when it used to behave accordingly if it was run as webswing and as an application not applet).

File size: 38.1 KB
Line 
1package org.greenstone.applet.GsdlCollageApplet;
2
3import org.webswing.toolkit.api.WebswingUtil;
4import org.webswing.toolkit.api.action.WebActionEvent;
5import org.webswing.toolkit.api.action.WebActionListener;
6
7import org.webswing.toolkit.api.lifecycle.WebswingShutdownListener;
8import org.webswing.toolkit.api.lifecycle.OnBeforeShutdownEvent;
9
10import org.webswing.toolkit.api.url.*;
11
12//import java.applet.Applet;
13import javax.swing.JApplet;
14import java.awt.event.ComponentListener;
15import java.awt.event.ComponentEvent;
16import java.awt.event.MouseAdapter;
17import java.awt.event.MouseEvent;
18import java.awt.event.MouseListener;
19import java.awt.event.WindowAdapter;
20import java.awt.event.WindowEvent;
21import java.awt.*;
22import java.net.*;
23
24
25import javax.swing.JFrame;
26import javax.swing.JLabel;
27import java.util.Map;
28import java.util.HashMap;
29
30
31/**
32 *
33 * @author Katrina Edgar
34 * @author David Bainbridge
35 * adjusted for GS3 in 2024 (Anu)
36 *
37 * Main class for the GsdlCollageApplet<br>
38 * Processes the parameters passed through the Applet<br>
39 * Builds an appropriate starting url from these parameters<br>
40 * Creates a storage class for downloaded images<br>
41 * Starts thread to download images and their associated url<br>
42 * Starts thread to display downloaded images on the applet screen<br>
43 */
44
45public class GsdlCollageApplet extends JApplet implements ComponentListener
46{
47 static final int EXTRA_HEIGHT = 30; // for status bar or any extra elements
48 private int X_DIM = 600;
49 private int Y_DIM = 300;
50
51 boolean isWebswing = false; // if run as webswing
52 // To run this GsdlCollageApplet as Application instead of as Applet
53 boolean isRunAsApplet = true;
54 // set only if JPhind object is run as an application
55 URL docBaseURL = null;
56 JLabel statusBar = null;
57 Map<String,String> appParams;
58
59 // package access, used mainly for GS3
60 int gsdlversion = 2;
61 String baseURL = null;
62
63 /** When asked to stop running, this variable will be set to true */
64 private boolean stop_running = false;
65
66 /** Amount of error checking output produced <br>
67 * Ranges from 0 - no output to 3 - maximum output */
68 protected int verbosity_ = 0;
69 /** Indicates whether java2 functionality should be used <br>
70 * If true will allow advanced image processing techniques to occur,
71 * such as fading and colouring using pixel manipulation <br>
72 * If false, images will maintain an alpha value of 1 (appear solid),
73 * newer images will simply be pasted on top of existing images <br> */
74 protected boolean isJava2_ = true;
75 /** Number of nested links to follow When used with greenstone,
76 * controls to which level of the document links will be followed For
77 * example a maximum depth of 1 will mean only the top page of a
78 * document will be examined, while a depth of 2 will include sections
79 * within this document, 3 includes sections within sections, and so
80 * on */
81 protected int maxDepth_ = 3;
82
83 /** Maximum number of images to display on the canvas */
84 protected int maxDisplay_ = 25;
85
86 /** Maximum number of downloaded images to store <br> Prevents applet
87 * from using excessive amounts of bandwidth by downloading too many
88 * images simulataneously*/
89 protected int maxDownloads_ = Integer.MAX_VALUE;
90
91 /** Types of images permitted in the collage, for example gif, jpg,
92 * png... */
93 protected String imageType_ = ".jpg%.png";
94
95 /** Caption of the collage */
96 protected String caption_ = "";
97
98 /** Background color of applet screen */
99 protected Color bgcolor_ = new Color(150, 193, 155);
100
101 /** Time lapse between repainting of the applet */
102 protected int refreshDelay_ = 2000;
103
104 /** Stores an image and url pair and provides associated methods */
105 protected DownloadImages download_images_ = null;
106 /** Downloads images from a starting url, recursively follows nested
107 * links */
108 protected DownloadUrls download_thread_ = null;
109 /** Image processing and placement on applet screen */
110 protected DisplayImages display_thread_ = null;
111
112
113 /** Gets the set, calculated or default width (x dimension) of the applet/application */
114 public int X_DIM() {
115 return X_DIM;
116 }
117 /** Gets the set, calculated or default height (y dimension) of the applet/application */
118 public int Y_DIM() {
119 return Y_DIM;
120 }
121
122 /** Gets verbosity */
123 public int verbosity() { return verbosity_; }
124 /** Gets maximum depth for nested links */
125 public int maxDepth() { return maxDepth_; }
126
127 /** Gets maximum number of images to display */
128 public int maxDisplay() { return maxDisplay_;}
129
130 /** Gets maximum number of images to store during downloading */
131 public int maxDownloads() { return maxDownloads_; }
132 /** Gets the refresh delay used between repaint */
133 public int refreshDelay() { return refreshDelay_; }
134
135 /** Gets parameters from the applet code and stores locally.
136 * Forms a starting url for image retrieval to begin from.
137 * The starting url is formed either by:
138 * * Using the image_url parameter as provided, which is assumed to
139 * be a complete url
140 * * If this parameter does not exist, the assumption is that the
141 * collage is being incorporated with the Greenstone Digital Library
142 * Software. The starting url is formed by concatenating
143 * the gwcgi, collection and classifier parameters as provided.
144 * Then starts downloading and displaying images */
145
146 Thread paint ;
147
148 public GsdlCollageApplet() {
149
150 super();
151
152 // status bar that applet has, but we want one also if we run JPhind as application
153 // And in fact, when webswing runs our applet, we never get a status bar. So we
154 // create a custom status bar now even if we're running as an applet.
155 // When this applet is run through the appletviewer we might end up with 2 status bars.
156 //setLayout(new BorderLayout());
157 //if(!isRunAsApplet) {
158 statusBar = null; //new JLabel();
159
160 //this.add(statusBar, BorderLayout.SOUTH);
161
162 ///Window win = SwingUtilities.getWindowAncestor(this);
163 ///if(win instanceof JFrame) {
164 /// JFrame topFrame = (JFrame)win;
165 /// topFrame.add(statusBar, BorderLayout.SOUTH);
166 ///} else { //AppletViewer or browser
167 /// this.add(statusBar, BorderLayout.SOUTH);
168 ///}
169
170 //Dimension d = statusBar.getSize();
171 //d.height = EXTRA_HEIGHT;
172 //statusBar.setPreferredSize(d);
173 //}
174
175 this.addMouseListener(new CollageMouseAdapter());
176 }
177
178 public void applicationPreInit(String[] args) {
179 // I copied the contents of this contructor for my code for the JPhind.java constructor
180 // This constructor will be used when this JApplet is run as an application
181 // instead of as applet
182 this.isRunAsApplet = false;
183
184 appParams = new HashMap<String,String>((args.length+1)/2);
185 // For GS2, gwcgi = param gwcgi
186 // For GS3, gwcgi = param gwcgi + param library
187 // docBaseURL = starting_url/image_url = param gwcgi
188
189 String key = null;
190 String value = null;
191 for(int i = 0; i < args.length; i++) {
192 if(i%2==0) {
193 key = args[i].substring(2); // remove -- prefix of paramname
194 //System.err.println("got key: " + key);
195 } else {
196 value = args[i];
197 appParams.put(key, value);
198 //System.err.println("got value: " + value);
199
200
201 // HttpUtils.parseQueryString() deprecated, so hacking decode xtra key-value pairs
202 // https://stackoverflow.com/questions/13592236/parse-a-uri-string-into-name-value-collection?page=1&tab=scoredesc#tab-top
203
204 if(key.equals("xtraParams")) {
205 value = value.replace("&amp;", "&"); // just in case we have html entities
206 parseXtraParams(value, "=", "&", appParams);
207 /*
208 String[] param_list = value.split("&", 0); // 0 for all occurrences
209 for(String key_val : param_list) {
210 String[] paramPair = key_val.split("=", 2); // get first 2 strings, key and val
211 //System.err.println("key_val: " + key_val);
212 if(paramPair.length == 2) {
213 String xtraParamsKey = paramPair[0];
214 String xtraParamsVal = paramPair[1];
215 //System.err.println("key - val: " + xtraParamsKey + " - " + xtraParamsVal);
216 appParams.put(xtraParamsKey, xtraParamsVal);
217 }
218 }
219 */
220 }
221
222 key = value = null;
223 }
224 }
225
226 // TODO: we can possibly remove this as it's done in init() now before we need it
227 isWebswing = appParams.getOrDefault("webswing", "0").equals("1") ? true : false;
228
229 // Need these parameters set before init(), so that the display works
230 int w = this.getWidth();
231 if(appParams.get("width") != null) {
232 w = Integer.parseInt(appParams.get("width"));
233 }
234 int h = this.getHeight();
235 if(appParams.get("height") != null) {
236 h = Integer.parseInt(appParams.get("height"));
237 }
238 // For applet, w and h may now be set to a positive value. But if GsdlCollageApplet
239 // is run as an application, it won't yet be visible and not have a non-zero dimension.
240 // So use defaults here:
241 this.X_DIM = (w <= 0) ? X_DIM : w;
242 this.Y_DIM = (h <= 0) ? Y_DIM : h;
243
244 // Attempting hack to circumvent division by zero error in MyAffineTransform line 39
245 //DisplayImages.app_x_dim_ = X_DIM;
246 //DisplayImages.app_y_dim_ = Y_DIM;
247
248 try {
249 this.docBaseURL = new URL(appParams.get("baseurl"));
250 } catch(MalformedURLException mue) {
251 mue.printStackTrace();
252 System.err.println("*** Unable to instantiate URL from parameter baseurl: " + appParams.get("baseurl"));
253 System.exit(-1);
254 }
255
256 }
257
258 /**
259 * Overriding (J)Applet method getParameter to first check appParams map
260 * if Phind was run run as an application.
261 * https://stackoverflow.com/questions/15905127/overridden-methods-in-javadoc
262 */
263 @Override
264 public String getParameter(String name) {
265 if(!isRunAsApplet) {
266 return appParams.get(name);
267 }
268 else {
269 if(appParams != null) {
270 String value = appParams.get(name);
271 if(value != null) {
272 return value;
273 }
274 }
275 return super.getParameter(name);
276 }
277 }
278
279 @Override
280 public URL getDocumentBase() {
281 if(!isRunAsApplet) { // launched as application
282 //System.err.println("*** docBaseURL: " + docBaseURL);
283 return this.docBaseURL;
284 } else {
285 return super.getDocumentBase();
286 }
287 }
288
289 @Override
290 public void showStatus(String msg) {
291 // Either firefox doesn't provide a status bar window for applets any more or webswing
292 // doesn't provide a status window, so we don't see Applet.showStatus() output appear
293 // in webswing-phind and in fact don't even see any Applet statusBar in webswing.
294 // However, since we print useful and interesting information to the status bar,
295 // we'll now always show and print to our manually added statusBar now
296 // not only if(!isRunAsApplet) when we needed to manually create a statusBar.
297 if(this.statusBar != null) {
298 this.statusBar.setText(msg);
299 }
300 if(isRunAsApplet) {
301 super.showStatus(msg);
302 }
303 }
304
305 // Given a string xtraParams of key-value pairs separatod by pairSeparators,
306 // parses out each (key, value) and puts them into the given map
307 Map parseXtraParams(String xtraParams, String kvSeparator, String kvPairSeparator, Map map) {
308
309 // String.split() is preferred over Tokenizer but 2nd parameter behaves differently
310 // than I expected.
311 // https://docs.oracle.com/javase/6/docs/api/java/lang/String.html#split%28java.lang.String,%20int%29
312 String[] param_list = xtraParams.split(kvPairSeparator, 0);// 0 means for all occurrences
313
314 if(map == null) {
315 map = new HashMap<String,String>(param_list.length);
316 }
317
318 for(String key_val : param_list) {
319 String[] paramPair = key_val.split(kvSeparator, 2); // get first 2 strings, key and val
320 System.err.println("key_val: " + key_val);
321 if(paramPair.length == 2) {
322 String xtraParamsKey = paramPair[0];
323 String xtraParamsVal = paramPair[1];
324 // Let's remove any bookending quotes from value, this is necessary for some
325 // values when run as a webswing applet
326 if(xtraParamsVal.startsWith("\"") && xtraParamsVal.endsWith("\"")) {
327 xtraParamsVal = xtraParamsVal.substring(1, xtraParamsVal.length()-1);
328 }
329
330 if (verbosity_ >= 3) {
331 System.err.println("*** xtraParams key - val: " + xtraParamsKey + " - " + xtraParamsVal);
332 }
333 map.put(xtraParamsKey, xtraParamsVal);
334 }
335 }
336
337 return map;
338 }
339
340 public void init()
341 {
342 if (verbosity_ >= 4) {
343 System.err.println("Attempting to retrieve parameters...");
344 }
345
346 // This section is to help *webswing* *applet* use dynamic params in the config file.
347 // Extract anything that's in xtraParams (key=value&k2=v2&... string) into
348 // appParams, so all is ready to call getParameter and get expected params out by name
349 // thereafter.
350 // Basically, this section allows us to support any dynamic parameters getting added
351 // by Javascript to the existing applet parameters defined in webswing.config.in
352 // Here we unpack the xtraParams sent and set them up as regular applet parameters, so
353 // that the rest of the applet init() function can read them in as regular applet params.
354 String xtraParams = getParameter("xtraParams");
355 if(xtraParams != null) {
356 // will optionally create appParams and add the key,value pairs in xtraParams into it
357 // after splitting key::value;;k2::v2 etc
358 appParams = parseXtraParams(xtraParams, "::", ";;", appParams);
359 }
360
361
362 // gets the parameters specified
363 String showStatusBar = getParameter("statusbar");
364 if(statusBar == null) {
365 System.err.println("### Status bar code turned off. Ignoring statusbar settings.");
366 } else {
367 if(showStatusBar == null || showStatusBar.equals("0")) {
368 System.err.println("### Hiding status bar (default)");
369 statusBar.setVisible(false);
370 }
371 }
372 String verbosity_param = getParameter("verbosity");
373 String is_java2_param = getParameter("isJava2");
374 String max_depth_param = getParameter("maxDepth");
375 String max_downloads_param = getParameter("maxDownloads");
376 String max_display_param = getParameter("maxDisplay");
377 String refresh_delay_param = getParameter("refreshDelay");
378 String image_type_param = getParameter("imageType");
379 String bgcolor_param = getParameter("bgcolor");
380 String caption_param = getParameter("caption");
381 String document_root = getParameter("documentroot");
382 String gwcgi = getParameter("gwcgi"); // for GS2
383 String baseurl = getParameter("baseurl"); // for GS3, URL before /library
384
385 String webswing = getParameter("webswing");
386 isWebswing = (webswing == null) ? false : webswing.equals("1");
387
388 String gsdlVersionStr = getParameter("gsdlversion");
389 if(gsdlVersionStr != null) {
390 System.err.println("*** gsdl version: " + gsdlVersionStr);
391 this.gsdlversion = Integer.parseInt(gsdlVersionStr);
392 }
393
394 // Check it isn't null
395 if ((verbosity_param!=null) && (!verbosity_param.startsWith("_"))) {
396 verbosity_ = Integer.parseInt(verbosity_param);
397 }
398 else {
399 verbosity_ = 1;
400 }
401
402
403 if (verbosity_ >= 4) {
404 System.err.println("Got parameters.");
405 }
406
407 if (caption_param != null && !caption_param.startsWith("_")) {
408 caption_ = caption_param;
409 }
410 else {
411 if (verbosity_ >= 4) {
412 System.err.println("No Caption: setting to a space.");
413 }
414 caption_ = " ";
415 }
416
417 if ((bgcolor_param != null) && (!bgcolor_param.startsWith("_"))) {
418 if (bgcolor_param.startsWith("#")) {
419 bgcolor_ = Color.decode(bgcolor_param);
420 }
421 else {
422 String [] c = bgcolor_param.split(",");
423 if (c.length == 3)
424 bgcolor_ = new Color(Integer.parseInt(c[0]), Integer.parseInt(c[1]), Integer.parseInt(c[2]));
425 }
426 if (verbosity_ >= 4){
427 System.err.println("Set BG to be " + bgcolor_.toString());
428 }
429 }
430 else {
431 if (verbosity_ >= 4) {
432 System.err.println("No BG: setting to NZDL green.");
433 }
434 bgcolor_ = new Color(150, 193, 155);
435 }
436
437 if ((image_type_param != null) && (!image_type_param.startsWith("_"))) {
438 imageType_ = image_type_param;
439 }
440
441 if ((is_java2_param == null) || (is_java2_param.equals("auto")) || (is_java2_param.startsWith("_"))) {
442 String version = System.getProperty("java.version");
443 version = version.substring(0, version.lastIndexOf("."));
444 System.err.println("VERSION: " + version);
445 float ver = (new Float(version)).floatValue();
446 isJava2_ = (ver >= 1.2) ? true : false;
447 }
448 else {
449 isJava2_ = (is_java2_param.equals("true")) ? true : false;
450 }
451
452 if ((max_depth_param != null) && (!max_depth_param.startsWith("_"))) {
453 // System.err.println("maxDepth = " + max_depth_param);
454 maxDepth_ = Integer.parseInt(max_depth_param);
455 }
456
457 if ((max_downloads_param!=null) && (!max_downloads_param.startsWith("_"))) {
458 maxDownloads_ = Integer.parseInt(max_downloads_param);
459 System.out.println(" maxDownloads " + maxDownloads_ );
460 maxDownloads_=50;
461 }
462
463 if ((max_display_param!=null) && (!max_display_param.startsWith("_"))) {
464 maxDisplay_ = Integer.parseInt(max_display_param);
465
466 }
467
468 if ((refresh_delay_param!=null) && (!refresh_delay_param.startsWith("_"))) {
469 refreshDelay_ = Integer.parseInt(refresh_delay_param);
470 }
471
472
473 if (document_root !=null){ // e.g. "/greenstone/web/images" for GS2
474 if(document_root.indexOf("/") == 0) {
475 document_root = document_root.substring(1); // takes off first slash
476 }
477 if (document_root.indexOf("/") > 0 ){ // e.g we end up with "greenstone" for GS2, "greenstone3" for GS3
478 document_root = document_root.substring(0, document_root.indexOf("/"));
479 }
480 }
481
482 String image_url = getParameter("imageURL");
483 String image_ignore = getParameter("imageIgnorePrefix");
484 String href_musthave = getParameter("hrefMustHave");
485 String image_mustnothave = getParameter("imageMustNotHave");
486
487 // builds starting url when incorporated with Greenstone
488 if (image_url==null)
489 {
490 String collection_param = getParameter("collection");
491 String classifier_param = getParameter("classifier");
492
493 if(gsdlversion == 2) {
494 // GS2 way
495 String gwcgi_param = gwcgi;
496 gwcgi_param = tidy_URL(gwcgi_param, true); //true to append ? to URL
497 image_url = gwcgi_param + "a=d";
498 image_url += "&c=" + collection_param;
499 image_url += "&cl=" + classifier_param;
500 } else {
501 // Try GS3 way
502 String library = getParameter("library");
503 //String site = getParameter("sitename");
504
505 // starting URL (image_url) might not be base_url. We need to store base_url separately
506 baseURL = tidy_URL(baseurl, false);
507 if(!baseURL.endsWith("/")) {
508 baseURL += "/";
509 }
510
511 // building up to baseURL/LIBNAME/collection/COLNAME/browse/
512 image_url = baseURL + library + "/collection/" + collection_param + "/browse/";
513
514 int index = classifier_param.indexOf(".");
515 if(index == -1) {
516 image_url += classifier_param;
517 } else {
518
519 //"CL2#" + classifier_param;
520 //String superClassifier = classifier_param.substring(0, index);
521 //image_url = image_url + superClassifier + "#" + classifier_param;
522
523 // I'm guessing we want something like
524 //http://localhost:8383/greenstone3/library/collection/smallbea/browse/CL2/3, when classifier_param is CL2.3
525 // to get the classifier page containing the images that are to be turned into a collage?
526 String classifierSuffix = classifier_param.replace(".", "/");
527 image_url = image_url + classifierSuffix;
528 }
529 }
530 }
531
532 // This block was a nice idea if it worked, but it doesn't work. It's here for syntax.
533 // Handle getting called by webswing JavaScript (but only if run in webswing mode):
534 // when the user navigates away from the webswing app's web page, do cleanup
535 // https://www.webswing.org/docs/23.2/integrate/jslink.html#invoking-java-from-javascript
536 // https://vaadin.com/directory/component/webswing-vaadin1
537 if(isWebswing) {
538
539 System.err.println("#### isWebswing, about to add listeners");
540
541 WebswingUtil.getWebswingApi().addBrowserActionListener(new WebActionListener() {
542 //@Override
543 public void actionPerformed(WebActionEvent actionEvent) {
544
545 System.err.println("******** JavaScript to Java: actionPerformed called.");
546 //System.err.println("actionEvent = ");
547
548 switch (actionEvent.getActionName()) {
549 case "navigatingAway":
550 // do cleanup
551 System.err.println("@@@@ In Java collage addBrowserActionListener - on navigatingAway called");
552 GsdlCollageApplet.this.stopRunning();
553 break;
554 case "testJavaCall":
555 System.err.println("@@@@ In GsdlCollageApplet's addBrowserActionListener - testJavaCall called");
556 WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet got TEST call !!", null);
557 //WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet - testjavaCall handler got your message", null);
558 }
559 }
560 });
561 }
562
563 if(isWebswing && isRunAsApplet) {
564
565 // When webswing runs Collage in Applet mode, it's not
566 // shutdown the way we'd like on the javascript kill()
567 // command when the user navigates away from the collage
568 // page as that only generates a Java WindowClosingEvent
569 // by default if not shutdown handlers registered. And a
570 // WindowClosingEvent only has any effect on applications,
571 // not applets. But we can write our own custom handler to
572 // mimic the applet shutdown sequence: refer to
573 // https://www.webswing.org/docs/20.2/integrate/api.html
574 // This code also written with the help of the source code
575 // to know the methods in interface WebswingShutdownListener
576 // and the import statements required for the methods.
577 // and then call System.exit(0) on top of usual applet
578 // behaviour to make sure we get rid of the applet
579 // "application" that webswing launched for us (it took
580 // the applet code and ran it, presumably like a java
581 // program, so a System.exit call may be warranted).
582 WebswingUtil.getWebswingApi().addShutdownListener(new WebswingShutdownListener() {
583 // These are official API comments for info
584 /**
585 * Invoked before Webswing requests application to exit.
586 * Do not execute long operations in this listener - listener execution will be interrupted if blocking for &gt; 3 seconds and the application will exit without delay.
587 * Connection to server is still open when this callback is triggered.
588 * This method can delay the shutdown. Calling {@link WebswingApi#resetInactivityTimeout()} within the delay period will cancel the shutdown sequence.
589 *
590 * This method is not called on the event dispatch thread.
591 *
592 * @param event Event contains the reason of this shutdown - either triggered from Admin console's rest interface or by inactivity
593 * @return number of seconds to delay the shutdown, returning 0 will cause shutdown without delay (even if {@link WebswingApi#resetInactivityTimeout()} has been called)
594 */
595 public int onBeforeShutdown(OnBeforeShutdownEvent event) {
596 return 0; // seconds to delay before starting shutdown procedure
597 }
598
599 /**
600 * Invoked when Webswing requests swing application to exit.
601 * This method should cause this process to exit (not necessarily in the same thread).
602 * When this method is called, connection to server is already closed
603 *
604 * This method is not called on the event dispatch thread.
605 */
606 public void onShutdown() {
607 // https://docs.oracle.com/javase%2F7%2Fdocs%2Fapi%2F%2F/java/applet/Applet.html#destroy()
608 // destroy(): "Called by the browser or
609 // applet viewer to inform this applet that
610 // it is being reclaimed and that it should
611 // destroy any resources that it has
612 // allocated. The stop method will always be
613 // called before destroy."
614 // We're like the browser now and so we should
615 // honour the above contract.
616
617 System.err.println("------- WebswingShutdownListener.onShutdown() - our custom behaviour");
618 stop();
619 destroy();
620 // Even though we're an applet and applets
621 // shouldn't system.exit(0) we're running our
622 // applet version of the code through
623 // webswing. And this handler/webswing stands
624 // in place of a browser and controls what
625 // Ought to happen if a browser doesn't take
626 // care of the applet life cycle and we ought
627 // to do it.
628 System.exit(0);
629 }
630 });
631 }
632
633 /*
634 System.err.println("XXX App width: " + this.getWidth());
635 System.err.println("XXX App height: " + this.getHeight());
636
637
638 // webswing applet doesn't have dimensions set because there's no applet element setting it
639 // so we need to set the size on the Collage Applet if launched with webswing and run in applet mode
640 if(isWebswing && isRunAsApplet) {
641 //this.setPreferredSize(new Dimension(X_DIM(), Y_DIM()));
642 this.setSize(X_DIM(), Y_DIM());
643 this.resize(X_DIM(), Y_DIM());
644
645 System.err.println("@@@@ App width: " + this.getWidth());
646 System.err.println("@@@@ App height: " + this.getHeight());
647
648 if(!this.isShowing()) {
649 System.err.println("@@@@ The webswing Applet isn't showing!");
650 this.setVisible(true);
651 }
652 if(!this.isShowing()) {
653 System.err.println("@@@@ The webswing Applet is STILL not showing!");
654 }
655 }
656 */
657
658 MediaTracker trk = new MediaTracker(this);
659
660 // creates a class to store the image and its associated url
661 download_images_ = new DownloadImages( this, verbosity_,isJava2_ );
662 // starts the download image thread with the starting url
663
664 download_thread_ = new DownloadUrls(this, download_images_,
665 image_url, href_musthave, image_mustnothave,
666 image_ignore, imageType_,document_root,verbosity_,trk);
667
668 if(isShowing() && isDisplayable()) { //The case if(!(isRunAsApplet && isWebswing)) {
669 System.err.println("**** Creating display thread");
670 // starts the display image thread with the currently downloaded images
671 display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
672 } else {
673 System.err.println("**** GsdlCollageApplet.init() - display_thread_ not yet showing.");
674 System.err.println("**** Will create and start display_thread_ later, in ComponentListener.resize()");
675 }
676 // The display_thread_ needs the component showing before it can be started up.
677 // It was showing already by this point for the applet in non-webswing mode (when
678 // using the appletviewer),
679 // and I got it showing by this point when run as a cmdline or webswing application.
680 // But when the applet is run in webswing mode, it's not yet showing
681 // at this point! A componentListener allows us to handle componentShown events on
682 // the JComponent of this JApplet. However, the applet in webswing mode doesn't trigger
683 // that, it only triggers componentResized. So maybe webswing starts it off as 0 size?
684 this.addComponentListener(this);
685
686
687 }
688
689 public void componentHidden(ComponentEvent e) {
690 System.err.println(e.getComponent().getClass().getName() + " --- Hidden");
691 }
692
693 public void componentMoved(ComponentEvent e) {
694 System.err.println(e.getComponent().getClass().getName() + " --- Moved");
695 }
696
697 public void componentResized(ComponentEvent e) {
698 System.err.println(e.getComponent().getClass().getName() + " --- Resized");
699
700 if(display_thread_ == null) {
701 display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
702 display_thread_.start();
703 }
704 }
705
706 public void componentShown(ComponentEvent e) {
707 System.err.println("XXXXX" + e.getComponent().getClass().getName() + " --- Shown");
708 if(display_thread_ == null) {
709 display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
710 display_thread_.start();
711 }
712 }
713
714 public JLabel getStatusBar() {
715 return this.statusBar;
716 }
717
718 // TODO: Do I need to follow
719 // https://stackoverflow.com/questions/5861894/how-to-synchronize-or-lock-upon-variables-in-java
720 public void stopRunning() {
721 if(verbosity_ >= 3) {
722 System.err.println("**** GsdlCollageApplet.stopRunning() called");
723 }
724
725 stop_running = true;
726 if(download_thread_ != null) {
727 download_thread_.stopRunning();
728 }
729 if(display_thread_ != null) {
730 display_thread_.stopRunning();
731 }
732
733 // The Display Thread?
734 //if(!Thread.currentThread().isInterrupted()) {
735 // Thread.currentThread().interrupt();
736 //}
737 }
738
739 public boolean isStopping() {
740 return stop_running;
741 }
742
743 protected class CollageMouseAdapter extends MouseAdapter {
744
745 /** Goes to the url associated with the image that is clicked on screen<br>
746 * Displays the url containing the image in a new window */
747 //public boolean mouseDown(Event event, int x, int y)
748 public void mousePressed(MouseEvent e)
749 {
750
751 System.err.println("Mouse pressed");
752 int x = e.getX();
753 int y = e.getY();
754
755 // determines which image was clicked on
756 CollageImage cimage = GsdlCollageApplet.this.display_thread_.clickedOnImage(x,y);
757 // if they were clicking on an image (as opposed to background)
758 if (cimage != null)
759 {
760 System.err.println("Click on image: from url = " + cimage.from_url_);
761 try {
762 // displays the associated url in a new window
763 URL from_url = null;
764
765 java.util.regex.Pattern p = java.util.regex.Pattern.compile("cl=CL\\d(\\.\\d)*\\s");
766 java.util.regex.Matcher m = p.matcher(cimage.from_url_.trim()+" ");
767
768 if (m.find() ){
769 from_url = cimage.url_;
770 }
771 else{
772 from_url = new URL(cimage.from_url_ + "#" + cimage.name_);
773 }
774
775 if(isRunAsApplet) {
776 GsdlCollageApplet.this.getAppletContext().showDocument(from_url,"gsdlDoc");
777 } else if(isWebswing) {
778 WebswingUtil.getWebswingApi().sendActionEvent("openURL",
779 from_url.toString() + " - " +"gsdlDoc",
780 null); // window name is gsdlDoc
781 } else {
782 System.err.println("@@@GsdlCollageApplet.CollageMouseAdapter.mousePressed()"
783 + "\n\topening url " + from_url + "\n\t"
784 + "for non-applet and non-webswing application is not yet implemented.");
785 }
786 }
787 catch (MalformedURLException ex) {
788 ex.printStackTrace();
789 }
790
791 }
792 //return true;
793 }
794 }
795
796 /** Start threads for downloading and displaying images */
797 public void start()
798 {
799 // starts the display image thread with the currently downloaded images
800 //display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
801 //this.setVisible(true);
802 download_thread_.start();
803
804 if(display_thread_ != null) {
805 //if(!(isRunAsApplet && isWebswing)) { // then display_thread_ is instantiated at this point
806 // start it
807 display_thread_.start();
808 }
809
810 paint = new Thread(new Runnable(){
811 public void run() {
812 try {
813
814 Thread curr_thread = Thread.currentThread();
815
816 while (curr_thread == paint) {
817
818 repaint();
819
820 Thread.sleep(2000);
821 curr_thread = Thread.currentThread();
822 }
823
824
825 } catch (Exception e) {
826
827 }
828 }
829
830
831 });
832
833 paint.start();
834
835 }
836
837 /** Stops threads for downloading and displaying images */
838 public void stop()
839 {
840 System.err.println("\n\n*** Stopping collage Applet: stopping threads...");
841 //download_thread_.stop();
842 //display_thread_.stop();
843 stopRunning();
844 // TODO: maybe we want to unset the interrupt variable on the threads to restart
845 // on start() getting called? No: we system.exit() in the webswing shutdown handler
846
847 //download_thread_ = null;
848 //display_thread_ = null;
849
850 }
851
852 /** Destroys threads for downloading and displaying images */
853 public void destroyed()
854 {
855
856 System.err.println("@@@@@@@@@ GsdlCollageApplet.destroy() called");
857
858 // Let's be sure to have stopped running
859 //stopRunning();
860
861 download_thread_ = null;
862
863 display_thread_ = null;
864
865 }
866
867
868 /** Repaints the applet */
869 public void update(Graphics g)
870 {
871 // System.err.println("Update called");
872 paint(g);
873 }
874
875 /** Repaints the applet using the paint method of the thread that is
876 * currently displaying images */
877 public void paint(Graphics g) {
878
879 if (display_thread_!=null)
880 {
881
882 //display_thread_.display_collage();
883 display_thread_.paint(g);
884 }
885 else
886 {
887 System.err.println("Applet still trying to paint!!");
888 }
889
890
891
892 }
893
894
895
896 /** Ensures a URL address (as string) has a protocol, host, and file.
897 *
898 * If the URL is a CGI script URL, it should be tidied up so that it is
899 * appropriate to tag attrib=value pairs on the end. This means it
900 * must either end with a "?" or (if it contains a question-mark
901 * internally) end with a "&". */
902 String tidy_URL(String address, boolean isCGI) {
903
904 // System.err.println("tidy URL: " + address);
905
906 // make sure the URL has protocol, host, and file
907 if (address.startsWith("http")) {
908 // the address has all the necessary components
909 } else if (address.startsWith("/")) {
910 // there is not protocol and host
911 URL document = getDocumentBase();
912 String port = "";
913 if (document.getPort()!=-1) {
914 port = ":" + document.getPort();
915 }
916 address = "http://" + document.getHost() + port + address;
917 } else {
918 // this URL is relative to the directory the document is in
919 URL document = getDocumentBase();
920 String directory = document.getFile();
921 int end = directory.lastIndexOf('/');
922 String port = "";
923 if (document.getPort()!=-1) {
924 port = ":" + document.getPort();
925 }
926 directory = directory.substring(0,end + 1);
927 address = "http://" + document.getHost() + port + directory + address;
928
929 }
930
931 // if the URL is a cgi script, make sure it has a "?" in ti,
932 // and that it ends with a "?" or "&"
933 if (isCGI) {
934 if (address.indexOf((int) '?') == -1) {
935 address = address + "?";
936 } else if (!address.endsWith("?")) {
937 address = address + "&";
938 }
939 }
940
941 return address;
942 }
943
944
945 public static void printUsage() {
946 System.err.println("Params needed include: --gsdlversion <3/2> [--statusbar 0/1] [--baseurl <GS3 DL baseURL>/--gwcgi <library.cgi URL>] --library l --collection c --classifier cl --imageType \".jpg%.png\" --imageMustNotHave \"interfaces/\" [--hrefMustHave \"LIBRARYNAME/sites/SITENAME/collect/COLNAME\"] -documentroot greenstone3 [--webswing <1/0>] [--verbosity <v>] --maxDepth 500 --maxDisplay 25 --refreshDelay 1500 --isJava2 auto --bgcolor \"#96c29a\" [--xtraParams <key1=value1&key2=value2&...]");
947 System.err.println("To run as webswing application, additionally pass in --webswing 1");
948 }
949
950 // To also be able to run this applet as an application, need a main method
951 /**
952 * After building the GS3 Multimedia collection, try running this Application as:
953java -cp ./web/applet/GsdlCollageApplet.jar:./web/WEB-INF/lib/log4j-1.2.8.jar:./web/WEB-INF/classes:./web/ext/webswing/api/webswing-api.jar org.greenstone.applet.GsdlCollageApplet.GsdlCollageApplet --statusbar 0 --baseurl "http://localhost:8383/greenstone3/" --library library --collection smallbea --gsdlversion 3 --hrefMustHave "library/collection/smallbea/browse/CL3" --documentroot greenstone3 --verbosity 3 --imageType ".jpg%.png" --imageMustNotHave "interfaces/" --classifier "CL3.1" --maxDepth 500 --maxDisplay 25 --refreshDelay 1500 --isJava2 auto --bgcolor "#96c29a" --width 645 --height 780
954 */
955 public static void main(String[] args) {
956 if(args.length == 0) {
957 printUsage();
958 } else if(args[0].equals("--help") || args[0].equals("-h")) {
959 printUsage();
960 } else {
961 JFrame frame = new JFrame("Collage Applet as Application");
962 GsdlCollageApplet collageApp = new GsdlCollageApplet();
963 frame.getContentPane().add(collageApp, BorderLayout.CENTER);
964 //frame.setSize(X_DIM, Y_DIM); //Y_DIM+EXTRA_HEIGHT);
965 frame.setVisible(true);
966 // https://stackoverflow.com/questions/19433358/difference-between-dispose-and-exit-on-close-in-java
967 // default: https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html#EXIT_ON_CLOSE
968 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Running as an application. But don't do EXIT_ON_CLOSE in Applet mode!
969
970 // prepare to run the collage applet as an application
971 collageApp.applicationPreInit(args);
972 // It's now figured out the dimensions based on anything specified, so set frame size
973 frame.setSize(collageApp.X_DIM(), collageApp.Y_DIM()); //Y_DIM+EXTRA_HEIGHT);
974
975 // status bar code. Not ideal, but had to add it here to get relative dimensions right
976 JLabel statusBar = collageApp.getStatusBar();
977 if(statusBar != null) {
978 collageApp.setLayout(new BorderLayout());
979
980 frame.add(statusBar, BorderLayout.SOUTH);
981 Dimension d = statusBar.getSize();
982 d.height = EXTRA_HEIGHT;
983 d.width = collageApp.getWidth();
984 statusBar.setPreferredSize(d);
985 //statusBar.setPreferredSize(new Dimension(collageApp.getWidth(), EXTRA_HEIGHT));
986 }
987
988 // Run it at last: manually calling (J)Applet methods init() and start()
989 collageApp.init();
990
991 collageApp.showStatus("Collage application running");
992 collageApp.start();
993
994 // When we terminate the application, need to manually call the applet method stop()
995 //https://docs.oracle.com/javase/tutorial/uiswing/events/windowlistener.html#windowfocuslistener
996 frame.addWindowListener(new WindowAdapter() {
997 public void windowClosing(WindowEvent e) {
998 if(collageApp.isWebswing) {
999 WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet - quitting now", null);
1000 //org.webswing.toolkit.api.WebswingUtil.getWebswingApi().sendActionEvent("javaToWebswingJSConsoleLog", "GsdlCollageApplet about to QUIT !!", null);
1001 }
1002 collageApp.showStatus("Stopping threads");
1003 System.err.println("\n\n*** Closing collage Application: stopping threads...");
1004 collageApp.stopRunning();
1005
1006 //collageApp.stop();
1007 //collageApp.destroy();
1008 }
1009 });
1010
1011 }
1012 }
1013}
Note: See TracBrowser for help on using the repository browser.