1 /*
   2  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 /*
  24  * @test
  25  * @bug 5102289
  26  * @summary Stress test for ResourceBundle.getBundle with ResourceBundle.Control.
  27  * @run main/othervm -esa StressTest 2 15
  28  */
  29 
  30 import java.util.*;
  31 import java.util.concurrent.atomic.*;
  32 
  33 // Usage: java StressTest [threadsFactor [duration]]
  34 public class StressTest {
  35     static final Locale ROOT_LOCALE = new Locale("");
  36     static final Random rand = new Random();
  37     static final Locale[] locales = {
  38         Locale.US,
  39         Locale.CHINA,
  40         ROOT_LOCALE,
  41         Locale.JAPAN,
  42         Locale.CANADA,
  43         Locale.KOREA
  44     };
  45     static final String[] expected = {
  46         "U.S.A.",
  47         "China",
  48         "U.S.A.",
  49         "Japan",
  50         "U.S.A.", // StressOut_en_CA.properties is empty.
  51         "Korea"
  52     };
  53     final static long startTime = System.currentTimeMillis();
  54 
  55     // increment each element when one getBundle call is done.
  56     static AtomicIntegerArray counters;
  57     static int[] prevCounters;
  58     static int intervalForCounterCheck;
  59     static AtomicInteger clearCounter = new AtomicInteger();
  60 
  61     static volatile boolean runrun = true;
  62 
  63     public static void main(String[] args) {
  64         int threadsFactor = 2;
  65         if (args.length > 0) {
  66             threadsFactor = Math.max(2, Integer.parseInt(args[0]));
  67         }
  68         int duration = 180;
  69         if (args.length > 1) {
  70             duration = Math.max(5, Integer.parseInt(args[1]));
  71         }
  72 
  73         Locale reservedLocale = Locale.getDefault();
  74         try {
  75             Locale.setDefault(Locale.US);
  76             Thread[] tasks = new Thread[locales.length * threadsFactor];
  77             counters = new AtomicIntegerArray(tasks.length);
  78 
  79             for (int i = 0; i < tasks.length; i++) {
  80                 tasks[i] = new Thread(new Worker(i));
  81             }
  82             for (int i = 0; i < tasks.length; i++) {
  83                 tasks[i].start();
  84             }
  85 
  86             int nProcessors = Runtime.getRuntime().availableProcessors();
  87             intervalForCounterCheck = Math.max(tasks.length / nProcessors, 1);
  88             System.out.printf(
  89                 "%d processors, intervalForCounterCheck = %d [sec]%n",
  90                           nProcessors, intervalForCounterCheck);
  91             try {
  92                 for (int i = 0; runrun && i < duration; i++) {
  93                     Thread.sleep(1000); // 1 second
  94                     if ((i % intervalForCounterCheck) == 0) {
  95                         checkCounters();
  96                     }
  97                 }
  98                 runrun = false;
  99                 for (int i = 0; i < tasks.length; i++) {
 100                     tasks[i].join();
 101                 }
 102             } catch (InterruptedException e) {
 103             }
 104 
 105             printCounters();
 106         } finally {
 107             // restore the reserved locale
 108             Locale.setDefault(reservedLocale);
 109         }
 110     }
 111 
 112     static void checkCounters() {
 113         int length = counters.length();
 114         int[] snapshot = new int[length];
 115         for (int i = 0; i < length; i++) {
 116             snapshot[i] = counters.get(i);
 117         }
 118 
 119         if (prevCounters == null) {
 120             prevCounters = snapshot;
 121             return;
 122         }
 123 
 124         for (int i = 0; i < length; i++) {
 125             if (snapshot[i] > prevCounters[i]) {
 126                 continue;
 127             }
 128             System.out.printf(
 129                 "Warning: Thread #%d hasn't updated its counter for the last %d second(s).%n",
 130                 i, intervalForCounterCheck);
 131         }
 132         prevCounters = snapshot;
 133     }
 134 
 135     static void printCounters() {
 136         long total = 0;
 137         int min = Integer.MAX_VALUE;
 138         int max = Integer.MIN_VALUE;
 139         for (int i = 0; i < counters.length(); i++) {
 140             int counter = counters.get(i);
 141             total += counter;
 142             min = Math.min(min, counter);
 143             max = Math.max(max, counter);
 144         }
 145         System.out.printf("Total: %d calls, min=%d, max=%d, cache cleared %d times%n",
 146                           total, min, max, clearCounter.get());
 147     }
 148 
 149     static class Worker implements Runnable {
 150         final int id;
 151         final int index;
 152         final Locale locale;
 153         final String str;
 154         final int max;
 155         final boolean cleaner;
 156         ResourceBundle.Control control;
 157 
 158         Worker(int i) {
 159             id = i;
 160             index = i % locales.length;
 161             locale = locales[index];
 162             cleaner = locale.equals(ROOT_LOCALE);
 163             str = expected[index];
 164             max = rand.nextInt((index + 1) * 500) + 1000;
 165             control = new TestControl(max);
 166             System.out.println("Worker" + i + ": locale="+locale+", expected="+str+
 167                                ", max="+max);
 168         }
 169 
 170         public void run() {
 171             while (runrun) {
 172                 ResourceBundle rb = ResourceBundle.getBundle("StressOut", locale, control);
 173                 counters.incrementAndGet(id);
 174                 String s = rb.getString("data");
 175                 if (!s.equals(str)) {
 176                     runrun = false;
 177                     throw new RuntimeException(locale + ": rb.locale=" + rb.getLocale() +
 178                                                ", got " + s + ", expected " + str);
 179                 }
 180                 try {
 181                     Thread.sleep(rand.nextInt(max/500));
 182                 } catch (InterruptedException e) {
 183                 }
 184                 if (cleaner && (rand.nextInt(10000) == 0)) {
 185                     //System.out.println("Clearing cache!");
 186                     ResourceBundle.clearCache();
 187                     clearCounter.incrementAndGet();
 188                 }
 189             }
 190         }
 191 
 192         static class TestControl extends ResourceBundle.Control {
 193             int max;
 194 
 195             public List<Locale> getCandidateLocales(String baseName, Locale locale) {
 196                 List<Locale> list = super.getCandidateLocales(baseName, locale);
 197                 //System.out.println("Candidate locales=" + list);
 198                 return list;
 199             }
 200             public TestControl(int max) {
 201                 this.max = max;
 202             }
 203             public long getTimeToLive(String baseName, Locale locale) {
 204                 // This will set TTL to a random value for each bundle.
 205                 long ttl = rand.nextInt(max);
 206                 //System.out.println("TTL: " + baseName + "_" + locale + " for " + ttl);
 207                 return ttl;
 208             }
 209             public boolean needsReload(String baseName, Locale locale,
 210                                        String format, ClassLoader loader,
 211                                        ResourceBundle bundle, long loadTime) {
 212                 //System.out.println("Expired: " + baseName + "_" + locale + "." + format +
 213                 //                 " at " + (loadTime-startTime) + ", bundle=" + bundle);
 214                 return rand.nextBoolean();
 215             }
 216         }
 217     }
 218 }