Package com.hbasebook.hush

Source Code of com.hbasebook.hush.UserManager

package com.hbasebook.hush;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Increment;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;

import com.hbasebook.hush.model.ShortUrl;
import com.hbasebook.hush.model.User;
import com.hbasebook.hush.servlet.RequestInfo;
import com.hbasebook.hush.table.HushTable;
import com.hbasebook.hush.table.UserTable;

public class UserManager {
  private final Log LOG = LogFactory.getLog(UserManager.class);

  private static final String ANONYMOUS_SUFFIX = ":anon";
  private static final Random RANDOM = new Random(System.currentTimeMillis());
  private static final String ADMIN_LOGIN_STRING = "admin";
  private static final byte[] ADMIN_LOGIN = Bytes.toBytes(ADMIN_LOGIN_STRING);
  private static final byte[] ADMIN_PASSWORD = ADMIN_LOGIN;

  private final ResourceManager rm;

  UserManager(ResourceManager rm) throws IOException {
    this.rm = rm;
  }

  /**
   * Initialize the instance. This is done lazily as it requires global
   * resources that need to be setup first.
   *
   * @throws IOException When preparing the stored data fails.
   */
  public void init() throws IOException {
    createRootUser();
    initializeAnonUserCounter();
    initializeAdminStats();
  }

