/**
   * Build a hash key to make the single component source for all components cacheable but
   * updateable when the roles of the user change. This is not meant to be a secure hash, the roles
   * of a user are not secret.
   */
  public static int getRolesCachekey(HttpServletRequest request) {
    Set<String> roles = Authorizations.getRoles(request);

    if (roles.isEmpty()) {
      return 0;
    }

    List<String> sorted = new ArrayList<String>(roles);
    Collections.sort(sorted);

    int hash = 0;
    for (String role : sorted) {
      hash = hash ^ role.hashCode();
    }
    return hash;
  }
  private void makeAttributeJSONArray(final SimpleFeatureType layerSft) throws JSONException {
    List<ConfiguredAttribute> cas = applicationLayer.getAttributes();
    // Sort the attributes, by featuretype and the featuretyp
    Collections.sort(
        cas,
        new Comparator<ConfiguredAttribute>() {
          @Override
          public int compare(ConfiguredAttribute o1, ConfiguredAttribute o2) {
            if (o1.getFeatureType() == null) {
              return -1;
            }
            if (o2.getFeatureType() == null) {
              return 1;
            }
            if (o1.getFeatureType().getId().equals(layerSft.getId())) {
              return -1;
            }
            if (o2.getFeatureType().getId().equals(layerSft.getId())) {
              return 1;
            }
            return o1.getFeatureType().getId().compareTo(o2.getFeatureType().getId());
          }
        });
    for (ConfiguredAttribute ca : cas) {
      JSONObject j = ca.toJSONObject();

      // Copy alias over from feature type
      SimpleFeatureType sft = ca.getFeatureType();
      if (sft == null) {
        sft = layerSft;
      }
      AttributeDescriptor ad = sft.getAttribute(ca.getAttributeName());
      j.put("alias", ad.getAlias());
      j.put("featureTypeAttribute", ad.toJSONObject());

      attributesJSON.put(j);
    }
  }
  private void buildComponentSourceHTML() throws IOException {

    StringBuilder sb = new StringBuilder();

    // Sort components by classNames, so order is always the same for debugging
    List<ConfiguredComponent> comps =
        new ArrayList<ConfiguredComponent>(application.getComponents());
    Collections.sort(comps);

    if (isDebug()) {

      Set<String> classNamesDone = new HashSet<String>();
      for (ConfiguredComponent cc : comps) {

        if (!Authorizations.isConfiguredComponentAuthorized(cc, context.getRequest())) {
          continue;
        }

        if (!classNamesDone.contains(cc.getClassName())) {
          classNamesDone.add(cc.getClassName());

          if (cc.getViewerComponent() != null && cc.getViewerComponent().getSources() != null) {
            for (File f : cc.getViewerComponent().getSources()) {
              String url =
                  new ForwardResolution(ComponentActionBean.class, "source")
                      .addParameter("app", name)
                      .addParameter("version", version)
                      .addParameter("className", cc.getClassName())
                      .addParameter("file", f.getName())
                      .getUrl(context.getLocale());

              sb.append("        <script type=\"text/javascript\" src=\"");
              sb.append(HtmlUtil.encode(context.getServletContext().getContextPath() + url));
              sb.append("\"></script>\n");
            }
          }
        }
      }
    } else {
      // If not debugging, create a single script tag with all source
      // for all components for the application for a minimal number of HTTP requests

      // The ComponentActionBean supports conditional HTTP requests using
      // Last-Modified.
      // Create a hash value that will change when the classNames used
      // in the application change, so that a browser will not use a
      // previous version from cache with other contents.

      int hash = 0;
      Set<String> classNamesDone = new HashSet<String>();
      for (ConfiguredComponent cc : comps) {
        if (!Authorizations.isConfiguredComponentAuthorized(cc, context.getRequest())) {
          continue;
        }

        if (!classNamesDone.contains(cc.getClassName())) {
          hash = hash ^ cc.getClassName().hashCode();
        } else {
          classNamesDone.add(cc.getClassName());
        }
      }
      if (user != null) {
        // Update component sources when roles of user change
        hash = hash ^ getRolesCachekey(context.getRequest());

        // Update component sources when roles of configured components
        // may have changed
        hash = hash ^ (int) application.getAuthorizationsModified().getTime();
      }

      String url =
          new ForwardResolution(ComponentActionBean.class, "source")
              .addParameter("app", name)
              .addParameter("version", version)
              .addParameter("minified", true)
              .addParameter("hash", hash)
              .getUrl(context.getLocale());

      sb.append("        <script type=\"text/javascript\" src=\"");
      sb.append(HtmlUtil.encode(context.getServletContext().getContextPath() + url));
      sb.append("\"></script>\n");
    }

    componentSourceHTML = sb.toString();
  }