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  * @key randomness
  29  */
  30 
  31 import java.util.*;
  32 import java.util.concurrent.atomic.*;
  33 
  34 // Usage: java StressTest [threadsFactor [duration]]
  35 public class StressTest {
  36     static final Locale ROOT_LOCALE = new Locale("");
  37     static final Random rand = new Random();
  38     static final Locale[] locales = {
  39         Locale.US,
  40         Locale.CHINA,
  41         ROOT_LOCALE,
  42         Locale.JAPAN,
  43         Locale.CANADA,
  44         Locale.KOREA
  45     };
  46     static final String[] expected = {
  47         "U.S.A.",
  48         "China",
  49         "U.S.A.",
  50         "Japan",
  51         "U.S.A.", // StressOut_en_CA.properties is empty.
  52         "Korea"
  53     };
  54     final static long startTime = System.currentTimeMillis();
  55 
  56     // increment each element when one getBundle call is done.
  57     static AtomicIntegerArray counters;
  58     static int[] prevCounters;
  59     static int intervalForCounterCheck;
  60     static AtomicInteger clearCounter = new AtomicInteger();
  61 
  62     static volatile boolean runrun = true;
  63 
  64     public static void main(String[] args) {
  65         int threadsFactor = 2;
  66         if (args.length > 0) {
  67             threadsFactor = Math.max(2, Integer.parseInt(args[0]));
  68         }
  69         int duration = 180;
  70         if (args.length > 1) {
  71             duration = Math.max(5, Integer.parseInt(args[1]));
  72         }
  73 
  74         Locale reservedLocale = Locale.getDefault();
  75         try {
  76             Locale.setDefault(Locale.US);
  77             Thread[] tasks = new Thread[locales.length * threadsFactor];
  78             counters = new AtomicIntegerArray(tasks.length);
  79 
  80             for (int i = 0; i < tasks.length; i++) {
  81                 tasks[i] = new Thread(new Worker(i));
  82             }
  83             for (int i = 0; i < tasks.length; i++) {
  84                 tasks[i].start();
  85             }
  86 
  87             int nProcessors = Runtime.getRuntime().availableProcessors();
  88             intervalForCounterCheck = Math.max(tasks.length / nProcessors, 1);
  89             System.out.printf(
  90                 "%d processors, intervalForCounterCheck = %d [sec]%n",
  91                           nProcessors, intervalForCounterCheck);
  92             try {
  93                 for (int i = 0; runrun && i < duration; i++) {
  94                     Thread.sleep(1000); // 1 second
  95                     if ((i % intervalForCounterCheck) == 0) {
  96                         checkCounters();
  97                     }
  98                 }
  99                 runrun = false;
 100                 for (int i = 0; i < tasks.length; i++) {
 101                     tasks[i].join();
 102                 }
 103             } catch (InterruptedException e) {
 104             }
 105 
 106             printCounters();
 107         } finally {
 108             // restore the reserved locale
 109             Locale.setDefault(reservedLocale);
 110         }
 111     }
 112 
 113     static void checkCounters() {
 114         int length = counters.length();
 115         int[] snapshot = new int[length];
 116         for (int i = 0; i < length; i++) {
 117             snapshot[i] = counters.get(i);
 118         }
 119 
 120         if (prevCounters == null) {
 121             prevCounters = snapshot;
 122             return;
 123         }
 124 
 125         for (int i = 0; i < length; i++) {
 126             if (snapshot[i] > prevCounters[i]) {
 127                 continue;
 128             }
 129             System.out.printf(
 130                 "Warning: Thread #%d hasn't updated its counter for the last %d second(s).%n",
 131                 i, intervalForCounterCheck);
 132         }
 133         prevCounters = snapshot;
 134     }
 135 
 136     static void printCounters() {
 137         long total = 0;
 138         int min = Integer.MAX_VALUE;
 139         int max = Integer.MIN_VALUE;
 140         for (int i = 0; i < counters.length(); i++) {
 141             int counter = counters.get(i);
 142             total += counter;
 143             min = Math.min(min, counter);
 144             max = Math.max(max, counter);
 145         }
 146         System.out.printf("Total: %d calls, min=%d, max=%d, cache cleared %d times%n",
 147                           total, min, max, clearCounter.get());
 148     }
 149 
 150     static class Worker implements Runnable {
 151         final int id;
 152         final int index;
 153         final Locale locale;
 154         final String str;
 155         final int max;
 156         final boolean cleaner;
 157         ResourceBundle.Control control;
 158 
 159         Worker(int i) {
 160             id = i;
 161             index = i % locales.length;
 162             locale = locales[index];
 163             cleaner = locale.equals(ROOT_LOCALE);
 164             str = expected[index];
 165             max = rand.nextInt((index + 1) * 500) + 1000;
 166             control = new TestControl(max);
 167             System.out.println("Worker" + i + ": locale="+locale+", expected="+str+
 168                                ", max="+max);
 169         }
 170 
 171         public void run() {
 172             while (runrun) {
 173                 ResourceBundle rb = ResourceBundle.getBundle("StressOut", locale, control);
 174                 counters.incrementAndGet(id);
 175                 String s = rb.getString("data");
 176                 if (!s.equals(str)) {
 177                     runrun = false;
 178                     throw new RuntimeException(locale + ": rb.locale=" + rb.getLocale() +
 179                                                ", got " + s + ", expected " + str);
 180                 }
 181                 try {
 182                     Thread.sleep(rand.nextInt(max/500));
 183                 } catch (InterruptedException e) {
 184                 }
 185                 if (cleaner && (rand.nextInt(10000) == 0)) {
 186                     //System.out.println("Clearing cache!");
 187                     ResourceBundle.clearCache();
 188                     clearCounter.incrementAndGet();
 189                 }
 190             }
 191         }
 192 
 193         static class TestControl extends ResourceBundle.Control {
 194             int max;
 195 
 196             public List<Locale> getCandidateLocales(String baseName, Locale locale) {
 197                 List<Locale> list = super.getCandidateLocales(baseName, locale);
 198                 //System.out.println("Candidate locales=" + list);
 199                 return list;
 200             }
 201             public TestControl(int max) {
 202                 this.max = max;
 203             }
 204             public long getTimeToLive(String baseName, Locale locale) {
 205                 // This will set TTL to a random value for each bundle.
 206                 long ttl = rand.nextInt(max);
 207                 //System.out.println("TTL: " + baseName + "_" + locale + " for " + ttl);
 208                 return ttl;
 209             }
 210             public boolean needsReload(String baseName, Locale locale,
 211                                        String format, ClassLoader loader,
 212                                        ResourceBundle bundle, long loadTime) {
 213                 //System.out.println("Expired: " + baseName + "_" + locale + "." + format +
 214                 //                 " at " + (loadTime-startTime) + ", bundle=" + bundle);
 215                 return rand.nextBoolean();
 216             }
 217         }
 218     }
 219 }