1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package no.feide.moria.servlet;
22
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Enumeration;
26 import java.util.HashMap;
27 import java.util.Locale;
28 import java.util.MissingResourceException;
29 import java.util.Properties;
30 import java.util.ResourceBundle;
31 import java.util.StringTokenizer;
32 import java.util.TreeMap;
33 import java.util.Vector;
34
35 import javax.servlet.ServletContext;
36 import javax.servlet.http.Cookie;
37
38 import no.feide.moria.log.MessageLogger;
39
40 /***
41 * This class is a toolkit for the servlets. Its main functionality is to
42 * retrieve resource bundles.
43 * @author Lars Preben S. Arnesen <lars.preben.arnesen@conduct.no>
44 * @version $Revision: 1.56 $
45 */
46 public final class RequestUtil {
47
48 /***
49 * Used for logging.
50 */
51 static MessageLogger log = new MessageLogger(RequestUtil.class);
52
53 /***
54 * Prefix for all web module properties. <br>
55 * <br>
56 * Current value is <code>"no.feide.moria.web."</code>.
57 */
58 private static final String PATH_PREFIX = "no.feide.moria.web.";
59
60 /***
61 * Property name for web module configuration. <br>
62 * <br>
63 * Current value is <code>PATH_PREFIX + "config"</code>.
64 */
65 public static final String PROP_CONFIG = PATH_PREFIX + "config";
66
67 /***
68 * Configuration property giving the name of the URL parameter containing
69 * the Moria ticket ID. <em>This property is required.</em><br>
70 * <br>
71 * Current value is <code>PATH_PREFIX + "login.ticket_param"</code>.
72 */
73 public static final String PROP_LOGIN_TICKET_PARAM = PATH_PREFIX + "login.ticket_param";
74
75 /***
76 * Property name for Organization.
77 */
78 public static final String PROP_ORG = PATH_PREFIX + "org";
79
80 /***
81 * Configuration property giving the default Moria language, to be used if
82 * no other preferences (user or service) are available.
83 * <em>This property is required.</em><br>
84 * <br>
85 * Current value is <code>PATH_PREFIX + "login.default_language"</code>.
86 */
87
88 public static final String PROP_LOGIN_DEFAULT_LANGUAGE = PATH_PREFIX + "login.default_language";
89
90 /***
91 * Property name for Language.
92 */
93 public static final String PROP_LANGUAGE = PATH_PREFIX + "lang";
94
95 /***
96 * Configuration property giving the URL of the login servlet.
97 * <em>This property is required.</em><br>
98 * <br>
99 * Current value is <code>PATH_PREFIX + "login.url_prefix"</code>.
100 */
101 public static final String PROP_LOGIN_URL_PREFIX = PATH_PREFIX + "login.url_prefix";
102
103 /***
104 * Configuration property giving the URL of the information servlet.
105 * <em>This property is required.</em><br>
106 * <br>
107 * Current value is <code>PATH_PREFIX + "information.url_prefix"</code>.
108 */
109 public static final String PROP_INFORMATION_URL_PREFIX = PATH_PREFIX + "information.url_prefix";
110
111 /***
112 * Configuration property sub-string giving the abbreviations and names for
113 * all available languages. The property name is on the form
114 * <code>PROP_LANGUAGE + "_" + PROP_COMMON</code>.<br>
115 * <br>
116 * The actual values are a comma separated list of elements on the form
117 * <code>EN:English</code>, that is, a two-letter language abbreviation
118 * and a ':' character followed by its display name. <br>
119 * <br>
120 * <em>This property is required</em><br>
121 * <br>
122 * Current value is <code>"common"</code>.
123 */
124 public static final String PROP_COMMON = "common";
125
126 /***
127 * Configuration property giving the name of the cookie used to remember the
128 * user's previously selected organization.
129 * <em>This property is required.</em><br>
130 * <br>
131 * Current value is <code>PATH_PREFIX + "cookie.org.name"</code>.
132 */
133 public static final String PROP_COOKIE_ORG = PATH_PREFIX + "cookie.org.name";
134
135 /***
136 * Configuration property giving the lifetime, in hours, for the cookie used
137 * to remember the user's previously selected organization.
138 * <em>This property is required.</em> <br>
139 * <br>
140 * Current value is <code>PATH_PREFIX + "cookie.org.ttl"</code>.
141 * @see #PROP_COOKIE_ORG
142 */
143 public static final String PROP_COOKIE_ORG_TTL = PATH_PREFIX + "cookie.org.ttl";
144
145 /***
146 * Configuration property giving the name of the cookie used to set user's
147 * desired language. <em>This property is required.</em><br>
148 * <br>
149 * Current value is <code>PATH_PREFIX + "cookie.lang.name"</code>.
150 */
151 public static final String PROP_COOKIE_LANG = PATH_PREFIX + "cookie.lang.name";
152
153 /***
154 * Configuration property giving the lifetime, in hours, for the cookie used
155 * to set user's desired language. <em>This property is required.</em>
156 * <br>
157 * <br>
158 * Current value is <code>PATH_PREFIX + "cookie.lang.ttl"</code>.
159 */
160 public static final String PROP_COOKIE_LANG_TTL = PATH_PREFIX + "cookie.lang.ttl";
161
162 /***
163 * Configuration property giving the name of the cookie used to carry SSO
164 * sessions (that is, a Moria ticket ID belonging to a SSO session).
165 * <em>This property is required.</em><br>
166 * <br>
167 * Current value is <code>PATH_PREFIX + "cookie.sso.name"</code>.
168 */
169 public static final String PROP_COOKIE_SSO = PATH_PREFIX + "cookie.sso.name";
170
171 /***
172 * Configuration property giving the lifetime, in hours, for the cookie used
173 * to carry SSO sessions (named by <code>PROP_COOKIE_SSO</code>).
174 * <em>This property is required.</em><br>
175 * <br>
176 * Current value is <code>PATH_PREFIX + "cookie.sso.ttl"</code>.
177 */
178 public static final String PROP_COOKIE_SSO_TTL = PATH_PREFIX + "cookie.sso.ttl";
179
180 /***
181 * Configuration property giving the name of the cookie used to deny SSO
182 * (when using Moria on public computers, for example).
183 * <em>This property is required.</em>.<br>
184 * <br>
185 * Current value is <code>PATH_PREFIX + "cookie.denysso.name"</code>.
186 */
187 public static final String PROP_COOKIE_DENYSSO = PATH_PREFIX + "cookie.denysso.name";
188
189 /***
190 * Configuration property giving the lifetime, in hours, for the cookie used
191 * to deny SSO (named by <code>PROP_COOKIE_DENYSSO</code>).
192 * <em>This property is required</em><br>
193 * <br>
194 * Current value is <code>PATH_PREFIX + "cookie.denysso.ttl"</code>.
195 * @see #PROP_COOKIE_DENYSSO
196 */
197 public static final String PROP_COOKIE_DENYSSO_TTL = PATH_PREFIX + "cookie.denysso.ttl";
198
199 /***
200 * Property name for Logout URL.
201 */
202 public static final String PROP_LOGOUT_URL_PARAM = PATH_PREFIX + "logout.url_param";
203
204 /***
205 * The name of the resource bundle for the login page. <br>
206 * <br>
207 * Current value is <code>"login"</code>.
208 */
209 public static final String BUNDLE_LOGIN = "login";
210
211 /***
212 * Bundle for the Welcome page for the Information Service.
213 */
214 public static final String BUNDLE_INFOWELCOME = "infowelcome";
215
216 /***
217 * Bundle for the Statistics page.
218 */
219 public static final String BUNDLE_STATISTICSSERVLET = "statistics";
220
221 /***
222 * Configuration property for the Information service's attribute
223 * description XML file.
224 */
225 public static final String PROP_INFORMATION_DESCRIPTIONS = PATH_PREFIX + "information.descriptions";
226
227 /***
228 * Configuration property for the StatusServlet status.xml file path.
229 */
230 public static final String PROP_BACKENDSTATUS_STATUS_XML = PATH_PREFIX + "backendstatus.status_xml";
231
232 /***
233 * Configuration property for the StatusServlet statistics.xml file path.
234 */
235 public static final String PROP_BACKENDSTATUS_STATISTICS_XML = PATH_PREFIX + "backendstatus.statistics_xml";
236
237 /***
238 * Configuration property for the StatusServlet statistics2 basename file path.
239 */
240 public static final String PROP_BACKENDSTATUS_STATISTICS2_BASENAME_XML = PATH_PREFIX + "backendstatus.statistics2_basename_xml";
241
242 /***
243 * Configuration property for the StatusServlet statistics2.xml file path.
244 */
245 public static final String PROP_BACKENDSTATUS_STATISTICS2_XML = PATH_PREFIX + "backendstatus.statistics2_xml";
246
247 /***
248 * Configuration property for the StatisticsServlet's ignored services
249 */
250 public static final String PROP_BACKENDSTATUS_IGNORE = PATH_PREFIX + "backendstatus.ignore";
251
252 /***
253 * Bundle for the Information servlet's attribute descriptions.
254 */
255 public static final String BUNDLE_INFORMATIONSERVLET = "attributes";
256
257 /***
258 * Bundle for the information page about the information servlet.
259 */
260 public static final String BUNDLE_INFOABOUT = "infoabout";
261
262 /***
263 * Bundle for the status servlet.
264 */
265 public static final String BUNDLE_STATUSSERVLET = "status";
266
267 /***
268 * Bundle for the faq page.
269 */
270 public static final String BUNDLE_FAQ = "faq";
271
272 /***
273 * Bundle for the error page.
274 */
275 public static final String BUNDLE_ERROR = "error";
276
277 /***
278 * Link to faq, shown on the login page.
279 */
280 public static final String FAQ_LINK = PATH_PREFIX + "faqlink";
281
282 /***
283 * URL to the Status servlet, as shown on the FAQ page.<br>
284 * <em>This property is required.</em> <br>
285 * Current value is <code>PATH_PREFIX + "faq.status"</code>.
286 */
287 public static final String PROP_FAQ_STATUS = PATH_PREFIX + "faq.status";
288
289 /***
290 * URL to the Statistics servlet, used for language selection.
291 */
292 public static final String PROP_STATISTICS_URL = PATH_PREFIX + "statistics.url";
293
294 /***
295 * Organization name of the Moria owner, as shown on the FAQ page.<br>
296 * <br>
297 * Current value is <code>PATH_PREFIX + "faq.owner"</code>.
298 */
299 public static final String PROP_FAQ_OWNER = PATH_PREFIX + "faq.owner";
300
301 /***
302 * Link to pictures from the information service.
303 */
304 public static final String PIC_LINK = PATH_PREFIX + "piclink";
305
306 /***
307 * TODO: Add JavaDoc. Is this in use?
308 */
309 public static final String RESOURCE_MAIL = PATH_PREFIX + "resource.mail";
310
311 /***
312 * TODO: Add JavaDoc. Is this in use?
313 */
314 public static final String RESOURCE_DATE = PATH_PREFIX + "resource.date";
315
316 /***
317 * TODO: Add JavaDoc. Is this in use?
318 */
319 public static final String RESOURCE_LINK = PATH_PREFIX + "resource.link";
320
321 /***
322 * Name of property from authorization module giving the default language
323 * for a given service. <em>This property is required.</em><br>
324 * <br>
325 * Current value is <code>"language"</code>.
326 */
327
328
329 public static final String CONFIG_LANG = "language";
330
331 /***
332 * From Authorization config Home organization of service.
333 */
334 public static final String CONFIG_HOME = "home";
335
336 /***
337 * From Authorization config: Service name.
338 */
339 public static final String CONFIG_DISPLAY_NAME = "displayName";
340
341 /***
342 * From Authorization configuration; service principal.
343 */
344 public static final String CONFIG_SERVICE_PRINCIPAL = "name";
345
346 /***
347 * From Authorization config: Service URL.
348 */
349 public static final String CONFIG_URL = "url";
350
351 /***
352 * Parameter in request object: Username.
353 */
354 public static final String PARAM_USERNAME = "username";
355
356 /***
357 * Parameter in request object: Password.
358 */
359 public static final String PARAM_PASSWORD = "password";
360
361 /***
362 * Organization URL parameter, used when overriding default organization in
363 * the URL redirecting a user from a service to Moria. Useful for services
364 * that wish to dynamically force use of a certain organization irrespective
365 * of the user's previous selections or the service defaults. <br>
366 * <br>
367 * Current value is <code>"org"</code>.
368 */
369 public static final String PARAM_ORG = "org";
370
371 /***
372 * Language URL parameter, used when overriding current language settings.
373 * <br>
374 * <br>
375 * Current value is <code>"language"</code>.
376 */
377 public static final String PARAM_LANG = "language";
378
379 /***
380 * Attribute URL parameter, used when attributes are shown on the login page.
381 */
382 public static final String PARAM_SHOW_ATTRS = "showAttributes";
383
384 /***
385 * Attribute URL parameter, used to show statistics from the previous year.
386 */
387 public static final String PARAM_OLD_STATISTICS = "old_statistics";
388 /***
389 * Parameter in request object: Deny SSO.
390 */
391 public static final String PARAM_DENYSSO = "denySSO";
392
393 /***
394 * Base URL attribute in request object. Used to fill in the URL to the
395 * authentication web page. <br>
396 * <br>
397 * Current value is <code>"baseURL"</code>.
398 */
399 public static final String ATTR_BASE_URL = "baseURL";
400
401 /***
402 * Attribute in request object: Security level.
403 */
404
405 public static final String ATTR_SEC_LEVEL = "secLevel";
406
407 /***
408 * Attribute in request object: Error type.
409 */
410 public static final String ATTR_ERROR_TYPE = "errorType";
411
412 /***
413 * Attribute in request object: Available languages.
414 */
415 public static final String ATTR_LANGUAGES = "languages";
416
417 /***
418 * Attribute in request object: Available organizations.
419 */
420 public static final String ATTR_ORGANIZATIONS = "organizations";
421
422 /***
423 * Attribute in request object: Preselected organization.
424 */
425 public static final String ATTR_SELECTED_ORG = "selectedOrg";
426
427 /***
428 * Attribute in request object: Denial of SSO.
429 */
430 public static final String ATTR_SELECTED_DENYSSO = "selectedDenySSO";
431
432 /***
433 * Attribute in request object: Preselected lanugage.
434 */
435 public static final String ATTR_SELECTED_LANG = "selectedLang";
436
437 /***
438 * Attribute in request object: Name of client/service.
439 */
440 public static final String ATTR_CLIENT_NAME = "clientName";
441
442 /***
443 * Attribute in request object: Link to associate with service name.
444 */
445 public static final String ATTR_CLIENT_URL = "clientURL";
446
447 /***
448 * Attribute in request object: Language bundle.
449 */
450 public static final String ATTR_BUNDLE = "bundle";
451
452 /***
453 * Attribute in request object: Requested attributes.
454 */
455 public static final String ATTR_REQUESTED_ATTRIBUTES = "requestedAttributes";
456
457 /***
458 * Error type: No organization selected.
459 */
460 public static final String ERROR_NO_ORG = "noOrg";
461
462 /***
463 * Error type: Invalid organization selected.
464 */
465 public static final String ERROR_INVALID_ORG = "invalidOrg";
466
467 /***
468 * Error type: Authentication failed.
469 */
470 public static final String ERROR_AUTHENTICATION_FAILED = "authnFailed";
471
472 /***
473 * Error type: Authorization failed.
474 */
475 public static final String ERROR_AUTHORIZATION_FAILED = "authorizationFailed";
476
477 /***
478 * Error type: Unknown ticket.
479 */
480 public static final String ERROR_UNKNOWN_TICKET = "unknownTicket";
481
482 /***
483 * Error type: The directory is down.
484 */
485 public static final String ERROR_DIRECTORY_DOWN = "directoryDown";
486
487 /***
488 * Error type: Moria is unavailable.
489 */
490 public static final String ERROR_MORIA_DOWN = "moriaDown";
491
492 /***
493 * Error type: User must supply username and password.
494 */
495 public static final String ERROR_NO_CREDENTIALS = "noCredentials";
496
497
498 /***
499 * Default private constructor.
500 */
501 private RequestUtil() {
502
503 }
504
505
506 /***
507 * Generates a resource bundle. The language of the resource bundle is
508 * selected from the following priority list:
509 * <ol>
510 * <li>URL parameter (<code>requestParamLang</code>)
511 * <li>User's cookie (<code>langFromCookie</code>)
512 * <li>Default service language (<code>serviceLang</code>)
513 * <li>User's browser settings (<code>browserLang</code>)
514 * <li>Moria default setting (<code>moriaLang</code>)
515 * </ol>
516 * @param bundleName
517 * Name of the resource bundle to retrieve. Cannot be
518 * <code>null</code>.
519 * @param requestParamLang
520 * Language specified from URL parameter. Can be
521 * <code>null</code>.
522 * @param langFromCookie
523 * Language specified from user's cookie. Can be
524 * <code>null</code>.
525 * @param serviceLang
526 * Default language as specified by configuration for the
527 * service. Can be <code>null</code>.
528 * @param browserLang
529 * Language as requested by the users browser. Can be
530 * <code>null</code>.
531 * @param moriaLang
532 * Default language for Moria. Cannot be <code>null</code>.
533 * @return The requested resource bundle.
534 * @throws IllegalArgumentException
535 * If <code>bundleName</code> or <code>moriaLang</code> is
536 * <code>null</code> or an empty string.
537 * @throws MissingResourceException
538 * If the resource bundle cannot be found.
539 */
540 public static ResourceBundle getBundle(final String bundleName, final String requestParamLang, final String langFromCookie, final String serviceLang, final String browserLang, final String moriaLang) {
541
542
543 if (bundleName == null || bundleName.equals(""))
544 throw new IllegalArgumentException("Resource bundle name must be a non-empty string.");
545 if (moriaLang == null || moriaLang.equals(""))
546 throw new IllegalArgumentException("Default language must be a non-empty string.");
547
548
549 final Vector langSelections = new Vector();
550
551
552 if (requestParamLang != null && !requestParamLang.equals(""))
553 langSelections.add(requestParamLang);
554
555
556 if (langFromCookie != null)
557 langSelections.add(langFromCookie);
558
559
560 if (serviceLang != null && !serviceLang.equals(""))
561 langSelections.add(serviceLang);
562
563
564 if (browserLang != null && !browserLang.equals("")) {
565 final String[] browserLangs = sortedAcceptLang(browserLang);
566 for (int i = 0; i < browserLangs.length; i++) {
567 langSelections.add(browserLangs[i]);
568 }
569 }
570
571
572 langSelections.add(moriaLang);
573
574
575 ResourceBundle bundle;
576 for (Enumeration e = langSelections.elements(); e.hasMoreElements();) {
577 bundle = locateBundle(bundleName, (String) e.nextElement());
578 if (bundle != null)
579 return bundle;
580 }
581
582
583 throw new MissingResourceException("Resource bundle not found", "ResourceBundle", bundleName);
584 }
585
586
587 /***
588 * Locates a bundle in a given language.
589 * @param bundleName
590 * Name of the bundle, cannot be null or "".
591 * @param lang
592 * The bundle's langauge.
593 * @return The resourceBundle for the selected language, null if it is not
594 * found.
595 * @throws IllegalArgumentException
596 * If <code>bundleName</code> or <code>lang</code> is
597 * <code>null</code> or an empty string.
598 */
599 private static ResourceBundle locateBundle(final String bundleName, final String lang) {
600
601
602 if (bundleName == null || bundleName.equals("")) { throw new IllegalArgumentException("bundleName must be a non-empty string."); }
603 if (lang == null || lang.equals("")) { throw new IllegalArgumentException("lang must be a non-empty string."); }
604
605
606 ResourceBundle fallback;
607 try {
608 fallback = ResourceBundle.getBundle(bundleName, new Locale("bogus"));
609 } catch (MissingResourceException e) {
610 fallback = null;
611 }
612
613 final Locale locale = new Locale(lang);
614 ResourceBundle bundle = null;
615 try {
616 bundle = ResourceBundle.getBundle(bundleName, locale);
617 } catch (MissingResourceException e) {
618
619 }
620
621 if (bundle != fallback) { return bundle; }
622
623
624 if (bundle != null && bundle == fallback && locale.getLanguage().equals(Locale.getDefault().getLanguage())) { return bundle; }
625
626
627 return null;
628 }
629
630
631 /***
632 * Returns a requested cookie value from the HTTP request.
633 * @param cookieName
634 * Name of the cookie.
635 * @param cookies
636 * The cookies from the HTTP request.
637 * @return Requested value, empty string if not found.
638 * @throws IllegalArgumentException
639 * If <code>cookieName</code> is null or an empty string.
640 */
641 public static String getCookieValue(final String cookieName, final Cookie[] cookies) {
642
643
644 if (cookieName == null || cookieName.equals(""))
645 throw new IllegalArgumentException("Cookie name must be a non-empty string");
646 if (cookies == null)
647 return null;
648
649
650 String value = null;
651 for (int i = 0; i < cookies.length; i++) {
652 if (cookies[i].getName().equals(cookieName))
653 value = cookies[i].getValue();
654 }
655 return value;
656
657 }
658
659
660 /***
661 * Creates a cookie.
662 * @param cookieName
663 * Name of the cookie. Cannot be <code>null</code>.
664 * @param cookieValue
665 * Value to set in cookie. Cannot be <code>null</code>.
666 * @param validHours
667 * Number of hours before the cookie expires. Must be 0 or
668 * greater.
669 * @return A Cookie with the specified name and value, with the given expiry
670 * time.
671 * @throws IllegalArgumentException
672 * If <code>cookieName</code> or <code>cookieValue</code> is
673 * <code>null</code> or an empty string.
674 */
675 public static Cookie createCookie(final String cookieName, final String cookieValue, final int validHours) {
676
677
678 if (cookieName == null || cookieName.equals(""))
679 throw new IllegalArgumentException("Cookie name must be a non-empty string");
680 if (cookieValue == null || cookieValue.equals(""))
681 throw new IllegalArgumentException("Cookie value must be a non-empty string");
682
683
684 final Cookie cookie = new Cookie(cookieName, cookieValue);
685 cookie.setMaxAge(validHours * 60 * 60);
686 cookie.setVersion(0);
687 return cookie;
688 }
689
690
691 /***
692 * Parses an Accept-Language header sent from a browser. The language
693 * entries in the string can be weighted and the parser generates a list of
694 * the languages sorted by the weight value.
695 * @param acceptLang
696 * The accept language header, cannot be null or "".
697 * @return A string array of language names, sorted by the browser's weight
698 * preferences.
699 * @throws IllegalArgumentException
700 * If <code>acceptLang</code> is null or an empty string.
701 */
702 static String[] sortedAcceptLang(final String acceptLang) {
703
704 if (acceptLang == null || acceptLang.equals("")) { throw new IllegalArgumentException("acceptLang must be a non-empty string."); }
705
706 final StringTokenizer tokenizer = new StringTokenizer(acceptLang, ",");
707 final HashMap weightedLangs = new HashMap();
708
709 while (tokenizer.hasMoreTokens()) {
710 final String token = tokenizer.nextToken();
711 String lang = token;
712 boolean ignore = false;
713 String weight = "1.0";
714 int index;
715
716
717 if ((index = token.indexOf(";")) != -1) {
718 String parsedWeight;
719 lang = token.substring(0, index);
720
721
722 parsedWeight = token.substring(index + 1, token.length());
723 parsedWeight = parsedWeight.trim();
724 if (parsedWeight.startsWith("q=")) {
725 parsedWeight = parsedWeight.substring(2, parsedWeight.length());
726 weight = parsedWeight;
727 } else {
728
729 ignore = true;
730 }
731 }
732
733 if (!ignore) {
734 lang = lang.trim();
735
736
737 if ((index = lang.indexOf("-")) != -1) {
738 lang = lang.substring(index + 1, lang.length());
739 }
740
741 weightedLangs.put(weight, lang);
742 }
743 }
744
745 final Vector sortedLangs = new Vector();
746 final String[] sortedKeys = (String[]) weightedLangs.keySet().toArray(new String[weightedLangs.size()]);
747 Arrays.sort(sortedKeys, Collections.reverseOrder());
748
749 for (int i = 0; i < sortedKeys.length; i++) {
750 sortedLangs.add(weightedLangs.get(sortedKeys[i]));
751 }
752
753 return (String[]) sortedLangs.toArray(new String[sortedLangs.size()]);
754 }
755
756
757 /***
758 * Reads institution names from the servlet configuration and generates a
759 * TreeMap with the result, using the correct language. <br>
760 * <br>
761 * The configuration <em>must</em> contain properties on the form
762 * <code>element + "_" + language</code>.
763 * @param config
764 * The web module configuration.
765 * @param element
766 * The sub-element of the web module configuration to process.
767 * @param language
768 * The language used when generating institution names.
769 * @return A <code>TreeMap</code> of institution names with full name as
770 * key and ID as value.
771 * @throws IllegalArgumentException
772 * If <code>config</code> is <code>null</code>, or if
773 * <code>element</code> or <code>language</code> is
774 * <code>null</code> or an empty string.
775 * @throws IllegalStateException
776 * If no elements of type <code>element</code> are found in
777 * the configuration <code>config</code>. Also thrown if the
778 * values found in <code>config</code> contains less than or
779 * more than one occurrence of the ':' separator character.
780 */
781 static TreeMap parseConfig(final Properties config, final String element, final String language) {
782
783
784 if (config == null)
785 throw new IllegalArgumentException("Configuration cannot be null");
786 if (element == null || element.equals(""))
787 throw new IllegalArgumentException("Configuration element must be a non-empty string");
788 if (language == null || language.equals(""))
789 throw new IllegalArgumentException("Institution name language must be a non-empty string");
790
791
792 final String value = config.getProperty(element + "_" + language);
793 if (value == null)
794 throw new IllegalStateException("No elements of type '" + element + "' in configuration.");
795
796
797 final StringTokenizer tokenizer = new StringTokenizer(value, ",");
798 final TreeMap names = new TreeMap();
799 while (tokenizer.hasMoreTokens()) {
800 final String token = tokenizer.nextToken();
801
802
803 final int index = token.indexOf(":");
804 if (index == -1)
805 throw new IllegalStateException("Missing ':' separator in language declaration: '" + token + "'");
806
807
808
809 final String shortName = token.substring(0, index);
810 final String longName = token.substring(index + 1, token.length());
811
812
813 if (shortName.indexOf(":") != -1 || longName.indexOf(":") != -1)
814 throw new IllegalStateException("Config has wrong format.");
815
816
817
818 names.put(longName, shortName);
819 }
820
821
822 return names;
823 }
824
825
826 /***
827 * Replaces a given token with hyperlinks. The URL and name of the hyperlink
828 * is given as parameters. Every occurance of the token in the data string
829 * is replaced by a hyperlink.
830 * @param token
831 * The token to replace with link.
832 * @param data
833 * The data containing text and token(s).
834 * @param name
835 * The link text.
836 * @param url
837 * The URL to link to.
838 * @return A string with hyperlinks in stead of tokens.
839 * @throws IllegalArgumentException
840 * If token, data, name or url is null or an empty string.
841 */
842 public static String insertLink(final String token, final String data, final String name, final String url) {
843
844
845 if (token == null || token.equals("")) { throw new IllegalArgumentException("token must be a non-empty string"); }
846 if (data == null || data.equals("")) { throw new IllegalArgumentException("data must be a non-empty string"); }
847 if (name == null || name.equals("")) { throw new IllegalArgumentException("name must be a non-empty string"); }
848 if (url == null || url.equals("")) { throw new IllegalArgumentException("url must be a non-empty string"); }
849
850 final String link = "<a href=\"" + url + "\">" + name + "</a>";
851
852 return data.replaceAll(token, link);
853 }
854
855
856 /***
857 * Get the config from the context. The configuration is expected to be set
858 * by the controller before requests are sent to this servlet.
859 * @param context
860 * ServletContext containing the configuration.
861 * @return The configuration.
862 * @throws IllegalStateException
863 * If config is not properly set in the context.
864 */
865 static Properties getConfig(final ServletContext context) {
866
867
868 if (context == null) { throw new IllegalArgumentException("context cannot be null."); }
869
870 final Properties config;
871
872
873 try {
874 config = (Properties) context.getAttribute("no.feide.moria.web.config");
875 } catch (ClassCastException e) {
876 throw new IllegalStateException("Config is not correctly set in context. Not a java.util.Properties object.");
877 }
878
879 if (config == null) { throw new IllegalStateException("Config is not set in context."); }
880
881 return config;
882 }
883 }