LimitsHandler.java

/*
 * This file is part of Waarp Project (named also Waarp or GG).
 *
 *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
 *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
 * individual contributors.
 *
 *  All Waarp Project is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 * Waarp . If not, see <http://www.gnu.org/licenses/>.
 */

package org.waarp.openr66.protocol.http.restv2.dbhandlers;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.cdap.http.HttpResponder;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import org.waarp.openr66.dao.DAOFactory;
import org.waarp.openr66.dao.LimitDAO;
import org.waarp.openr66.dao.exception.DAOConnectionException;
import org.waarp.openr66.dao.exception.DAONoDataException;
import org.waarp.openr66.pojo.Limit;
import org.waarp.openr66.protocol.http.restv2.converters.LimitsConverter;
import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
import org.waarp.openr66.protocol.http.restv2.utils.JsonUtils;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import java.util.ArrayList;
import java.util.List;

import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static javax.ws.rs.core.HttpHeaders.*;
import static javax.ws.rs.core.MediaType.*;
import static org.waarp.common.role.RoleDefault.ROLE.*;
import static org.waarp.openr66.protocol.configuration.Configuration.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;

/**
 * This is the {@link AbstractRestDbHandler} handling all requests made on the
 * bandwidth limits REST entry
 * point.
 */
@Path(LIMITS_HANDLER_URI)
public class LimitsHandler extends AbstractRestDbHandler {

  /**
   * The content of the 'Allow' header sent when an 'OPTIONS' request is made
   * on the handler.
   */
  private static final HttpHeaders OPTIONS_HEADERS;

  static {
    OPTIONS_HEADERS = new DefaultHttpHeaders();
    final List<HttpMethod> allow = new ArrayList<HttpMethod>();
    allow.add(HttpMethod.GET);
    allow.add(HttpMethod.POST);
    allow.add(HttpMethod.PUT);
    allow.add(HttpMethod.DELETE);
    allow.add(HttpMethod.OPTIONS);
    OPTIONS_HEADERS.add(ALLOW, allow);
  }

  /**
   * Initializes the handler with the given CRUD mask.
   *
   * @param crud the CRUD mask for this handler
   */
  public LimitsHandler(final byte crud) {
    super(crud);
  }

  /**
   * Method called to obtain a description of the host's current bandwidth
   * limits.
   *
   * @param request the HttpRequest made on the resource
   * @param responder the HttpResponder which sends the reply to the
   *     request
   */
  @GET
  @Consumes(WILDCARD)
  @RequiredRole(LIMIT)
  public final void getLimits(final HttpRequest request,
                              final HttpResponder responder) {
    LimitDAO limitDAO = null;
    try {
      limitDAO = DAO_FACTORY.getLimitDAO(true);
      final String host = serverName();
      if (limitDAO.exist(host)) {
        final Limit limits = limitDAO.select(host);
        final ObjectNode responseObject = LimitsConverter.limitToNode(limits);
        final String responseText = JsonUtils.nodeToString(responseObject);
        responder.sendJson(OK, responseText);
      } else {
        responder.sendStatus(NOT_FOUND);
      }
    } catch (final DAOConnectionException e) {
      throw new InternalServerErrorException(e);
    } catch (final DAONoDataException e) {
      responder.sendStatus(NOT_FOUND);
    } finally {
      DAOFactory.closeDAO(limitDAO);
    }
  }

  /**
   * Method called to initiate the entry for this host in the bandwidth limits
   * database. If the host already has
   * limits set in its configuration, they will be replaced by these new
   * ones.
   *
   * @param request the HttpRequest made on the resource
   * @param responder the HttpResponder which sends the reply to the
   *     request
   */
  @POST
  @Consumes(APPLICATION_JSON)
  @RequiredRole(READONLY)
  public final void initializeLimits(final HttpRequest request,
                                     final HttpResponder responder) {
    LimitDAO limitDAO = null;
    try {
      limitDAO = DAO_FACTORY.getLimitDAO(false);

      if (!limitDAO.exist(serverName())) {
        final ObjectNode requestObject = JsonUtils.deserializeRequest(request);
        checkSanity(requestObject);
        final Limit limits = LimitsConverter.nodeToNewLimit(requestObject);
        limitDAO.insert(limits);

        configuration.changeNetworkLimit(limits.getReadGlobalLimit(),
                                         limits.getWriteGlobalLimit(),
                                         limits.getReadSessionLimit(),
                                         limits.getWriteSessionLimit(),
                                         limits.getDelayLimit());

        final ObjectNode responseObject = LimitsConverter.limitToNode(limits);
        final String responseText = JsonUtils.nodeToString(responseObject);
        responder.sendJson(CREATED, responseText);
      } else {
        throw new RestErrorException(ALREADY_EXISTING(serverName()));
      }
    } catch (final DAOConnectionException e) {
      throw new InternalServerErrorException(e);
    } finally {
      DAOFactory.closeDAO(limitDAO);
    }
  }

