Java Code Examples for org.apache.hadoop.classification.InterfaceAudience#Public

The following examples show how to use org.apache.hadoop.classification.InterfaceAudience#Public . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: SecurityUtil.java    From hadoop with Apache License 2.0 6 votes vote down vote up
/**
 * Login as a principal specified in config. Substitute $host in user's Kerberos principal 
 * name with hostname. If non-secure mode - return. If no keytab available -
 * bail out with an exception
 * 
 * @param conf
 *          conf to use
 * @param keytabFileKey
 *          the key to look for keytab file in conf
 * @param userNameKey
 *          the key to look for user's Kerberos principal name in conf
 * @param hostname
 *          hostname to use for substitution
 * @throws IOException if the config doesn't specify a keytab
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static void login(final Configuration conf,
    final String keytabFileKey, final String userNameKey, String hostname)
    throws IOException {
  
  if(! UserGroupInformation.isSecurityEnabled()) 
    return;
  
  String keytabFilename = conf.get(keytabFileKey);
  if (keytabFilename == null || keytabFilename.length() == 0) {
    throw new IOException("Running in secure mode, but config doesn't have a keytab");
  }

  String principalConfig = conf.get(userNameKey, System
      .getProperty("user.name"));
  String principalName = SecurityUtil.getServerPrincipal(principalConfig,
      hostname);
  UserGroupInformation.loginUserFromKeytab(principalName, keytabFilename);
}
 
Example 2
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 6 votes vote down vote up
/**
 * Run the given action as the user, potentially throwing an exception.
 * @param <T> the return type of the run method
 * @param action the method to execute
 * @return the value from the run method
 * @throws IOException if the action throws an IOException
 * @throws Error if the action throws an Error
 * @throws RuntimeException if the action throws a RuntimeException
 * @throws InterruptedException if the action throws an InterruptedException
 * @throws UndeclaredThrowableException if the action throws something else
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public <T> T doAs(PrivilegedExceptionAction<T> action
                  ) throws IOException, InterruptedException {
  try {
    logPrivilegedAction(subject, action);
    return Subject.doAs(subject, action);
  } catch (PrivilegedActionException pae) {
    Throwable cause = pae.getCause();
    if (LOG.isDebugEnabled()) {
      LOG.debug("PrivilegedActionException as:" + this + " cause:" + cause);
    }
    if (cause instanceof IOException) {
      throw (IOException) cause;
    } else if (cause instanceof Error) {
      throw (Error) cause;
    } else if (cause instanceof RuntimeException) {
      throw (RuntimeException) cause;
    } else if (cause instanceof InterruptedException) {
      throw (InterruptedException) cause;
    } else {
      throw new UndeclaredThrowableException(cause);
    }
  }
}
 
Example 3
Source File: SecurityUtil.java    From big-c with Apache License 2.0 6 votes vote down vote up
/**
 * Convert Kerberos principal name pattern to valid Kerberos principal names.
 * This method is similar to {@link #getServerPrincipal(String, String)},
 * except 1) the reverse DNS lookup from addr to hostname is done only when
 * necessary, 2) param addr can't be null (no default behavior of using local
 * hostname when addr is null).
 * 
 * @param principalConfig
 *          Kerberos principal name pattern to convert
 * @param addr
 *          InetAddress of the host used for substitution
 * @return converted Kerberos principal name
 * @throws IOException if the client address cannot be determined
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static String getServerPrincipal(String principalConfig,
    InetAddress addr) throws IOException {
  String[] components = getComponents(principalConfig);
  if (components == null || components.length != 3
      || !components[1].equals(HOSTNAME_PATTERN)) {
    return principalConfig;
  } else {
    if (addr == null) {
      throw new IOException("Can't replace " + HOSTNAME_PATTERN
          + " pattern since client address is null");
    }
    return replacePattern(components, addr.getCanonicalHostName());
  }
}
 
Example 4
Source File: XLearningConstants.java    From XLearning with Apache License 2.0 5 votes vote down vote up
@InterfaceAudience.Public
@InterfaceStability.Unstable
public String $$() {
  return ApplicationConstants.PARAMETER_EXPANSION_LEFT +
      variable +
      ApplicationConstants.PARAMETER_EXPANSION_RIGHT;
}
 
Example 5
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 5 votes vote down vote up
/**
 * Return the current user, including any doAs in the current stack.
 * @return the current user
 * @throws IOException if login fails
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized
static UserGroupInformation getCurrentUser() throws IOException {
  AccessControlContext context = AccessController.getContext();
  Subject subject = Subject.getSubject(context);
  if (subject == null || subject.getPrincipals(User.class).isEmpty()) {
    return getLoginUser();
  } else {
    return new UserGroupInformation(subject);
  }
}
 
Example 6
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 5 votes vote down vote up
/**
 * Log a user in from a keytab file. Loads a user identity from a keytab
 * file and logs them in. They become the currently logged-in user.
 * @param user the principal name to load from the keytab
 * @param path the path to the keytab file
 * @throws IOException if the keytab file can't be read
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized
static void loginUserFromKeytab(String user,
                                String path
                                ) throws IOException {
  if (!isSecurityEnabled())
    return;

  keytabFile = path;
  keytabPrincipal = user;
  Subject subject = new Subject();
  LoginContext login; 
  long start = 0;
  try {
    login = newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME,
          subject, new HadoopConfiguration());
    start = Time.now();
    login.login();
    metrics.loginSuccess.add(Time.now() - start);
    loginUser = new UserGroupInformation(subject);
    loginUser.setLogin(login);
    loginUser.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
  } catch (LoginException le) {
    if (start > 0) {
      metrics.loginFailure.add(Time.now() - start);
    }
    throw new IOException("Login failure for " + user + " from keytab " + 
                          path+ ": " + le, le);
  }
  LOG.info("Login successful for user " + keytabPrincipal
      + " using keytab file " + keytabFile);
}
 
Example 7
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 5 votes vote down vote up
/**
 * Run the given action as the user.
 * @param <T> the return type of the run method
 * @param action the method to execute
 * @return the value from the run method
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public <T> T doAs(PrivilegedAction<T> action) {
  logPrivilegedAction(subject, action);
  return Subject.doAs(subject, action);
}
 
Example 8
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 5 votes vote down vote up
/**
 * Log a user in from a keytab file. Loads a user identity from a keytab
 * file and logs them in. They become the currently logged-in user.
 * @param user the principal name to load from the keytab
 * @param path the path to the keytab file
 * @throws IOException if the keytab file can't be read
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized
static void loginUserFromKeytab(String user,
                                String path
                                ) throws IOException {
  if (!isSecurityEnabled())
    return;

  keytabFile = path;
  keytabPrincipal = user;
  Subject subject = new Subject();
  LoginContext login; 
  long start = 0;
  try {
    login = newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME,
          subject, new HadoopConfiguration());
    start = Time.now();
    login.login();
    metrics.loginSuccess.add(Time.now() - start);
    loginUser = new UserGroupInformation(subject);
    loginUser.setLogin(login);
    loginUser.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
  } catch (LoginException le) {
    if (start > 0) {
      metrics.loginFailure.add(Time.now() - start);
    }
    throw new IOException("Login failure for " + user + " from keytab " + 
                          path+ ": " + le, le);
  }
  LOG.info("Login successful for user " + keytabPrincipal
      + " using keytab file " + keytabFile);
}
 
Example 9
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 5 votes vote down vote up
/**
 * Create a UGI for testing HDFS and MapReduce
 * @param user the full user principal name
 * @param userGroups the names of the groups that the user belongs to
 * @return a fake user for running unit tests
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static UserGroupInformation createUserForTesting(String user, 
                                                        String[] userGroups) {
  ensureInitialized();
  UserGroupInformation ugi = createRemoteUser(user);
  // make sure that the testing object is setup
  if (!(groups instanceof TestingGroups)) {
    groups = new TestingGroups(groups);
  }
  // add the user groups
  ((TestingGroups) groups).setUserGroups(ugi.getShortUserName(), userGroups);
  return ugi;
}
 
Example 10
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 4 votes vote down vote up
/**
 * Re-Login a user in from the ticket cache.  This
 * method assumes that login had happened already.
 * The Subject field of this UserGroupInformation object is updated to have
 * the new credentials.
 * @throws IOException on a failure
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized void reloginFromTicketCache()
throws IOException {
  if (!isSecurityEnabled() || 
      user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
      !isKrbTkt)
    return;
  LoginContext login = getLogin();
  if (login == null) {
    throw new IOException("login must be done first");
  }
  long now = Time.now();
  if (!hasSufficientTimeElapsed(now)) {
    return;
  }
  // register most recent relogin attempt
  user.setLastLogin(now);
  try {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Initiating logout for " + getUserName());
    }
    //clear up the kerberos state. But the tokens are not cleared! As per 
    //the Java kerberos login module code, only the kerberos credentials
    //are cleared
    login.logout();
    //login and also update the subject field of this instance to 
    //have the new credentials (pass it to the LoginContext constructor)
    login = 
      newLoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, 
          getSubject(), new HadoopConfiguration());
    if (LOG.isDebugEnabled()) {
      LOG.debug("Initiating re-login for " + getUserName());
    }
    login.login();
    setLogin(login);
  } catch (LoginException le) {
    throw new IOException("Login failure for " + getUserName(), le);
  } 
}
 
Example 11
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 4 votes vote down vote up
/**
 * Re-Login a user in from a keytab file. Loads a user identity from a keytab
 * file and logs them in. They become the currently logged-in user. This
 * method assumes that {@link #loginUserFromKeytab(String, String)} had 
 * happened already.
 * The Subject field of this UserGroupInformation object is updated to have
 * the new credentials.
 * @throws IOException on a failure
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized void reloginFromKeytab()
throws IOException {
  if (!isSecurityEnabled() ||
       user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
       !isKeytab)
    return;
  
  long now = Time.now();
  if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) {
    return;
  }

  KerberosTicket tgt = getTGT();
  //Return if TGT is valid and is not going to expire soon.
  if (tgt != null && !shouldRenewImmediatelyForTests &&
      now < getRefreshTime(tgt)) {
    return;
  }
  
  LoginContext login = getLogin();
  if (login == null || keytabFile == null) {
    throw new IOException("loginUserFromKeyTab must be done first");
  }
  
  long start = 0;
  // register most recent relogin attempt
  user.setLastLogin(now);
  try {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Initiating logout for " + getUserName());
    }
    synchronized (UserGroupInformation.class) {
      // clear up the kerberos state. But the tokens are not cleared! As per
      // the Java kerberos login module code, only the kerberos credentials
      // are cleared
      login.logout();
      // login and also update the subject field of this instance to
      // have the new credentials (pass it to the LoginContext constructor)
      login = newLoginContext(
          HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, getSubject(),
          new HadoopConfiguration());
      if (LOG.isDebugEnabled()) {
        LOG.debug("Initiating re-login for " + keytabPrincipal);
      }
      start = Time.now();
      login.login();
      metrics.loginSuccess.add(Time.now() - start);
      setLogin(login);
    }
  } catch (LoginException le) {
    if (start > 0) {
      metrics.loginFailure.add(Time.now() - start);
    }
    throw new IOException("Login failure for " + keytabPrincipal + 
        " from keytab " + keytabFile, le);
  } 
}
 
Example 12
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 4 votes vote down vote up
/**
 * Get the user's full principal name.
 * @return the user's full principal name.
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public String getUserName() {
  return user.getName();
}
 
Example 13
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 4 votes vote down vote up
/**
 * Did the login happen via keytab
 * @return true or false
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized static boolean isLoginKeytabBased() throws IOException {
  return getLoginUser().isKeytab;
}
 
Example 14
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 4 votes vote down vote up
/**
 * Create a UserGroupInformation from a Kerberos ticket cache.
 * 
 * @param user                The principal name to load from the ticket
 *                            cache
 * @param ticketCachePath     the path to the ticket cache file
 *
 * @throws IOException        if the kerberos login fails
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static UserGroupInformation getUGIFromTicketCache(
          String ticketCache, String user) throws IOException {
  if (!isAuthenticationMethodEnabled(AuthenticationMethod.KERBEROS)) {
    return getBestUGI(null, user);
  }
  try {
    Map<String,String> krbOptions = new HashMap<String,String>();
    if (IBM_JAVA) {
      krbOptions.put("useDefaultCcache", "true");
      // The first value searched when "useDefaultCcache" is used.
      System.setProperty("KRB5CCNAME", ticketCache);
    } else {
      krbOptions.put("doNotPrompt", "true");
      krbOptions.put("useTicketCache", "true");
      krbOptions.put("useKeyTab", "false");
      krbOptions.put("ticketCache", ticketCache);
    }
    krbOptions.put("renewTGT", "false");
    krbOptions.putAll(HadoopConfiguration.BASIC_JAAS_OPTIONS);
    AppConfigurationEntry ace = new AppConfigurationEntry(
        KerberosUtil.getKrb5LoginModuleName(),
        LoginModuleControlFlag.REQUIRED,
        krbOptions);
    DynamicConfiguration dynConf =
        new DynamicConfiguration(new AppConfigurationEntry[]{ ace });
    LoginContext login = newLoginContext(
        HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, null, dynConf);
    login.login();

    Subject loginSubject = login.getSubject();
    Set<Principal> loginPrincipals = loginSubject.getPrincipals();
    if (loginPrincipals.isEmpty()) {
      throw new RuntimeException("No login principals found!");
    }
    if (loginPrincipals.size() != 1) {
      LOG.warn("found more than one principal in the ticket cache file " +
        ticketCache);
    }
    User ugiUser = new User(loginPrincipals.iterator().next().getName(),
        AuthenticationMethod.KERBEROS, login);
    loginSubject.getPrincipals().add(ugiUser);
    UserGroupInformation ugi = new UserGroupInformation(loginSubject);
    ugi.setLogin(login);
    ugi.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
    return ugi;
  } catch (LoginException le) {
    throw new IOException("failure to login using ticket cache file " +
        ticketCache, le);
  }
}
 
Example 15
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 4 votes vote down vote up
/**
 * Re-Login a user in from the ticket cache.  This
 * method assumes that login had happened already.
 * The Subject field of this UserGroupInformation object is updated to have
 * the new credentials.
 * @throws IOException on a failure
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized void reloginFromTicketCache()
throws IOException {
  if (!isSecurityEnabled() || 
      user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
      !isKrbTkt)
    return;
  LoginContext login = getLogin();
  if (login == null) {
    throw new IOException("login must be done first");
  }
  long now = Time.now();
  if (!hasSufficientTimeElapsed(now)) {
    return;
  }
  // register most recent relogin attempt
  user.setLastLogin(now);
  try {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Initiating logout for " + getUserName());
    }
    //clear up the kerberos state. But the tokens are not cleared! As per 
    //the Java kerberos login module code, only the kerberos credentials
    //are cleared
    login.logout();
    //login and also update the subject field of this instance to 
    //have the new credentials (pass it to the LoginContext constructor)
    login = 
      newLoginContext(HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, 
          getSubject(), new HadoopConfiguration());
    if (LOG.isDebugEnabled()) {
      LOG.debug("Initiating re-login for " + getUserName());
    }
    login.login();
    setLogin(login);
  } catch (LoginException le) {
    throw new IOException("Login failure for " + getUserName(), le);
  } 
}
 
Example 16
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 4 votes vote down vote up
/**
 * Re-Login a user in from a keytab file. Loads a user identity from a keytab
 * file and logs them in. They become the currently logged-in user. This
 * method assumes that {@link #loginUserFromKeytab(String, String)} had 
 * happened already.
 * The Subject field of this UserGroupInformation object is updated to have
 * the new credentials.
 * @throws IOException on a failure
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized void reloginFromKeytab()
throws IOException {
  if (!isSecurityEnabled() ||
       user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
       !isKeytab)
    return;
  
  long now = Time.now();
  if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) {
    return;
  }

  KerberosTicket tgt = getTGT();
  //Return if TGT is valid and is not going to expire soon.
  if (tgt != null && !shouldRenewImmediatelyForTests &&
      now < getRefreshTime(tgt)) {
    return;
  }
  
  LoginContext login = getLogin();
  if (login == null || keytabFile == null) {
    throw new IOException("loginUserFromKeyTab must be done first");
  }
  
  long start = 0;
  // register most recent relogin attempt
  user.setLastLogin(now);
  try {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Initiating logout for " + getUserName());
    }
    synchronized (UserGroupInformation.class) {
      // clear up the kerberos state. But the tokens are not cleared! As per
      // the Java kerberos login module code, only the kerberos credentials
      // are cleared
      login.logout();
      // login and also update the subject field of this instance to
      // have the new credentials (pass it to the LoginContext constructor)
      login = newLoginContext(
          HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, getSubject(),
          new HadoopConfiguration());
      if (LOG.isDebugEnabled()) {
        LOG.debug("Initiating re-login for " + keytabPrincipal);
      }
      start = Time.now();
      login.login();
      metrics.loginSuccess.add(Time.now() - start);
      setLogin(login);
    }
  } catch (LoginException le) {
    if (start > 0) {
      metrics.loginFailure.add(Time.now() - start);
    }
    throw new IOException("Login failure for " + keytabPrincipal + 
        " from keytab " + keytabFile, le);
  } 
}
 
Example 17
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 4 votes vote down vote up
/**
 * Create a UserGroupInformation from a Kerberos ticket cache.
 * 
 * @param user                The principal name to load from the ticket
 *                            cache
 * @param ticketCachePath     the path to the ticket cache file
 *
 * @throws IOException        if the kerberos login fails
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static UserGroupInformation getUGIFromTicketCache(
          String ticketCache, String user) throws IOException {
  if (!isAuthenticationMethodEnabled(AuthenticationMethod.KERBEROS)) {
    return getBestUGI(null, user);
  }
  try {
    Map<String,String> krbOptions = new HashMap<String,String>();
    if (IBM_JAVA) {
      krbOptions.put("useDefaultCcache", "true");
      // The first value searched when "useDefaultCcache" is used.
      System.setProperty("KRB5CCNAME", ticketCache);
    } else {
      krbOptions.put("doNotPrompt", "true");
      krbOptions.put("useTicketCache", "true");
      krbOptions.put("useKeyTab", "false");
      krbOptions.put("ticketCache", ticketCache);
    }
    krbOptions.put("renewTGT", "false");
    krbOptions.putAll(HadoopConfiguration.BASIC_JAAS_OPTIONS);
    AppConfigurationEntry ace = new AppConfigurationEntry(
        KerberosUtil.getKrb5LoginModuleName(),
        LoginModuleControlFlag.REQUIRED,
        krbOptions);
    DynamicConfiguration dynConf =
        new DynamicConfiguration(new AppConfigurationEntry[]{ ace });
    LoginContext login = newLoginContext(
        HadoopConfiguration.USER_KERBEROS_CONFIG_NAME, null, dynConf);
    login.login();

    Subject loginSubject = login.getSubject();
    Set<Principal> loginPrincipals = loginSubject.getPrincipals();
    if (loginPrincipals.isEmpty()) {
      throw new RuntimeException("No login principals found!");
    }
    if (loginPrincipals.size() != 1) {
      LOG.warn("found more than one principal in the ticket cache file " +
        ticketCache);
    }
    User ugiUser = new User(loginPrincipals.iterator().next().getName(),
        AuthenticationMethod.KERBEROS, login);
    loginSubject.getPrincipals().add(ugiUser);
    UserGroupInformation ugi = new UserGroupInformation(loginSubject);
    ugi.setLogin(login);
    ugi.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
    return ugi;
  } catch (LoginException le) {
    throw new IOException("failure to login using ticket cache file " +
        ticketCache, le);
  }
}
 
Example 18
Source File: SecurityUtil.java    From big-c with Apache License 2.0 3 votes vote down vote up
/**
 * Login as a principal specified in config. Substitute $host in
 * user's Kerberos principal name with a dynamically looked-up fully-qualified
 * domain name of the current host.
 * 
 * @param conf
 *          conf to use
 * @param keytabFileKey
 *          the key to look for keytab file in conf
 * @param userNameKey
 *          the key to look for user's Kerberos principal name in conf
 * @throws IOException if login fails
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static void login(final Configuration conf,
    final String keytabFileKey, final String userNameKey) throws IOException {
  login(conf, keytabFileKey, userNameKey, getLocalHostName());
}
 
Example 19
Source File: UserGroupInformation.java    From big-c with Apache License 2.0 2 votes vote down vote up
/**
 * Set the static configuration for UGI.
 * In particular, set the security authentication mechanism and the
 * group look up service.
 * @param conf the configuration to use
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static void setConfiguration(Configuration conf) {
  initialize(conf, true);
}
 
Example 20
Source File: UserGroupInformation.java    From hadoop with Apache License 2.0 2 votes vote down vote up
/**
 * Set the static configuration for UGI.
 * In particular, set the security authentication mechanism and the
 * group look up service.
 * @param conf the configuration to use
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static void setConfiguration(Configuration conf) {
  initialize(conf, true);
}