@Override
  public List<Map<String, String>> getEndpoints(String camelContextName) throws Exception {
    if (jolokia == null) {
      throw new IllegalStateException("Need to connect to remote jolokia first");
    }

    List<Map<String, String>> answer = new ArrayList<Map<String, String>>();

    ObjectName found = lookupCamelContext(camelContextName);
    if (found != null) {
      String pattern =
          String.format(
              "%s:context=%s,type=endpoints,*", found.getDomain(), found.getKeyProperty("context"));
      J4pSearchResponse sr = jolokia.execute(new J4pSearchRequest(pattern));

      List<J4pReadRequest> list = new ArrayList<J4pReadRequest>();
      for (ObjectName on : sr.getObjectNames()) {
        list.add(new J4pReadRequest(on, "CamelId", "EndpointUri", "State"));
      }

      List<J4pReadResponse> lrr = jolokia.execute(list);
      for (J4pReadResponse rr : lrr) {
        Map<String, String> row = new LinkedHashMap<String, String>();
        row.put("camelContextName", rr.getValue("CamelId").toString());
        row.put("uri", rr.getValue("EndpointUri").toString());
        row.put("state", rr.getValue("State").toString());
        answer.add(row);
      }
    }

    return answer;
  }
  @Override
  public List<Map<String, String>> getCamelContexts() throws Exception {
    if (jolokia == null) {
      throw new IllegalStateException("Need to connect to remote jolokia first");
    }

    List<Map<String, String>> answer = new ArrayList<Map<String, String>>();

    J4pSearchResponse sr = jolokia.execute(new J4pSearchRequest("*:type=context,*"));

    List<J4pReadRequest> list = new ArrayList<J4pReadRequest>();
    for (ObjectName on : sr.getObjectNames()) {
      list.add(
          new J4pReadRequest(
              on,
              "CamelId",
              "State",
              "Uptime",
              "ExchangesTotal",
              "ExchangesInflight",
              "ExchangesFailed"));
    }

    List<J4pReadResponse> lrr = jolokia.execute(list);
    for (J4pReadResponse rr : lrr) {
      Map<String, String> row = new LinkedHashMap<String, String>();
      row.put("name", rr.getValue("CamelId").toString());
      row.put("state", rr.getValue("State").toString());
      row.put("uptime", rr.getValue("Uptime").toString());
      row.put("exchangesTotal", rr.getValue("ExchangesTotal").toString());
      row.put("exchangesInflight", rr.getValue("ExchangesInflight").toString());
      row.put("exchangesFailed", rr.getValue("ExchangesFailed").toString());
      answer.add(row);
    }

    return answer;
  }
  @Override
  public Map<String, Object> getCamelContextInformation(String camelContextName) throws Exception {
    if (jolokia == null) {
      throw new IllegalStateException("Need to connect to remote jolokia first");
    }

    Map<String, Object> answer = new LinkedHashMap<String, Object>();

    ObjectName found = lookupCamelContext(camelContextName);
    if (found != null) {

      String pattern =
          String.format(
              "%s:context=%s,type=services,name=DefaultTypeConverter",
              found.getDomain(), found.getKeyProperty("context"));
      ObjectName tc = ObjectName.getInstance(pattern);

      String pattern2 =
          String.format(
              "%s:context=%s,type=services,name=DefaultAsyncProcessorAwaitManager",
              found.getDomain(), found.getKeyProperty("context"));
      ObjectName am = ObjectName.getInstance(pattern2);

      List<J4pReadRequest> list = new ArrayList<J4pReadRequest>();
      list.add(new J4pReadRequest(found));
      list.add(new J4pReadRequest(tc));
      list.add(new J4pReadRequest(am));

      List<J4pReadResponse> rr = jolokia.execute(list);
      if (rr != null && rr.size() > 0) {
        // camel context attributes
        J4pReadResponse first = rr.get(0);
        for (String key : first.getAttributes()) {
          answer.put(asKey(key), first.getValue(key));
        }

        // type converter attributes
        if (rr.size() >= 2) {
          J4pReadResponse second = rr.get(1);
          for (String key : second.getAttributes()) {
            answer.put("typeConverter." + asKey(key), second.getValue(key));
          }
        }

        // async processor await manager attributes
        if (rr.size() >= 3) {
          J4pReadResponse second = rr.get(2);
          for (String key : second.getAttributes()) {
            answer.put("asyncProcessorAwaitManager." + asKey(key), second.getValue(key));
          }
        }
      }

      // would be great if there was an api in jolokia to read optional (eg ignore if an mbean does
      // not exists)
      answer.put("streamCachingEnabled", false);
      try {
        pattern =
            String.format(
                "%s:context=%s,type=services,name=DefaultStreamCachingStrategy",
                found.getDomain(), found.getKeyProperty("context"));
        ObjectName sc = ObjectName.getInstance(pattern);

        // there is only a mbean if stream caching is enabled
        J4pReadResponse rsc = jolokia.execute(new J4pReadRequest(sc));
        if (rsc != null) {
          for (String key : rsc.getAttributes()) {
            answer.put("streamCaching." + asKey(key), rsc.getValue(key));
          }
        }
        answer.put("streamCachingEnabled", true);
      } catch (J4pRemoteException e) {
        // ignore
        boolean ignore = InstanceNotFoundException.class.getName().equals(e.getErrorType());
        if (!ignore) {
          throw e;
        }
      }

      // store some data using special names as that is what the core-commands expects
      answer.put("name", answer.get("camelId"));
      answer.put("status", answer.get("state"));
      answer.put("version", answer.get("camelVersion"));
      answer.put("suspended", "Suspended".equals(answer.get("state")));
      TimeUnit unit = TimeUnit.valueOf((String) answer.get("timeUnit"));
      long timeout = (Long) answer.get("timeout");
      answer.put("shutdownTimeout", "" + unit.toSeconds(timeout));
      answer.put("applicationContextClassLoader", answer.get("applicationContextClassName"));
    }

    return answer;
  }
  @Override
  public List<Map<String, String>> getRoutes(String camelContextName, String filter)
      throws Exception {
    if (jolokia == null) {
      throw new IllegalStateException("Need to connect to remote jolokia first");
    }

    List<Map<String, String>> answer = new ArrayList<Map<String, String>>();

    ObjectName found = camelContextName != null ? lookupCamelContext(camelContextName) : null;
    if (found != null) {

      String pattern =
          String.format(
              "%s:context=%s,type=routes,*", found.getDomain(), found.getKeyProperty("context"));
      J4pSearchResponse sr = jolokia.execute(new J4pSearchRequest(pattern));

      List<J4pReadRequest> list = new ArrayList<J4pReadRequest>();
      for (ObjectName on : sr.getObjectNames()) {
        list.add(new J4pReadRequest(on, "CamelId", "RouteId", "State"));
      }

      List<J4pReadResponse> lrr = jolokia.execute(list);
      for (J4pReadResponse rr : lrr) {
        String routeId = rr.getValue("RouteId").toString();
        if (filter == null || routeId.matches(filter)) {
          Map<String, String> row = new LinkedHashMap<String, String>();
          row.put("camelContextName", rr.getValue("CamelId").toString());
          row.put("routeId", routeId);
          row.put("state", rr.getValue("State").toString());
          answer.add(row);
        }
      }

    } else {
      List<Map<String, String>> camelContexts = this.getCamelContexts();
      for (Map<String, String> row : camelContexts) {
        List<Map<String, String>> routes = getRoutes(row.get("name"), filter);
        answer.addAll(routes);
      }
    }

    // sort the list
    Collections.sort(
        answer,
        new Comparator<Map<String, String>>() {
          @Override
          public int compare(Map<String, String> o1, Map<String, String> o2) {
            // group by camel context first, then by route name
            String c1 = o1.get("camelContextName");
            String c2 = o2.get("camelContextName");

            int answer = c1.compareTo(c2);
            if (answer == 0) {
              // okay from same camel context, then sort by route id
              answer = o1.get("routeId").compareTo(o2.get("routeId"));
            }
            return answer;
          }
        });
    return answer;
  }