protected void createAndSaveClick(String hash, HttpServletRequest request) {
    /* Gets the IP from the request, and looks in the db for its country */
    String dirIp = extractIP(request);
    BigInteger valueIp = getIpValue(dirIp);
    Ip subnet = ipRepository.findSubnet(valueIp);
    String country = (subnet != null) ? (subnet.getCountry()) : ("");

    request.getHeader(USER_AGENT);
    JSONObject jn = getFreegeoip(request);
    String city = jn.getString("city");
    Float latitude = new Float(jn.getDouble("latitude"));
    Float longitude = new Float(jn.getDouble("longitude"));
    Click cl =
        new Click(
            null,
            hash,
            new Date(System.currentTimeMillis()),
            request.getHeader(REFERER),
            request.getHeader(USER_AGENT),
            request.getHeader(USER_AGENT),
            dirIp,
            country,
            city,
            longitude,
            latitude);
    cl = clickRepository.save(cl);
    logger.info(
        cl != null
            ? "[" + hash + "] saved with id [" + cl.getId() + "]"
            : "[" + hash + "] was not saved");
  }
  /**
   * Execute the rule and return true or false.
   *
   * @param rules
   * @param l
   * @return
   */
  public Boolean executeS(String rule, ShortURL l) {
    try {
      if (rule.contains("<")) {
        String[] partes = rule.split("<");
        if (partes[0].equals("created")) {
          if (l.getCreated().before(new SimpleDateFormat("yyyy-MM-dd").parse(partes[1]))) {
            return true;
          } else {
            return false;
          }
        } else if (partes[0].equals("expire")) {
          if (l.getExpire().before(new SimpleDateFormat("yyyy-MM-dd").parse(partes[1]))) {
            return true;
          } else {
            return false;
          }
        } else if (partes[0].equals("token")) {
          return null;
        } else if (partes[0].equals("country")) {

          return null;
        } else if (partes[0].equals("clicks")) {
          if (clickRepository.clicksByHash(l.getHash(), null, null, null, null, null, null)
              < Long.valueOf(partes[1])) {
            return true;
          } else {
            return false;
          }
        }
        return null;
      } else if (rule.contains(">")) {
        String[] partes = rule.split(">");
        if (partes[0].equals("created")) {
          if (l.getCreated().after(new SimpleDateFormat("yyyy-MM-dd").parse(partes[1]))) {
            return true;
          } else {
            return false;
          }
        } else if (partes[0].equals("expire")) {
          if (l.getExpire().after(new SimpleDateFormat("yyyy-MM-dd").parse(partes[1]))) {
            return true;
          } else {
            return false;
          }
        } else if (partes[0].equals("token")) {
          return null;
        } else if (partes[0].equals("country")) {

          return null;
        } else if (partes[0].equals("clicks")) {
          if (clickRepository.clicksByHash(l.getHash(), null, null, null, null, null, null)
              > Long.valueOf(partes[1])) {
            return true;
          } else {
            return false;
          }
        }
        return null;
      } else if (rule.contains("==")) {
        String[] partes = rule.split("==");
        if (partes[0].equals("created")) {
          if (l.getCreated().compareTo((new SimpleDateFormat("yyyy-MM-dd").parse(partes[1])))
              == 0) {
            return true;
          } else {
            return false;
          }
        } else if (partes[0].equals("expire")) {
          if (l.getExpire().compareTo((new SimpleDateFormat("yyyy-MM-dd").parse(partes[1]))) == 0) {
            return true;
          } else {
            return false;
          }
        } else if (partes[0].equals("token")) {
          if (partes[1].equals("true")) {
            return l.getToken() != null;
          } else if (partes[1].equals("false")) {
            return l.getToken() == null;
          } else {
            return false;
          }
        } else if (partes[0].equals("country")) {

          return l.getCountry().equals(partes[1]);
        } else if (partes[0].equals("clicks")) {
          if (clickRepository.clicksByHash(l.getHash(), null, null, null, null, null, null)
              == Long.valueOf(partes[1])) {
            return true;
          } else {
            return false;
          }
        }
        return null;

      } else {
        return null;
      }
    } catch (Exception e) {
      return null;
    }
  }
  @RequestMapping(value = "/{id:(?!link|index|profile).*}", method = RequestMethod.GET)
  public Object redirectTo(
      @PathVariable String id,
      @RequestParam(value = "token", required = false) String token,
      HttpServletResponse response,
      HttpServletRequest request,
      Model model) {

    logger.info("Requested redirection with hash " + id);
    ShortURL l = shortURLRepository.findByHash(id);
    logger.info("su: " + l);
    logger.info(l == null ? "null" : "not null");
    if (l != null) {
      /*
       * Check Token
       */
      if (l.getToken() != null && (token == null || !l.getToken().equals(token))) {
        /*
         * Wrong Token
         */
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        throw new CustomException("400", "It is need a token");
      } else {

        Date d = new Date(System.currentTimeMillis());
        if (l.getExpire() != null && d.after(l.getExpire())) {
          /*
           * Date has expired
           */
          response.setStatus(HttpStatus.BAD_REQUEST.value());
          throw new CustomException("400", "Link has expired");

        } else {
          ArrayList<String> rules = l.getRules();
          if (rules != null && !rules.isEmpty()) {
            /*
             * Execute javascript
             */
            for (int i = 0; i < rules.size(); i++) {
              Boolean resul = executeS(rules.get(i), l);
              if (resul != null) {
                if (resul == true) {
                  response.setStatus(HttpStatus.BAD_REQUEST.value());
                  throw new CustomException("400", "Link has expired");
                }
              } else {
                response.setStatus(HttpStatus.BAD_REQUEST.value());
                throw new CustomException("400", "Bad rule");
              }
            }
          }

          List<String> authorizedMails = l.getAllowedUsers();
          if (authorizedMails != null && !authorizedMails.isEmpty()) {

            if (!authentication(authorizedMails)) {
              request.getSession().setAttribute("redirect", id);

              // model.addAttribute("hash", id);
              return "login_special";
            }
          }
          createAndSaveClick(id, request);
          long click =
              clickRepository.clicksByHash(l.getHash(), null, null, null, null, null, null);
          /* Data from countries */
          DBObject groupObject = clickRepository.getClicksByCountry(id, null, null).getRawResults();
          String list = groupObject.get("retval").toString();
          String countryData = StatsController.processCountryJSON(list);
          /* Data from cities */
          DBObject groupObjectCity =
              clickRepository
                  .getClicksByCity(id, null, null, null, null, null, null)
                  .getRawResults();
          String listCities = groupObjectCity.get("retval").toString();
          String cityData = StatsController.processCityJSON(listCities);
          WebSocketsData wb = new WebSocketsData(false, click, countryData, cityData);
          this.template.convertAndSend("/topic/" + id, wb);
          return createSuccessfulRedirectToResponse(l);
        }
      }
    } else {
      response.setStatus(HttpStatus.BAD_REQUEST.value());
      throw new CustomException("400", "BAD_REQUEST\nURL SHORTENED DOESN'T EXISTS");
    }
  }