Package rogatkin.app

Source Code of rogatkin.app.SimpleJndi$SimpleName

/* tjws - SimpleJndi.java
* Copyright (C) 1999-2010 Dmitriy Rogatkin.  All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
*  ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
*  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
*  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
*  Visit http://tjws.sourceforge.net to get the latest information
*  about Rogatkin's products.                                                       
*  $Id: SimpleJndi.java,v 1.17 2011/06/29 05:06:18 dmitriy Exp $               
*  Created on Mar 25, 2007
*  @author Dmitriy
*/
package rogatkin.app;

import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Map;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NameNotFoundException;
import javax.naming.spi.InitialContextFactory;
import javax.naming.OperationNotSupportedException;
import javax.sql.DataSource;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;

import org.omg.CORBA.ORB;
import org.omg.CORBA.COMM_FAILURE;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.BAD_PARAM;

import Acme.Serve.Serve;

import rogatkin.app.remote.simple_naming;
import rogatkin.app.remote.simple_namingHelper;
import rogatkin.app.remote.simple_namingPOA;
import rogatkin.app.remote.naming_exception;
import rogatkin.app.remote.bind_listHolder;
import rogatkin.app.remote.bind;

public class SimpleJndi implements InitialContextFactory {
  public static final char NAME_SEP_CHAR = '/';

  public static final String NAME_SEP = "" + NAME_SEP_CHAR; // can be "\\."

  public static final String NAME_SEP_REGEXP = NAME_SEP;

  static ORB orb;

  static org.omg.PortableServer.POA rootPoa;

  protected static SimpleContext mainContext;

  protected static simple_naming remoteContext;

  static volatile boolean intitalized;

