/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.ozone.lib.server;

import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.ozone.lib.util.ConfigurationUtils;

/**
 * Convenience class implementing the {@link Service} interface.
 */
@InterfaceAudience.Private
public abstract class BaseService implements Service {
  private String prefix;
  private Server server;
  private Configuration serviceConfig;

  /**
   * Service constructor.
   *
   * @param prefix service prefix.
   */
  public BaseService(String prefix) {
    this.prefix = prefix;
  }

  /**
   * Initializes the service.
   * <p>
   * It collects all service properties (properties having the
   * <code>#SERVER#.#SERVICE#.</code> prefix). The property names are then
   * trimmed from the <code>#SERVER#.#SERVICE#.</code> prefix.
   * <p>
   * After collecting  the service properties it delegates to the
   * {@link #init()} method.
   *
   * @param s the s initializing the service, give access to the
   * s context.
   *
   * @throws ServiceException thrown if the service could not be initialized.
   */
  @Override
  public final void init(Server s) throws ServiceException {
    this.server = s;
    String servicePrefix = getPrefixedName("");
    serviceConfig = new Configuration(false);
    for (Map.Entry<String, String> entry : ConfigurationUtils
        .resolve(s.getConfig())) {
      String key = entry.getKey();
      if (key.startsWith(servicePrefix)) {
        serviceConfig.set(key.substring(servicePrefix.length()),
            entry.getValue());
      }
    }
    init();
  }


  /**
   * Post initializes the service. This method is called by the
   * {@link Server} after all services of the server have been initialized.
   * <p>
   * This method does a NOP.
   *
   * @throws ServiceException thrown if the service could not be
   * post-initialized.
   */
  @Override
  public void postInit() throws ServiceException {
  }

  /**
   * Destroy the services.  This method is called once, when the
   * {@link Server} owning the service is being destroyed.
   * <p>
   * This method does a NOP.
   */
  @Override
  public void destroy() {
  }

  /**
   * Returns the service dependencies of this service. The service will be
   * instantiated only if all the service dependencies are already initialized.
   * <p>
   * This method returns an empty array (size 0)
   *
   * @return an empty array (size 0).
   */
  @Override
  public Class[] getServiceDependencies() {
    return new Class[0];
  }

  /**
   * Notification callback when the server changes its status.
   * <p>
   * This method returns an empty array (size 0)
   *
   * @param oldStatus old server status.
   * @param newStatus new server status.
   *
   * @throws ServiceException thrown if the service could not process the status
   * change.
   */
  @Override
  public void serverStatusChange(Server.Status oldStatus,
                                 Server.Status newStatus)
      throws ServiceException {
  }

  /**
   * Returns the service prefix.
   *
   * @return the service prefix.
   */
  protected String getPrefix() {
    return prefix;
  }

  /**
   * Returns the server owning the service.
   *
   * @return the server owning the service.
   */
  protected Server getServer() {
    return server;
  }

  /**
   * Returns the full prefixed name of a service property.
   *
   * @param name of the property.
   *
   * @return prefixed name of the property.
   */
  protected String getPrefixedName(String name) {
    return server.getPrefixedName(prefix + "." + name);
  }

  /**
   * Returns the service configuration properties. Property
   * names are trimmed off from its prefix.
   * <p>
   * The sevice configuration properties are all properties
   * with names starting with <code>#SERVER#.#SERVICE#.</code>
   * in the server configuration.
   *
   * @return the service configuration properties with names
   *         trimmed off from their <code>#SERVER#.#SERVICE#.</code>
   *         prefix.
   */
  protected Configuration getServiceConfig() {
    return serviceConfig;
  }

  /**
   * Initializes the server.
   * <p>
   * This method is called by {@link #init(Server)} after all service properties
   * (properties prefixed with
   *
   * @throws ServiceException thrown if the service could not be initialized.
   */
  protected abstract void init() throws ServiceException;

}
