@RequestMapping(value = "/validateUrl", method = RequestMethod.POST)
  public @ResponseBody JsonResponse validateUrl(
      @RequestParam(value = "url", required = true) String url) {

    String error = "";
    if (StringUtils.isEmpty(url)) {
      error = messageResource.getMessage("org.hibernate.validator.constraints.NotEmpty.message");
    } else if (!urlValidator.isValid(url)) {
      error = messageResource.getMessage("org.hibernate.validator.constraints.URL.message");
    }

    return getResponse(error);
  }
  /** Handle the configure context's update button */
  public void doConfigure_update(RunData data, Context context) {
    // TODO: if we do limit the initState() calls, we need to make sure we get a new one after this
    // call -ggolden

    String peid = ((JetspeedRunData) data).getJs_peid();
    SessionState state = ((JetspeedRunData) data).getPortletSessionState(peid);

    Placement placement = ToolManager.getCurrentPlacement();

    // get the site toolConfiguration, if this is part of a site.
    ToolConfiguration toolConfig = SiteService.findTool(placement.getId());

    // height
    String height = data.getParameters().getString(HEIGHT);
    if (height.equals(rb.getString("gen.heisomelse"))) {
      String customHeight = data.getParameters().getString(CUSTOM_HEIGHT);
      if ((customHeight != null) && (!customHeight.equals(""))) {
        if (!checkDigits(customHeight)) {
          addAlert(state, rb.getString("java.alert.pleentval"));
          return;
        }
        state.setAttribute(HEIGHT, customHeight);
        height = customHeight + "px";
        state.setAttribute(HEIGHT, height);
        placement.getPlacementConfig().setProperty(HEIGHT, height);
      } else {
        addAlert(state, rb.getString("java.alert.pleentval"));
        return;
      }
    } else if (SPECIAL_ANNOTATEDURL.equals(state.getAttribute(SPECIAL))) {
      // update the site info
      try {
        String desp = data.getParameters().getString("description");
        state.setAttribute(ANNOTATED_TEXT, desp);
        placement.getPlacementConfig().setProperty(ANNOTATED_TEXT, desp);

      } catch (Throwable e) {
      }
    } else {
      state.setAttribute(HEIGHT, height);
      placement.getPlacementConfig().setProperty(HEIGHT, height);
    }

    // title
    String title = data.getParameters().getString(TITLE);
    if (StringUtils.isBlank(title)) {
      addAlert(state, rb.getString("gen.tootit.empty"));
      return;
      // SAK-19515 check for LENGTH of tool title
    } else if (title.length() > MAX_TITLE_LENGTH) {
      addAlert(state, rb.getString("gen.tootit.toolong"));
      return;
    }
    placement.setTitle(title);

    // site info url
    String infoUrl = StringUtils.trimToNull(data.getParameters().getString("infourl"));
    if (infoUrl != null && infoUrl.length() > MAX_SITE_INFO_URL_LENGTH) {
      addAlert(state, rb.getString("gen.info.url.toolong"));
      return;
    }

    try {
      Site site = SiteService.getSite(toolConfig.getSiteId());
      SitePage page = site.getPage(toolConfig.getPageId());
      page.setTitleCustom(true);

      // for web content tool, if it is a site page tool, and the only tool on the page, update the
      // page title / popup.
      if ((state.getAttribute(SPECIAL) == null) && (toolConfig != null)) {
        // if this is the only tool on that page, update the page's title also
        if ((page.getTools() != null) && (page.getTools().size() == 1)) {
          String newPageTitle = data.getParameters().getString(FORM_PAGE_TITLE);

          if (StringUtils.isBlank(newPageTitle)) {
            addAlert(state, rb.getString("gen.pagtit.empty"));
            return;
          } else if (newPageTitle.length() > MAX_TITLE_LENGTH) {
            addAlert(state, rb.getString("gen.pagtit.toolong"));
            return;
          }
          page.setTitle(newPageTitle);
          state.setAttribute(STATE_PAGE_TITLE, newPageTitle);

          // popup
          boolean popup = data.getParameters().getBoolean("popup");
          page.setPopup(popup);
        }
      }

      SiteService.save(site);
    } catch (Exception ignore) {
      M_log.warn("doConfigure_update: " + ignore);
    }

    // read source if we are not special
    if (state.getAttribute(SPECIAL) == null) {
      String source = StringUtils.trimToEmpty(data.getParameters().getString(SOURCE));

      // User entered nothing in the source box; give the user an alert
      if (StringUtils.isBlank(source)) {
        addAlert(state, rb.getString("gen.url.empty"));
        return;
      }

      if ((!source.startsWith("/")) && (source.indexOf("://") == -1)) {
        source = "http://" + source;
      }

      // Validate the url
      UrlValidator urlValidator = new UrlValidator();
      if (!urlValidator.isValid(source)) {
        addAlert(state, rb.getString("gen.url.invalid"));
        return;
      }

      // update state
      placement.getPlacementConfig().setProperty(SOURCE, source);
    } else if (SPECIAL_WORKSITE.equals(state.getAttribute(SPECIAL))) {
      if ((infoUrl != null)
          && (infoUrl.length() > 0)
          && (!infoUrl.startsWith("/"))
          && (infoUrl.indexOf("://") == -1)) {
        infoUrl = "http://" + infoUrl;
      }
      String description = StringUtils.trimToNull(data.getParameters().getString("description"));
      description = FormattedText.processEscapedHtml(description);

      // update the site info
      try {
        SiteService.saveSiteInfo(
            ToolManager.getCurrentPlacement().getContext(), description, infoUrl);
      } catch (Throwable e) {
        M_log.warn("doConfigure_update: " + e);
      }
    }

    // save
    // TODO: we might have just saved the entire site, so this would not be needed -ggolden
    placement.save();

    // we are done with customization... back to the main mode
    state.removeAttribute(STATE_MODE);

    // refresh the whole page, since popup and title may have changed
    scheduleTopRefresh();
  }