  /**
   * Creates the root user "admin" with root access, ie. all roles.
   *
   * @throws IOException When creating the user fails.
   */
  private void createRootUser() throws IOException {
    HTable table = rm.getTable(UserTable.NAME);
    try {
      Put put = new Put(ADMIN_LOGIN);
      put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS, ADMIN_PASSWORD);
      put.add(UserTable.DATA_FAMILY, UserTable.ROLES, UserTable.ADMIN_ROLES);
      boolean hasPut = table.checkAndPut(ADMIN_LOGIN, UserTable.DATA_FAMILY,
        UserTable.ROLES, null, put);
      if (hasPut) {
        LOG.info("Admin user initialized.");
      }
    } catch (Exception e) {
      LOG.error("Unable to initialize admin user.", e);
      throw new IOException(e);
    } finally {
      rm.putTable(table);
    }
  }

  /**
   * Initializes the anonymous user ID counter.
   *
   * @throws IOException When initializing the counter fails.
   */
  private void initializeAnonUserCounter() throws IOException {
    HTable table = rm.getTable(HushTable.NAME);
    try {
      Put put = new Put(HushTable.GLOBAL_ROW_KEY);
      put.add(HushTable.COUNTERS_FAMILY, HushTable.ANONYMOUS_USER_ID,
        Bytes.toBytes(HushUtil.hushDecode("0")));
      boolean hasPut = table.checkAndPut(HushTable.GLOBAL_ROW_KEY,
        HushTable.COUNTERS_FAMILY, HushTable.SHORT_ID, null, put);
      if (hasPut) {
        LOG.info("Anonymous User Id counter initialized.");
      }
      table.flushCommits();
    } catch (Exception e) {
      LOG.error("Unable to initialize counters.", e);
      throw new IOException(e);
    } finally {
      rm.putTable(table);
    }
  }

  /**
   * Adds usage statistics for the admin user. This is mainly for testing.
   *
   * @throws IOException When adding the statistics fails.
   */
  private void initializeAdminStats() throws IOException {
    Map<RequestInfo.InfoName, String> props =
      new HashMap<RequestInfo.InfoName, String>();
    props.put(RequestInfo.InfoName.RemoteAddr, getRandomIp());
    RequestInfo info = new RequestInfo(props);
    ShortUrl shortUrl = rm.getUrlManager().shorten(
      new URL("http://hbasebook.com"), "admin", info);
    Calendar startDate = Calendar.getInstance();
    startDate.set(2011, 1, 1);
    Calendar endDate = Calendar.getInstance();
    endDate.setTime(new Date());
    while (startDate.before(endDate)) {
      props.put(RequestInfo.InfoName.RemoteAddr, getRandomIp());
      int count = RANDOM.nextInt(200);
      rm.getCounters().incrementUsage(shortUrl.getId(), info,
        count, startDate.getTime());
      if (shortUrl.getRefShortId() != null) {
        rm.getCounters().incrementUsage(shortUrl.getRefShortId(), info,
          count, startDate.getTime());
      }
      startDate.add(Calendar.DATE, 1);
    }
    LOG.info("Admin statistics initialized.");
  }

  /**
   * Defines some well-known IP ranges, up to the first three octets.
   */
  private final String[] IP_BLOCK_BY_COUNTRY = { "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "20.0.0", // usa
      "2.24.0", // uk
      "2.24.0", // uk
      "2.24.0", // uk
      "1.0.1", // china
      "91.142.133", // russian fed
      "91.142.133", // russian fed
      "91.142.133", // russian fed
      "91.142.133", // russian fed
      "192.80.208", // whatup bra
      "192.80.208", // whatup bra
      "91.198.2", // germany
      "198.49.164", // argentina
      "180.149.200", // jp
      "91.142.143", // it
      "131.150.6", // ca
  };

  /**
   * Returns a "random" IP address based on weighted blocks of IP ranges. The
   * idea is to not get roughly the same amount of hits per country but get
   * some more popular to those that have barely any hits. Clear, Eh?
   *
   * @return
   */
  private String getRandomIp() {
    return String.format("%s.%d",
      IP_BLOCK_BY_COUNTRY[RANDOM.nextInt(IP_BLOCK_BY_COUNTRY.length)],
      RANDOM.nextInt(255));
  }

  /**
   * Creates a user in the user table.
   *
   * @param username The username to use.
   * @param firstName The first name of the user.
   * @param lastName The last name of the user.
   * @param email The email address of the user.
   * @param password The password of the user.
   * @param roles The user roles assigned to the new user.
   * @throws IOException When adding the user fails.
   */
  // cc HushHTablePoolUsage Using the pool in application code
  // vv HushHTablePoolUsage
  public void createUser(String username, String firstName, String lastName,
    String email, String password, String roles) throws IOException {
    /*[*/HTable table = rm.getTable(UserTable.NAME);/*]*/
    Put put = new Put(Bytes.toBytes(username));
    put.add(UserTable.DATA_FAMILY, UserTable.FIRSTNAME,
      Bytes.toBytes(firstName));
    put.add(UserTable.DATA_FAMILY, UserTable.LASTNAME, Bytes.toBytes(lastName));
    put.add(UserTable.DATA_FAMILY, UserTable.EMAIL, Bytes.toBytes(email));
    put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS,
      Bytes.toBytes(password));
    put.add(UserTable.DATA_FAMILY, UserTable.ROLES, Bytes.toBytes(roles));
    table.put(put);
    table.flushCommits();
    /*[*/rm.putTable(table);/*]*/
  }
  // ^^ HushHTablePoolUsage

  /**
   * Updates a user record.
   *
   * @param username The username to modify.
   * @param firstName The new first name.
   * @param lastName The new last name.
   * @param email The new email address.
   * @throws IOException When modifying the user fails.
   */
  public void updateUser(String username, String firstName, String lastName,
    String email) throws IOException {
    HTable table = rm.getTable(UserTable.NAME);
    Put put = new Put(Bytes.toBytes(username));
    put.add(UserTable.DATA_FAMILY, UserTable.FIRSTNAME,
      Bytes.toBytes(firstName));
    put.add(UserTable.DATA_FAMILY, UserTable.LASTNAME, Bytes.toBytes(lastName));
    put.add(UserTable.DATA_FAMILY, UserTable.EMAIL, Bytes.toBytes(email));
    table.put(put);
    table.flushCommits();
    rm.putTable(table);
  }

  public boolean changePassword(String username, String oldPassword,
    String newPassword) throws IOException {
    HTable table = rm.getTable(UserTable.NAME);
    Put put = new Put(Bytes.toBytes(username));
    put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS,
      Bytes.toBytes(newPassword));
    boolean check = table.checkAndPut(Bytes.toBytes(username),
      UserTable.DATA_FAMILY, UserTable.CREDENTIALS, Bytes.toBytes(oldPassword),
      put);
    table.flushCommits();
    rm.putTable(table);
    return check;
  }

  public void adminChangePassword(String username, String newPassword)
    throws IOException {
    HTable table = rm.getTable(UserTable.NAME);
    Put put = new Put(Bytes.toBytes(username));
    put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS,
      Bytes.toBytes(newPassword));
    table.put(put);
    table.flushCommits();
    rm.putTable(table);
  }

  public User getUser(String username) throws IOException {
    User user = null;
    HTable table = null;
    try {
      table = rm.getTable(UserTable.NAME);
      Get get = new Get(Bytes.toBytes(username));

      Result result = table.get(get);
      if (result.isEmpty()) {
        return null;
      }

      String firstName = Bytes.toString(
        result.getValue(UserTable.DATA_FAMILY, UserTable.FIRSTNAME));
      String lastName = Bytes.toString(
        result.getValue(UserTable.DATA_FAMILY, UserTable.LASTNAME));
      String email = Bytes.toString(result.getValue(UserTable.DATA_FAMILY,
        UserTable.EMAIL));
      String credentials = Bytes.toString(result.getValue(UserTable.DATA_FAMILY,
        UserTable.CREDENTIALS));
      String roles = Bytes.toString(result.getValue(UserTable.DATA_FAMILY,
        UserTable.ROLES));
      user = new User(username, firstName, lastName, email, credentials, roles);
    } catch (Exception e) {
      LOG.error(String.format("Unable to get user '%s'", username), e);
    } finally {
      rm.putTable(table);
    }

    return user;
  }

  public List<User> getUsers() throws IOException {
    List<User> users = new ArrayList<User>();
    HTable table = rm.getTable(UserTable.NAME);

    Scan scan = new Scan();
    ResultScanner scanner = table.getScanner(scan);

    Iterator<Result> results = scanner.iterator();
    int errors = 0;
    while (results.hasNext()) {
      Result result = results.next();
      if (!result.isEmpty()) {
        try {
          String username = Bytes.toString(result.getRow());
          String firstName = Bytes.toString(
            result.getValue(UserTable.DATA_FAMILY, UserTable.FIRSTNAME));
          String lastName = Bytes.toString(
            result.getValue(UserTable.DATA_FAMILY, UserTable.LASTNAME));
          String email = Bytes.toString(
            result.getValue(UserTable.DATA_FAMILY, UserTable.EMAIL));
          String credentials = Bytes.toString(
            result.getValue(UserTable.DATA_FAMILY, UserTable.CREDENTIALS));
          String roles = Bytes.toString(
            result.getValue(UserTable.DATA_FAMILY, UserTable.ROLES));
          User user = new User(username, firstName, lastName, email,
            credentials, roles);
          users.add(user);
        } catch (Exception e) {
          errors++;
        }
      }
    }
    if (errors > 0) {
      LOG.error(String.format("Encountered %d errors in getUsers", errors));
    }
    rm.putTable(table);
    return users;
  }

  /**
   * Convenience method to retrieve a new anonymous User Id. Each call
   * increments the counter by one.
   *
   * @return The newly created user Id.
   * @throws IOException When communicating with HBase fails.
   */
  public String generateAnonymousUserId() throws IOException {
    return generateAnonymousUserId(1L) + ANONYMOUS_SUFFIX;
  }

  /**
   * Creates a new short Id.
   *
   * @param incrBy The increment value.
   * @return The newly created short id, encoded as String.
   * @throws IOException When the counter fails to increment.
   */
  private String generateAnonymousUserId(long incrBy) throws IOException {
    ResourceManager manager = ResourceManager.getInstance();
    HTable table = manager.getTable(HushTable.NAME);
    try {
      Increment increment = new Increment(HushTable.GLOBAL_ROW_KEY);
      increment.addColumn(HushTable.COUNTERS_FAMILY,
        HushTable.ANONYMOUS_USER_ID, incrBy);
      Result result = table.increment(increment);
      long id = Bytes.toLong(result.getValue(HushTable.COUNTERS_FAMILY,
        HushTable.ANONYMOUS_USER_ID));
      return HushUtil.hushEncode(id);
    } catch (Exception e) {
      LOG.error("Unable to create a new anonymous user Id.", e);
      throw new IOException(e);
    } finally {
      try {
        manager.putTable(table);
      } catch (Exception e) {
        // ignore
      }
    }
  }

  public static boolean isAnonymous(String username) {
    return username == null || username.endsWith(ANONYMOUS_SUFFIX);
  }
}
TOP

Related Classes of com.hbasebook.hush.UserManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.