View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.ken.services.ws.impl;
17  
18  import java.text.DateFormat;
19  import java.text.MessageFormat;
20  import java.text.SimpleDateFormat;
21  import java.util.Calendar;
22  import java.util.Date;
23  import java.util.HashSet;
24  import java.util.Random;
25  import java.util.Set;
26  import java.util.TimeZone;
27  
28  import org.apache.commons.lang.RandomStringUtils;
29  import org.apache.log4j.Logger;
30  import org.junit.Ignore;
31  import org.junit.Test;
32  
33  /**
34   * Load tester for notification
35   * @author Kuali Rice Team (rice.collab@kuali.org)
36   */
37  @Ignore
38  public class NotificationLoadTester extends NotificationUsageSimulator {
39      private static final Logger LOG = Logger.getLogger(NotificationLoadTester.class);
40  
41      // from http://ws.apache.org/wss4j/xref/org/apache/ws/security/util/XmlSchemaDateFormat.html
42      // thanks wss4j
43      /***
44       * DateFormat for Zulu (UTC) form of an XML Schema dateTime string.
45       * This DateFormat will match the XSD datetime builtin type.
46       */
47      private static final DateFormat DATEFORMAT_XSD_ZULU = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"/*.SSS'Z'"*/);
48      
49      static {
50          DATEFORMAT_XSD_ZULU.setTimeZone(TimeZone.getTimeZone("UTC"));
51      }
52  
53      /**
54       * 0 channel
55       * 1 producer
56       * 2 senders
57       * 3 recipients
58       * 4 deliverytype
59       * 5 senddatetime
60       * 6 autoremovedatetime
61       * 7 priority
62       * 8 message
63       */
64      protected static final String MSG_FORMAT_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!-- A Simple Notification Message -->" +
65      "<notification xmlns=\"ns:notification/NotificationRequest\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
66      "xsi:schemaLocation=\"ns:notification/NotificationRequest resource:notification/NotificationRequest\"><!-- this is the " +
67      "name of the notification channel --><!-- that has been registered in the system --><channel>{0}</channel>" +
68      " <!-- this is the name of the producing system --><!-- the value must match a registered producer --><producer>{1}</producer>" +
69      "<!-- these are the people that the message is sent on --><!-- behalf of --><senders>{2}</senders>" +
70      "<!-- who is the notification going to? --><recipients>{3}" +
71      "</recipients><!--  fyi or acknowledge --><deliveryType>{4}</deliveryType><!-- optional date and time that a notification should be sent -->" +
72      "<!-- use this for scheduling a single future notification to happen --><sendDateTime>{5}</sendDateTime>" +
73      "<!-- optional date and time that a notification should be removed --><!-- from all recipients'' lists, b/c the message no longer applies -->" +
74      "<autoRemoveDateTime>{6}</autoRemoveDateTime><title>a title</title><!-- this is the name of the priority of the message -->" +
75      "<!-- priorities are registered in the system, so your value --><!-- here must match one of the registered priorities --><priority>{7}</priority>" +
76      "<!-- this is the name of the content type for the message --><!-- content types are registered in the system, so your value -->" +
77      "<!-- here must match one of the registered contents --><contentType>Simple</contentType><!-- actual content of the message -->" +
78      "<content xmlns=\"ns:notification/ContentTypeSimple\" xsi:schemaLocation=\"ns:notification/ContentTypeSimple resource:notification/ContentTypeSimple\">" +
79      "<message>{8}</message></content></notification>";
80      
81      protected static final MessageFormat NOTIFICATION_TEMPLATE = new MessageFormat(MSG_FORMAT_STRING);
82  
83      protected static final String[] CHANNELS = { "Test Channel #1", "Test Channel #2" };
84      //protected static final String[] CHANNELS = { "Kuali Rice Channel", "Library Events Channel", "Overdue Books" };
85      protected static final String[] PRODUCERS = { "Test Producer #1", "Test Producer #2", "Test Producer #3", "Test Producer #4" };
86      //protected static final String[] PRODUCERS = { "Notification System" };
87      protected static final String[] RECIPIENTS = { "<group>Group0</group>", "<user>user1</user>", "<user>user2</user>", "<user>user3</user>", "<user>edna</user>", "<user>earl</user>" };
88      protected static final String[] SENDERS = { "<sender>John Fereira</sender>", "<sender>Aaron Godert</sender>", "<sender>Aaron Hamid</sender>" };
89      protected static final String[] DELIVERY_TYPES = { "FYI", "ACK" };
90      protected static final String[] PRIORITIES = { "Normal", "Low", "High" };
91  
92      protected static String generateXMLDateTime(Date date) {
93          return DATEFORMAT_XSD_ZULU.format(date);
94      }
95  
96      protected Random random = new Random();
97  
98      private String webServiceHost = null;
99  
100     protected String selectRandomEntry(String[] entries) {
101         return entries[random.nextInt(entries.length)];
102     }
103 
104     protected String generateRandomChannel() {
105         return selectRandomEntry(CHANNELS);
106     }
107 
108     protected String generateRandomRecipient() {
109         return selectRandomEntry(RECIPIENTS);
110     }
111     
112     protected String generateRandomProducer() {
113         return selectRandomEntry(PRODUCERS);
114     }
115     
116     protected String generateRandomSender() {
117         return selectRandomEntry(SENDERS);
118     }
119     
120     protected String generateRandomDeliveryType() {
121         return selectRandomEntry(DELIVERY_TYPES);
122     }
123 
124     protected String generateRandomPriority() {
125         return selectRandomEntry(PRIORITIES);
126     }
127 
128     protected String generateRandomSenders() {
129         // senders must be unique, no duplicates
130         Set<String> senderStrings = new HashSet<String>();
131         /*ALTER TABLE NOTIFICATION_SENDERS
132         ADD CONSTRAINT NOTIFICATION_SENDERS_UK1 UNIQUE
133         (
134         NOTIFICATION_ID,
135         NAME
136         )
137          ENABLE
138         ;*/
139         // since senders must be unique, we obviously can't attempt to select
140         // more than the maximum number of senders
141         // (this will busy-spin until it gets the required number of unique
142         // entries, oh well)
143         int senders = 1 + random.nextInt(SENDERS.length);
144         while (senderStrings.size() < senders) {
145             String sender = generateRandomSender();
146             if (!senderStrings.contains(sender)) {
147                 senderStrings.add(sender);
148             }
149 
150         }
151         StringBuilder sendersString = new StringBuilder();
152         for (String s: senderStrings) {
153             sendersString.append(s);
154         }
155         return sendersString.toString();
156     }
157 
158     protected String generateRandomRecipients() {
159         // recipients must be unique also
160         Set<String> recipStrings = new HashSet<String>();
161         /*ALTER TABLE NOTIFICATION_RECIPIENTS
162         ADD CONSTRAINT NOTIFICATION_RECIPIENTS_UK1 UNIQUE
163         (
164         NOTIFICATION_ID,
165         RECIPIENT_TYPE,
166         RECIPIENT_ID
167         )
168          ENABLE
169         ;
170         */
171         // 5% change of very large list of recipients
172         boolean largeNumberOfRecipients = random.nextInt(100) < 5;
173         int recipients = 1 + random.nextInt(5);
174         if (largeNumberOfRecipients) {
175             recipients += 20 + random.nextInt(10);
176         }
177         recipients = Math.max(recipients, RECIPIENTS.length);
178 
179         while (recipStrings.size() < recipients) {
180             String recip = generateRandomRecipient();
181             if (!recipStrings.contains(recip)) {
182                 recipStrings.add(recip);
183             }
184         }
185 
186         StringBuilder recipientsString = new StringBuilder();
187         for (String s: recipStrings) {
188             recipientsString.append(s);
189         }
190         
191         return recipientsString.toString();
192     }
193 
194     protected String generateRandomMessage() {
195         int size = 100 + random.nextInt(300);
196         return RandomStringUtils.randomAlphanumeric(size);
197     }
198 
199     @Override
200     protected String generateNotificationMessage() {
201         /**
202          * 0 channel
203          * 1 producer
204          * 2 senders
205          * 3 recipients
206          * 4 deliverytype
207          * 5 senddatetime
208          * 6 autoremovedatetime
209          * 7 priority
210          * 8 message
211          */
212         String channel = generateRandomChannel();
213         String producer = generateRandomProducer();
214         String senders = generateRandomSenders();
215         String recipients = generateRandomRecipients();
216         String deliverytype = generateRandomDeliveryType();
217         Calendar cal = Calendar.getInstance();
218         cal.add(Calendar.HOUR, -1);
219         // set send time back an hour to make sure it's sent immediately
220         String senddatetime = generateXMLDateTime(cal.getTime());
221         // set autoremove time forward a day
222         cal = Calendar.getInstance();
223         cal.add(Calendar.DATE, +1);
224         String autoRemoveDateTime = generateXMLDateTime(cal.getTime());
225         String priority = generateRandomPriority();
226         String message = generateRandomMessage();
227         
228         String[] args = new String[] { channel, producer, senders, recipients, deliverytype, senddatetime, autoRemoveDateTime, priority, message };
229         //LOG.info("args: " + channel + " " + producer + " " + senders + " " + recipients + " " + deliverytype + " " + senddatetime + " " + autoRemoveDateTime + " " + priority + " " + message);
230         
231         String notification;
232         synchronized (NOTIFICATION_TEMPLATE) {
233             //LOG.info(MSG_FORMAT_STRING);
234             //LOG.info(NOTIFICATION_TEMPLATE);
235             //LOG.info(NOTIFICATION_TEMPLATE.toPattern());
236             //LOG.info("ARGS: " + NOTIFICATION_TEMPLATE.getFormats().length);
237             assertTrue(NOTIFICATION_TEMPLATE.getFormats().length == 9);
238             notification = NOTIFICATION_TEMPLATE.format(args);
239         }
240         //LOG.debug("Generated notification: " + notification);
241         return notification;
242     }
243 
244     @Override
245     protected int getNumThreads() {
246         return 10;
247     }
248 
249     @Override
250     protected long getSleepTimeMillis() {
251         return 2000;
252     }
253 
254     @Override
255     protected long getTestDuration() {
256         return 1000 * 60; // * 5; // 5 minutes
257     }
258 
259     /* I guess preventTransaction() is not available in this version of spring?? 
260     @Override
261     public void runBare() throws Throwable {
262         preventTransaction();
263     }
264     */
265 
266     /**
267      * Override runTest directly, as it is seems not to be called by either Eclipse or Ant JUnit test runner
268      * (which is the behavoir we want: we don't want a load test included with all the other tests)
269      */
270     @Test
271     public void runTest() throws Throwable {
272         // don't bother rolling back anything that was committed within the unit test transaction
273         //setComplete();
274 
275         // expose this method in this subclass for JUnit
276         super.runSimulation();
277 
278     }
279 
280     @Override
281     protected int getWebServicePort() {
282         return 8080;
283     }
284 
285     @Override
286     protected String getWebServiceHost() {
287         return webServiceHost;
288     }
289 
290     public void setWebServiceHost(String s) {
291         this.webServiceHost = s;
292     }
293 
294     @Override
295     protected boolean shouldStartWebService() {
296         return false;
297     }
298 
299     public static void main(String[] args) {
300         // can't use anonymous class to expose the real test
301         // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957
302         NotificationLoadTester test = new NotificationLoadTester();
303         if (args.length > 0) {
304             test.setWebServiceHost(args[0]);
305         }
306         //TestResult result = TestRunner.run(test);
307     }
308 }