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
31import java.util.*;
32import java.util.concurrent.atomic.*;
33
34// Usage: java StressTest [threadsFactor [duration]]
35public 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    static final 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}
220