001/*
002 * Copyright 2009 Red Hat, Inc.
003 * Red Hat licenses this file to you under the Apache License, version
004 * 2.0 (the "License"); you may not use this file except in compliance
005 * with the License.  You may obtain a copy of the License at
006 *    http://www.apache.org/licenses/LICENSE-2.0
007 * Unless required by applicable law or agreed to in writing, software
008 * distributed under the License is distributed on an "AS IS" BASIS,
009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
010 * implied.  See the License for the specific language governing
011 * permissions and limitations under the License.
012 */
013
014package org.hornetq.api.core.management;
015
016import java.util.Collections;
017import java.util.HashMap;
018import java.util.Iterator;
019import java.util.Map;
020
021import org.hornetq.api.core.Message;
022import org.hornetq.api.core.SimpleString;
023import org.hornetq.core.logging.Logger;
024import org.hornetq.utils.json.JSONArray;
025import org.hornetq.utils.json.JSONObject;
026
027/**
028 * Helper class to use HornetQ Core messages to manage server resources.
029 * 
030 * @author <a href="mailto:jmesnil@redhat.com">Jeff Mesnil</a>
031 * @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
032 * 
033 * @version <tt>$Revision$</tt>
034 */
035public class ManagementHelper
036{
037   // Constants -----------------------------------------------------
038
039   private static final Logger log = Logger.getLogger(ManagementHelper.class);
040
041   public static final SimpleString HDR_RESOURCE_NAME = new SimpleString("_HQ_ResourceName");
042
043   public static final SimpleString HDR_ATTRIBUTE = new SimpleString("_HQ_Attribute");
044
045   public static final SimpleString HDR_OPERATION_NAME = new SimpleString("_HQ_OperationName");
046
047   public static final SimpleString HDR_OPERATION_SUCCEEDED = new SimpleString("_HQ_OperationSucceeded");
048
049   public static final SimpleString HDR_NOTIFICATION_TYPE = new SimpleString("_HQ_NotifType");
050
051   public static final SimpleString HDR_NOTIFICATION_TIMESTAMP = new SimpleString("_HQ_NotifTimestamp");
052
053   public static final SimpleString HDR_ROUTING_NAME = new SimpleString("_HQ_RoutingName");
054
055   public static final SimpleString HDR_CLUSTER_NAME = new SimpleString("_HQ_ClusterName");
056
057   public static final SimpleString HDR_ADDRESS = new SimpleString("_HQ_Address");
058
059   public static final SimpleString HDR_BINDING_ID = new SimpleString("_HQ_Binding_ID");
060
061   public static final SimpleString HDR_BINDING_TYPE = new SimpleString("_HQ_Binding_Type");
062
063   public static final SimpleString HDR_FILTERSTRING = new SimpleString("_HQ_FilterString");
064
065   public static final SimpleString HDR_DISTANCE = new SimpleString("_HQ_Distance");
066
067   public static final SimpleString HDR_CONSUMER_COUNT = new SimpleString("_HQ_ConsumerCount");
068
069   public static final SimpleString HDR_USER = new SimpleString("_HQ_User");
070
071   public static final SimpleString HDR_CHECK_TYPE = new SimpleString("_HQ_CheckType");
072
073   public static final SimpleString HDR_PROPOSAL_GROUP_ID = new SimpleString("_JBM_ProposalGroupId");
074
075   public static final SimpleString HDR_PROPOSAL_VALUE = new SimpleString("_JBM_ProposalValue");
076
077   public static final SimpleString HDR_PROPOSAL_ALT_VALUE = new SimpleString("_JBM_ProposalAltValue");
078
079   // Attributes ----------------------------------------------------
080
081   // Static --------------------------------------------------------
082
083   /**
084    * Stores a resource attribute in a message to retrieve the value from the server resource.
085    * 
086    * @param message message
087    * @param resourceName the name of the resource
088    * @param attribute the name of the attribute
089    * 
090    * @see ResourceNames
091    */
092   public static void putAttribute(final Message message, final String resourceName, final String attribute)
093   {
094      message.putStringProperty(ManagementHelper.HDR_RESOURCE_NAME, new SimpleString(resourceName));
095      message.putStringProperty(ManagementHelper.HDR_ATTRIBUTE, new SimpleString(attribute));
096   }
097
098   /**
099    * Stores a operation invocation in a message to invoke the corresponding operation the value from the server resource.
100    * 
101    * @param message  message
102    * @param resourceName the name of the resource
103    * @param operationName the name of the operation to invoke on the resource
104    *
105    * @see ResourceNames
106    */
107   public static void putOperationInvocation(final Message message,
108                                             final String resourceName,
109                                             final String operationName) throws Exception
110   {
111      ManagementHelper.putOperationInvocation(message, resourceName, operationName, (Object[])null);
112   }
113
114   /**
115    * Stores a operation invocation in a  message to invoke the corresponding operation the value from the server resource.
116    * 
117    * @param message  message
118    * @param resourceName the name of the server resource
119    * @param operationName the name of the operation to invoke on the server resource
120    * @param parameters the parameters to use to invoke the server resource
121    *
122    * @see ResourceNames
123    */
124   public static void putOperationInvocation(final Message message,
125                                             final String resourceName,
126                                             final String operationName,
127                                             final Object... parameters) throws Exception
128   {
129      // store the name of the operation in the headers
130      message.putStringProperty(ManagementHelper.HDR_RESOURCE_NAME, new SimpleString(resourceName));
131      message.putStringProperty(ManagementHelper.HDR_OPERATION_NAME, new SimpleString(operationName));
132
133      // and the params go in the body, since might be too large for header
134
135      String paramString;
136
137      if (parameters != null)
138      {
139         JSONArray jsonArray = ManagementHelper.toJSONArray(parameters);
140
141         paramString = jsonArray.toString();
142      }
143      else
144      {
145         paramString = null;
146      }
147
148      message.getBodyBuffer().writeNullableSimpleString(SimpleString.toSimpleString(paramString));
149   }
150
151   private static JSONArray toJSONArray(final Object[] array) throws Exception
152   {
153      JSONArray jsonArray = new JSONArray();
154
155      for (Object parameter : array)
156      {
157         if (parameter instanceof Map)
158         {
159            Map<String, Object> map = (Map<String, Object>)parameter;
160
161            JSONObject jsonObject = new JSONObject();
162
163            for (Map.Entry<String, Object> entry : map.entrySet())
164            {
165               String key = entry.getKey();
166
167               Object val = entry.getValue();
168
169               if (val != null)
170               {
171                  if (val.getClass().isArray())
172                  {
173                     val = ManagementHelper.toJSONArray((Object[])val);
174                  }
175                  else
176                  {
177                     ManagementHelper.checkType(val);
178                  }
179               }
180
181               jsonObject.put(key, val);
182            }
183
184            jsonArray.put(jsonObject);
185         }
186         else
187         {
188            if (parameter != null)
189            {
190               Class clz = parameter.getClass();
191
192               if (clz.isArray())
193               {
194                  Object[] innerArray = (Object[])parameter;
195
196                  jsonArray.put(ManagementHelper.toJSONArray(innerArray));
197               }
198               else
199               {
200                  ManagementHelper.checkType(parameter);
201
202                  jsonArray.put(parameter);
203               }
204            }
205            else
206            {
207               jsonArray.put((Object)null);
208            }
209         }
210      }
211
212      return jsonArray;
213   }
214
215   private static Object[] fromJSONArray(final JSONArray jsonArray) throws Exception
216   {
217      Object[] array = new Object[jsonArray.length()];
218
219      for (int i = 0; i < jsonArray.length(); i++)
220      {
221         Object val = jsonArray.get(i);
222
223         if (val instanceof JSONArray)
224         {
225            Object[] inner = ManagementHelper.fromJSONArray((JSONArray)val);
226
227            array[i] = inner;
228         }
229         else if (val instanceof JSONObject)
230         {
231            JSONObject jsonObject = (JSONObject)val;
232
233            Map<String, Object> map = new HashMap<String, Object>();
234
235            Iterator<String> iter = jsonObject.keys();
236
237            while (iter.hasNext())
238            {
239               String key = iter.next();
240
241               Object innerVal = jsonObject.get(key);
242
243               if (innerVal instanceof JSONArray)
244               {
245                  innerVal = ManagementHelper.fromJSONArray(((JSONArray)innerVal));
246               }
247               else if (innerVal instanceof JSONObject)
248               {
249                  Map<String, Object> innerMap = new HashMap<String, Object>();
250                  JSONObject o = (JSONObject)innerVal;
251                  Iterator it = o.keys();
252                  while (it.hasNext())
253                  {
254                     String k = (String)it.next();
255                     innerMap.put(k, o.get(k));
256                  }
257                  innerVal = innerMap;
258               }
259               else if (innerVal instanceof Integer)
260               {
261                  innerVal = ((Integer)innerVal).longValue();
262               }
263
264               map.put(key, innerVal);
265            }
266
267            array[i] = map;
268         }
269         else
270         {
271            if (val == JSONObject.NULL)
272            {
273               array[i] = null;
274            }
275            else
276            {
277               array[i] = val;
278            }
279         }
280      }
281
282      return array;
283   }
284
285   private static void checkType(final Object param)
286   {
287      if (param instanceof Integer == false && param instanceof Long == false &&
288          param instanceof Double == false &&
289          param instanceof String == false &&
290          param instanceof Boolean == false &&
291          param instanceof Map == false &&
292          param instanceof Byte == false &&
293          param instanceof Short == false)
294      {
295         throw new IllegalArgumentException("Params for management operations must be of the following type: " + "int long double String boolean Map or array thereof " +
296                                            " but found " +
297                                            param.getClass().getName());
298      }
299   }
300
301   /**
302    * Used by HornetQ management service.
303    */
304   public static Object[] retrieveOperationParameters(final Message message) throws Exception
305   {
306      SimpleString sstring = message.getBodyBuffer().readNullableSimpleString();
307      String jsonString = (sstring == null) ? null : sstring.toString();
308
309      if (jsonString != null)
310      {
311         JSONArray jsonArray = new JSONArray(jsonString);
312
313         return ManagementHelper.fromJSONArray(jsonArray);
314      }
315      else
316      {
317         return null;
318      }
319   }
320
321   /**
322    * Returns whether the JMS message corresponds to the result of a management operation invocation.
323    */
324   public static boolean isOperationResult(final Message message)
325   {
326      return message.containsProperty(ManagementHelper.HDR_OPERATION_SUCCEEDED);
327   }
328
329   /**
330    * Returns whether the JMS message corresponds to the result of a management attribute value.
331    */
332   public static boolean isAttributesResult(final Message message)
333   {
334      return !ManagementHelper.isOperationResult(message);
335   }
336
337   /**
338    * Used by HornetQ management service.
339    */
340   public static void storeResult(final Message message, final Object result) throws Exception
341   {
342      String resultString;
343
344      if (result != null)
345      {
346         // Result is stored in body, also encoded as JSON array of length 1
347
348         JSONArray jsonArray = ManagementHelper.toJSONArray(new Object[] { result });
349
350         resultString = jsonArray.toString();
351      }
352      else
353      {
354         resultString = null;
355      }
356
357      message.getBodyBuffer().writeNullableSimpleString(SimpleString.toSimpleString(resultString));
358   }
359
360   /**
361    * Returns the result of an operation invocation or an attribute value.
362    * <br>
363    * If an error occurred on the server, {@link #hasOperationSucceeded(Message)} will return {@code false}.
364    * and the result will be a String corresponding to the server exception.
365    */
366   public static Object[] getResults(final Message message) throws Exception
367   {
368      SimpleString sstring = message.getBodyBuffer().readNullableSimpleString();
369      String jsonString = (sstring == null) ? null : sstring.toString();
370                                           ;
371      if (jsonString != null)
372      {
373         JSONArray jsonArray = new JSONArray(jsonString);
374
375         Object[] res = ManagementHelper.fromJSONArray(jsonArray);
376
377         return res;
378      }
379      else
380      {
381         return null;
382      }
383   }
384
385   /**
386    * Returns the result of an operation invocation or an attribute value.
387    * <br>
388    * If an error occurred on the server, {@link #hasOperationSucceeded(Message)} will return {@code false}.
389    * and the result will be a String corresponding to the server exception.
390    */
391   public static Object getResult(final Message message) throws Exception
392   {
393      Object[] res = ManagementHelper.getResults(message);
394
395      if (res != null)
396      {
397         return res[0];
398      }
399      else
400      {
401         return null;
402      }
403   }
404
405   /**
406    * Returns whether the invocation of the management operation on the server resource succeeded.
407    */
408   public static boolean hasOperationSucceeded(final Message message)
409   {
410      if (!ManagementHelper.isOperationResult(message))
411      {
412         return false;
413      }
414      if (message.containsProperty(ManagementHelper.HDR_OPERATION_SUCCEEDED))
415      {
416         return message.getBooleanProperty(ManagementHelper.HDR_OPERATION_SUCCEEDED);
417      }
418      return false;
419   }
420
421   /**
422    * Used by HornetQ management service.
423    */   
424   public static Map<String, Object> fromCommaSeparatedKeyValues(final String str) throws Exception
425   {
426      if (str == null || str.trim().length() == 0)
427      {
428         return Collections.emptyMap();
429      }
430
431      // create a JSON array with 1 object:
432      JSONArray array = new JSONArray("[{" + str + "}]");
433      Map<String, Object> params = (Map<String, Object>)ManagementHelper.fromJSONArray(array)[0];
434      return params;
435   }
436
437   /**
438    * Used by HornetQ management service.
439    */
440   public static Object[] fromCommaSeparatedArrayOfCommaSeparatedKeyValues(final String str) throws Exception
441   {
442      if (str == null || str.trim().length() == 0)
443      {
444         return new Object[0];
445      }
446
447      String s = str;
448
449      // if there is a single item, we wrap it in to make it a JSON object
450      if (!s.trim().startsWith("{"))
451      {
452         s = "{" + s + "}";
453      }
454      JSONArray array = new JSONArray("[" + s + "]");
455      return ManagementHelper.fromJSONArray(array);
456   }
457
458   // Constructors --------------------------------------------------
459
460   private ManagementHelper()
461   {
462   }
463
464   // Public --------------------------------------------------------
465
466   // Package protected ---------------------------------------------
467
468   // Protected -----------------------------------------------------
469
470   // Private -------------------------------------------------------
471
472   // Inner classes -------------------------------------------------
473}