  /**
   * Method called to update this host's bandwidth limits in the database and
   * configuration.
   *
   * @param request the HttpRequest made on the resource
   * @param responder the HttpResponder which sends the reply to the
   *     request
   */
  @PUT
  @Consumes(APPLICATION_JSON)
  @RequiredRole(LIMIT)
  public final void updateLimits(final HttpRequest request,
                                 final HttpResponder responder) {
    LimitDAO limitDAO = null;
    try {
      limitDAO = DAO_FACTORY.getLimitDAO(false);

      if (!limitDAO.exist(serverName())) {
        responder.sendStatus(NOT_FOUND);
        return;
      }

      final ObjectNode requestObject = JsonUtils.deserializeRequest(request);
      checkSanity(requestObject);
      final Limit oldLimits = limitDAO.select(serverName());
      final Limit newLimits =
          LimitsConverter.nodeToUpdatedLimit(requestObject, oldLimits);

      limitDAO.update(newLimits);

      configuration.changeNetworkLimit(newLimits.getReadGlobalLimit(),
                                       newLimits.getWriteGlobalLimit(),
                                       newLimits.getReadSessionLimit(),
                                       newLimits.getWriteSessionLimit(),
                                       newLimits.getDelayLimit());

      final ObjectNode responseObject = LimitsConverter.limitToNode(newLimits);
      final String responseText = JsonUtils.nodeToString(responseObject);
      responder.sendJson(CREATED, responseText);

    } catch (final DAOConnectionException e) {
      throw new InternalServerErrorException(e);
    } catch (final DAONoDataException e) {
      responder.sendStatus(NOT_FOUND);
    } finally {
      DAOFactory.closeDAO(limitDAO);
    }
  }

  /**
   * Method called to remove any bandwidth limits in place on this host. Also
   * removes any limits set in the
   * configuration.
   *
   * @param request the HttpRequest made on the resource
   * @param responder the HttpResponder which sends the reply to the
   *     request
   */
  @DELETE
  @Consumes(WILDCARD)
  @RequiredRole(LIMIT)
  public final void deleteLimits(final HttpRequest request,
                                 final HttpResponder responder) {
    LimitDAO limitDAO = null;
    try {
      limitDAO = DAO_FACTORY.getLimitDAO(false);

      if (limitDAO.exist(serverName())) {
        limitDAO.delete(limitDAO.select(serverName()));
        configuration.changeNetworkLimit(0, 0, 0, 0, 0);
        responder.sendStatus(NO_CONTENT);
      } else {
        responder.sendStatus(NOT_FOUND);
      }
    } catch (final DAOConnectionException e) {
      throw new InternalServerErrorException(e);
    } catch (final DAONoDataException e) {
      responder.sendStatus(NOT_FOUND);
    } finally {
      DAOFactory.closeDAO(limitDAO);
    }
  }

  /**
   * Method called to get a list of all allowed HTTP methods on this entry
   * point. The HTTP methods are sent as
   * an array in the reply's headers.
   *
   * @param request the HttpRequest made on the resource
   * @param responder the HttpResponder which sends the reply to the
   *     request
   */
  @OPTIONS
  @Consumes(WILDCARD)
  @RequiredRole(NOACCESS)
  public final void options(final HttpRequest request,
                            final HttpResponder responder) {
    responder.sendStatus(OK, OPTIONS_HEADERS);
  }
}