  public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
    return initMainContext(new SimpleContext(arg0));
  }

  private SimpleContext initMainContext(SimpleContext context) {
    if (intitalized == false)
      synchronized (SimpleJndi.class) {
        if (intitalized == false) {
          try {
            URL provUrl = new URL(System.getProperty(Context.PROVIDER_URL) == null ? (String) context
                .getEnvironment().get(Context.PROVIDER_URL) : System.getProperty(Context.PROVIDER_URL));
            if (true || "tjws".equals(provUrl.getProtocol())) {
              // try to connect
              try {
                if (__debug)
                  System.err.printf("Url %s to connect%n", new URL("http", provUrl.getHost(), provUrl
                      .getPort(), "getRootContext"));
                String ior = readUrltoString(new URL("http", provUrl.getHost(), provUrl.getPort(),
                    "getRootContext"));
                if (__debug)
                  System.err.printf("==========>>>>Read IOR: %s%n", ior);
                if (ior == null)
                  throw new IOException("IOR is null");
                remoteContext = simple_namingHelper.narrow(orb.string_to_object(ior));
              } catch (IOException e) {
                if (__debug)
                  e.printStackTrace();
                mainContext = exposeContext(context, provUrl.getHost(), provUrl.getPort());
              }
            }
          } catch (NamingException e) {
            System.err.printf("JNDI url %s%n", e);
          } catch (MalformedURLException e) {
            System.err.printf("JNDI url %s%n", e);
            if (__debug)
              e.printStackTrace();
          } finally {
            intitalized = true;
          }
        }
      }
    return context;
  }

  private SimpleContext exposeContext(SimpleContext context, String host, int port) {
    final Serve srv = new Acme.Serve.Serve();
    Properties properties = new java.util.Properties();
    properties.put(Serve.ARG_PORT, port);
    properties.put(Serve.ARG_NOHUP, Serve.ARG_NOHUP);
    if (host != null && host.length() > 0 && "localhost".equals(host) == false)
      properties.put(Serve.ARG_BINDADDRESS, host);
    srv.arguments = properties;
    srv.addServlet("/getRootContext", new HttpServlet() {
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
          java.io.IOException {
        if (mainContext != null) {
          try {
            resp.setContentType("text/plain");
            resp.getWriter().write(orb.object_to_string(rootPoa.servant_to_reference(mainContext)));
          } catch (org.omg.CORBA.UserException ce) {
            ce.printStackTrace();
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
          }
        } else
          resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
      }
    });
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
      public void run() {
        srv.notifyStop();
        srv.destroyAllServlets();
      }
    }));
    Thread exposerThread = new Thread("ContextExposer") {
      public void run() {
        srv.serve();
      }
    };
    exposerThread.setDaemon(true);
    exposerThread.setPriority(Thread.MIN_PRIORITY);
    exposerThread.start();
    return context;
  }

  private byte[] readUrl(URL url) throws IOException {
    URLConnection uc = url.openConnection();
    uc.setRequestProperty("connection", "close");
    uc.connect();
    if (((HttpURLConnection) uc).getResponseCode() == HttpURLConnection.HTTP_OK) {
      int cl = uc.getContentLength();
      if (cl > 0) {
        byte[] bytes = new byte[cl];
        int pos = 0;
        int l = bytes.length;
        do {
          int k = uc.getInputStream().read(bytes, pos, l);
          if (k <= 0)
            break;
          pos += k;
          l -= k;
        } while (l > 0);
        return bytes;
      } else {
        ByteArrayOutputStream bas = new ByteArrayOutputStream(512);
        byte[] bytes = new byte[256];
        InputStream is = uc.getInputStream();
        int l;
        while ((l = is.read(bytes)) > 0)
          bas.write(bytes, 0, l);
        return bas.toByteArray();
      }
    } else
      System.err.printf("Respose code %d%n", ((HttpURLConnection) uc).getResponseCode());
    return null;
  }

  private String readUrltoString(URL url) throws IOException {
    byte[] bytes = readUrl(url);
    if (bytes != null)
      return new String(bytes);
    return null;
  }

  public static void main(String... args) {
    try {
      System.getProperties().setProperty(Context.INITIAL_CONTEXT_FACTORY, SimpleJndi.class.getName());
      new SimpleDataSource(args[0]);

      Connection con = ((DataSource) new InitialContext().lookup("jdbc/access/MediaChest")).getConnection();
      System.out.printf("Connection taken %s\n", con);
      con.close();
    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (SQLException e) {
      e.printStackTrace();
    } catch (NamingException e) {
      e.printStackTrace();
    }
  }

  static class SimpleContext extends simple_namingPOA implements Context {
    private static Hashtable<String, Object> environment = new Hashtable<String, Object>();

    private static HashMap<String, Object> directory = new HashMap<String, Object>();

    SimpleContext(Hashtable<?, ?> env) {
      if (rootPoa == null)
        synchronized (SimpleContext.class) {
          if (rootPoa == null) {
            try {
              String orbArgs = System.getProperty("tjws.app.orb.arguments");
              // TODO: add properties
              if (orbArgs == null)
                orb = ORB.init(new String[]{}, new Properties());
              else {
                orb = ORB.init(orbArgs.split(","), new Properties());
              }
              rootPoa = org.omg.PortableServer.POAHelper
                  .narrow(orb.resolve_initial_references("RootPOA"));
              rootPoa.the_POAManager().activate();
            } catch (SystemException se) {
              se.printStackTrace();
            } catch (org.omg.CORBA.UserException ue) {
              ue.printStackTrace();
            }
          }
        }
      for (Object key : env.keySet()) {
        environment.put(key.toString(), env.get(key));
      }
    }

    public Object addToEnvironment(String arg0, Object arg1) throws NamingException {
      return environment.put(arg0, arg1);
    }

    public void bind(Name arg0, Object arg1) throws NamingException {
      bind(arg0.toString(), arg1);
    }

    public void bind(String arg0, Object arg1) throws NamingException {
      if (__debug)
        System.err.printf("===== Binding: %s at %s%n", arg0, arg1); ///////////////////////

      arg0 = arg0.trim();
      try {
        if (lookup(arg0) != null)
          throw new NameAlreadyBoundException();
      } catch (NameNotFoundException ne) {
        if (__debug)
          System.err.printf("Remote ctx %s%n", remoteContext);
        if (remoteContext != null)
          if (arg1 instanceof org.omg.CORBA.Object)
            try {
              remoteContext.bind1(arg0, (org.omg.CORBA.Object) arg1);
            } catch (naming_exception ne1) {
              throw new javax.naming.NameNotFoundException("Can't bind in remote repository");
            } catch (org.omg.CORBA.UserException ce) {
              ce.printStackTrace();
              throw new NamingException();
            }
          else if (arg1 instanceof org.omg.PortableServer.Servant)
            try {
              remoteContext.bind1(arg0, rootPoa
                  .servant_to_reference((org.omg.PortableServer.Servant) arg1));
            } catch (naming_exception ne1) {
              throw new javax.naming.NameNotFoundException("Can't bind in remote repository");
            } catch (org.omg.CORBA.UserException ce) {
              ce.printStackTrace();
              throw new NamingException();
            }
          else
            synchronized (directory) {
              directory.put(arg0, arg1);
            }
        else
          synchronized (directory) {
            directory.put(arg0, arg1);
          }
      }
    }

    public void close() throws NamingException {

      // TODO disconnect all remote objects
      if (remoteContext != null) {
        // deactivate
        orb.disconnect(remoteContext);
      }
    }

    public Name composeName(Name arg0, Name arg1) throws NamingException {
      return new SimpleName(composeName(arg0.toString(), arg1.toString()));
    }

    public String composeName(String arg0, String arg1) throws NamingException {
      return arg0 + NAME_SEP + arg1;
    }

    public Context createSubcontext(Name arg0) throws NamingException {
      return createSubcontext(arg0.toString());
    }

    public Context createSubcontext(String arg0) throws NamingException {
      final String subcontextName = arg0.endsWith(NAME_SEP) ? arg0 : arg0 + NAME_SEP;
      return (Context) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Context.class },
          new InvocationHandler() {
            public Object invoke(Object conn, Method methd, Object[] params) throws Throwable {
              for (int i = 0; i < params.length; i++)
                if (params[i] instanceof String)
                  params[i] = subcontextName + (String) params[i];
                else if (params[i] instanceof Name)
                  params[i] = new SimpleName(subcontextName + params[i].toString());
              try {
                return methd.invoke(SimpleContext.this, params);
              } catch (InvocationTargetException ite) {
                throw ite.getCause();
              }
            }
          });
    }

    public void destroySubcontext(Name arg0) throws NamingException {
      destroySubcontext(arg0.toString());
    }

    public void destroySubcontext(String arg0) throws NamingException {
      String subcontextName = arg0.endsWith(NAME_SEP) ? arg0 : arg0 + NAME_SEP;
      synchronized (directory) {
        for (String key : directory.keySet())
          if (key.startsWith(subcontextName))
            directory.remove(key);
      }
    }

    public Hashtable<?, ?> getEnvironment() throws NamingException {
      return environment;
    }

    public String getNameInNamespace() throws NamingException {
      throw new OperationNotSupportedException();
    }

    public NameParser getNameParser(Name arg0) throws NamingException {
      return new SimpleNameParser(arg0.toString());
    }

    public NameParser getNameParser(String arg0) throws NamingException {
      return new SimpleNameParser(arg0);
    }

    public NamingEnumeration<NameClassPair> list(Name arg0) throws NamingException {
      return list(arg0.toString());
    }

    public NamingEnumeration<NameClassPair> list(final String arg0) throws NamingException {
      return new SimpleNamingEnumeration<NameClassPair>(narrowLocal(arg0)) {
        public NameClassPair createEntry(String name, Object v) {
          return new NameClassPair(name, v == null ? null : v.getClass().getName());
        }

        public bind[] getRemoteDinds() {
          bind_listHolder h = new bind_listHolder();
          try {
            remoteContext.list1(arg0, h);
            return h.value;
          } catch (naming_exception ne) {

          } catch (COMM_FAILURE cfe) {
            // Can't connect to remote JNDI server:
          }
          return null;
        }
      };

    }

    public NamingEnumeration<Binding> listBindings(Name arg0) throws NamingException {
      return listBindings(arg0.toString());
    }

    public NamingEnumeration<Binding> listBindings(final String arg0) throws NamingException {
      return new SimpleNamingEnumeration<Binding>(narrowLocal(arg0)) {
        public Binding createEntry(String name, Object v) {
          return new Binding(name, v);
        }

        public bind[] getRemoteDinds() {
          if (remoteContext != null)
            try {
              bind_listHolder h = new bind_listHolder();
              remoteContext.list1(arg0, h);
              return h.value;
            } catch (naming_exception ne) {

            }  catch (COMM_FAILURE cfe) {
              // Can't connect to remote JNDI server:
            }
          return null;
        }
      };
    }

    public Object lookup(Name arg0) throws NamingException {
      return lookup(arg0.toString());
    }

    public Object lookup(String arg0) throws NamingException {
      if (__debug)
        System.err.printf("Requested %s = %s%n", arg0, directory.get(arg0)); ///////////////
      if (arg0.length() == 0)
        return this;
      Object result = directory.get(arg0.trim());
      if (result == null && remoteContext != null) {
        try {
          if (__debug)
            System.err.printf("Looking remotely%n");
          result = remoteContext.lookup1(arg0);
        } catch (naming_exception ne) {
        } catch (COMM_FAILURE cfe) {
        }
      }
      if (result == null)
        throw new NameNotFoundException(arg0 + " not found");
      return result;
    }

    public Object lookupLink(Name arg0) throws NamingException {
      return lookupLink(arg0.toString());
    }

    public Object lookupLink(String arg0) throws NamingException {
      // links not supported, only local and remote objects
      return lookup(arg0);
    }

    public void rebind(Name arg0, Object arg1) throws NamingException {
      rebind(arg0.toString(), arg1);
    }

    public void rebind(String arg0, Object arg1) throws NamingException {
      unbind(arg0);
      bind(arg0, arg1);
    }

    public Object removeFromEnvironment(String arg0) throws NamingException {
      return environment.remove(arg0);
    }

    public void rename(Name arg0, Name arg1) throws NamingException {
      rename(arg0.toString(), arg1.toString());
    }

    public void rename(String arg0, String arg1) throws NamingException {
      Object o = lookup(arg0);
      unbind(arg0);
      bind(arg1, o);
    }

    public void unbind(Name arg0) throws NamingException {
      unbind(arg0.toString());
    }

    public void unbind(String arg0) throws NamingException {
      if (directory.remove(arg0) == null)
        throw new NamingException("Not found: " + arg0);
    }

    private Iterator<Map.Entry<String, Object>> narrowLocal(String prefix) {
      ArrayList<Map.Entry<String, Object>> result = new ArrayList<Map.Entry<String, Object>>(directory.size());
      synchronized (directory) {
        if (prefix.length() == 0) {
          for (Map.Entry<String, Object> e : directory.entrySet())
            result.add(e);
        } else {
          for (Map.Entry<String, Object> e : directory.entrySet())
            if (e.getKey().startsWith(prefix))
              result.add(e);
        }
      }
      return result.iterator();
    }

    /////////////////////////  CORBA methods //////////////////////////////
    public org.omg.CORBA.Object lookup1(String name) throws naming_exception {
      name = name.trim();
      if (__debug)
        System.err.printf("Remote LOOKUP1 %s%n", name);
      // TODO name.length() == 0 -> return this
      Object result = directory.get(name.trim());
      if (result instanceof org.omg.CORBA.Object)
        return (org.omg.CORBA.Object) result;
      throw new naming_exception();
    }

    public void unbind1(String name) throws naming_exception {
      Object result = lookup1(name);
      if (result instanceof org.omg.CORBA.Object) {
        synchronized (directory) {
          directory.remove(name.trim());
        }
        try {
          rootPoa.deactivate_object(rootPoa.reference_to_id((org.omg.CORBA.Object) result));
        } catch (org.omg.CORBA.UserException ce) {

        }
      } else
        throw new naming_exception();
    }

    public void bind1(String name, org.omg.CORBA.Object o) throws naming_exception {
      if (__debug)
        System.err.printf("Remote bind %s%n", name);
      synchronized (directory) {
        if (directory.containsKey(name.trim()) == false)
          directory.put(name.trim(), o);
        else
          throw new naming_exception();
      }
    }

    public void list1(String filter, bind_listHolder binds) {
      if (__debug)
        System.err.printf("list1 called!!!!!!%n");
      Iterator<Map.Entry<String, Object>> i = narrowLocal(filter);
      ArrayList<bind> resBinds = new ArrayList<bind>(directory.size());
      while (i.hasNext()) {
        Map.Entry<String, Object> e = i.next();
        if (e.getValue() instanceof org.omg.CORBA.Object)
          resBinds.add(new bind(e.getKey(), (org.omg.CORBA.Object) e.getValue()));
      }
      binds.value = (bind[]) resBinds.toArray(new bind[resBinds.size()]);
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////
  }

  protected static abstract class SimpleNamingEnumeration<T> implements NamingEnumeration<T> {
    Iterator<Map.Entry<String, Object>> i;

    //simple_naming r;

    bind[] remoteBinds;

    int bi;

    SimpleNamingEnumeration(Iterator<Map.Entry<String, Object>> ei) {
      i = ei;
    }

    abstract T createEntry(String name, Object v);

    abstract bind[] getRemoteDinds();

    public void close() {
      i = null;
    }

    public boolean hasMore() {
      boolean result = false;
      if (i != null) {
        result = i.hasNext();
        if (result == false) {
          remoteBinds = getRemoteDinds();
          bi = 0;
          i = null;
        } else
          return result;
      }
      if (remoteBinds != null)
        return remoteBinds.length > bi;
      return result;
    }

    public boolean hasMoreElements() {
      return hasMore();
    }

    public T next() {
      if (hasMore()) {
        if (i != null) {
          Map.Entry<String, Object> e = i.next();
          return createEntry(e.getKey(), e.getValue());
        }
        if (remoteBinds != null) {
          bind b = remoteBinds[bi];
          bi++;
          return createEntry(b.name, b.o);
        }
      }
      throw new NoSuchElementException();
    }

    public T nextElement() {
      return next();
    }
  }

  protected static class SimpleNameParser implements NameParser {
    String parent;

    public SimpleNameParser(String name) {
      parent = name;
    }

    public Name parse(String arg0) throws NamingException {
      return new SimpleName(parent + NAME_SEP + arg0);
    }
  }

  static class SimpleName implements Name {
    private String name;

    public SimpleName(String n) {
      name = n;
    }

    public String toString() {
      return name;
    }

    public Name add(String arg0) throws InvalidNameException {
      if (arg0.indexOf(NAME_SEP_CHAR) >= 0)
        throw new InvalidNameException();
      name = name + NAME_SEP + arg0;
      return this;
    }

    public Name add(int arg0, String arg1) throws InvalidNameException {
      if (arg1.indexOf(NAME_SEP_CHAR) >= 0)
        throw new InvalidNameException();
      String[] cn = name.split(NAME_SEP);
      name = connect(cn, 0, arg0) + NAME_SEP + arg1 + NAME_SEP + connect(cn, arg0, cn.length - arg0);
      return this;
    }

    public Name addAll(Name arg0) throws InvalidNameException {
      name = name + NAME_SEP + arg0.toString();
      return this;
    }

    public Name addAll(int arg0, Name arg1) throws InvalidNameException {
      return add(arg0, arg1.toString());
    }

    public int compareTo(Object arg0) {
      return name.compareTo(arg0.toString());
    }

    public boolean endsWith(Name arg0) {
      return name.endsWith(arg0.toString());
    }

    public String get(int arg0) {
      String[] cn = name.split(NAME_SEP);
      return cn[arg0];
    }

    public Enumeration<String> getAll() {
      return new Enumeration<String>() {
        String cn[];

        int i = 0;

        public boolean hasMoreElements() {
          if (cn == null) {
            cn = name.split("");
            i = 0;
          }
          return i < cn.length;
        }

        public String nextElement() {
          if (hasMoreElements()) {
            return cn[i++];
          }
          throw new NoSuchElementException();
        }
      };
    }

    public Name getPrefix(int arg0) {
      String[] cn = name.split(NAME_SEP);
      return new SimpleName(connect(cn, 0, arg0));
    }

    public Name getSuffix(int arg0) {
      String[] cn = name.split(NAME_SEP);
      return new SimpleName(connect(cn, arg0, cn.length - arg0));
    }

    public boolean isEmpty() {
      return name.length() == 0;
    }

    public Object remove(int arg0) throws InvalidNameException {
      String[] cn = name.split(NAME_SEP);
      name = connect(cn, 0, arg0) + NAME_SEP + connect(cn, arg0, cn.length - arg0);
      return cn[arg0];
    }

    public int size() {
      return name.split(NAME_SEP).length;
    }

    public boolean startsWith(Name arg0) {
      return name.startsWith(arg0.toString());
    }

    public Object clone() {
      return new SimpleName(name);
    }

    private String connect(String[] parts, int s, int len) throws ArrayIndexOutOfBoundsException {
      if (parts == null || parts.length == 0)
        return "";
      if (len < 0 || s < 0 || s + len > parts.length)
        throw new ArrayIndexOutOfBoundsException(String.format("s=%d, l=%d, nl=%d", s, len, parts.length));
      String result = parts[s];
      for (int i = s + 1; i < parts.length && i < len + s; i++)
        result += NAME_SEP + parts[i];
      return result;
    }
  }

  final private static boolean __debug = false;

}
TOP

Related Classes of rogatkin.app.SimpleJndi$SimpleName

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.