Package org.apache.marmotta.kiwi.reasoner.persistence

Examples of org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection


        // clean up all justifications
        updateTaskStatus("removing old justifications");


        try {
            KiWiReasoningConnection connection = persistence.getConnection();
            try {

                // remove all justifications from the database
                connection.deleteJustifications();

                // clean up inferred triples by removing them from the triple store; the transaction system should take care of the rest
                cleanupUnsupported(connection);

                // and finally garbage collect those triples that are inferred and deleted
                // garbage collection is now carried out by a thread in the triple store
                //garbageCollectTriples();
                connection.commit();
            } catch (SQLException ex) {
                connection.rollback();
                throw ex;
            } finally {
                connection.close();
            }
        } catch (SailException ex) {
            log.error("REPOSITORY ERROR: could not clean up unsupported triples, database state will be inconsistent! Message: {}", ex.getMessage());
            log.debug("Exception details:", ex);
        } catch (SQLException ex) {
View Full Code Here


            body.remove(p);
        }

        CloseableIteration<QueryResult, SQLException> bodyResult;

        KiWiReasoningConnection connection = persistence.getConnection();
        SailConnection     sail = store.getConnection();
        KiWiSailConnection isail = getWrappedConnection(sail);
        try {

            // if there are further patterns, evaluate them; if the matched pattern was the only pattern, then
            // simply take the match as binding
            if(body.size() > 0) {
                bodyResult = connection.query(body,match,null,null,true);
            } else if(match != null) {
                bodyResult = new SingletonIteration<QueryResult, SQLException>(match);
            } else {
                bodyResult = new EmptyIteration<QueryResult, SQLException>();
            }

            // construct triples out of the bindings and the rule heads
            long counter = 0;

            // initialise a new set of justifications
            Set<Justification> justifications = new HashSet<Justification>();

            sail.begin();
            while(bodyResult.hasNext()) {
                QueryResult row = bodyResult.next();
                Map<VariableField,KiWiNode> binding = row.getBindings();

                Resource subject = null;
                URI property = null;
                Value object;

                if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isVariableField()) {
                    if(!binding.get(rule.getHead().getSubject()).isUriResource() && !binding.get(rule.getHead().getSubject()).isAnonymousResource()) {
                        log.info("cannot use value {} as subject, because it is not a resource",binding.get(rule.getHead().getSubject()));
                        continue;
                    }
                    subject = (KiWiResource)binding.get(rule.getHead().getSubject());
                } else if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isResourceField()) {
                    subject = ((ResourceField)rule.getHead().getSubject()).getResource();
                } else
                    throw new IllegalArgumentException("Subject of rule head may only be a variable or a resource; rule: "+rule);

                if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isVariableField()) {
                    if(!binding.get(rule.getHead().getProperty()).isUriResource()) {
                        log.info("cannot use value {} as property, because it is not a URI resource",binding.get(rule.getHead().getProperty()));
                        continue;
                    }
                    property = (KiWiUriResource)binding.get(rule.getHead().getProperty());
                } else if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isResourceField()) {
                    property = (KiWiUriResource)((ResourceField)rule.getHead().getProperty()).getResource();
                } else
                    throw new IllegalArgumentException("Property of rule head may only be a variable or a resource; rule: "+rule);

                if(rule.getHead().getObject() != null && rule.getHead().getObject().isVariableField()) {
                    object = binding.get(rule.getHead().getObject());
                } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isResourceField()) {
                    object = ((ResourceField)rule.getHead().getObject()).getResource();
                } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isLiteralField()) {
                    object = ((LiteralField)rule.getHead().getObject()).getLiteral();
                } else
                    throw new IllegalArgumentException("Object of rule head may only be a variable, a literal, or a resource; rule: "+rule);


                KiWiTriple triple = isail.addInferredStatement(subject, property, object);

                Justification justification = new Justification();
                justification.setTriple(triple);
                justification.getSupportingRules().add(rule);
                justification.getSupportingTriples().addAll(row.getJustifications());
                justifications.add(justification);

                // when the batch size is reached, commit the transaction, save the justifications, and start a new
                // transaction and new justification set
                if(++counter % config.getBatchSize() == 0) {
                    persistenceLock.lock();

                    try {

                        sail.commit();

                        log.debug("adding {} justifications",justifications.size());

                        updateTaskStatus("storing justifications ...");
                        Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);

                        if(config.isRemoveDuplicateJustifications()) {
                            removeDuplicateJustifications(connection,baseJustifications);
                        }

                        log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());

                        // persist the justifications that have been created in the rule processing
                        if(baseJustifications.size() > 0) {
                            connection.storeJustifications(baseJustifications);
                        }
                        connection.commit();
                        sail.begin();
                    } finally {
                        persistenceLock.unlock();
                    }
                    justifications.clear();
                }
            }

            persistenceLock.lock();
            try {
                sail.commit();

                log.debug("adding {} justifications",justifications.size());
                updateTaskStatus("storing justifications ...");
                Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);

                if(config.isRemoveDuplicateJustifications()) {
                    removeDuplicateJustifications(connection,baseJustifications);
                }

                // persist the justifications that have been created in the rule processing
                if(baseJustifications.size() > 0) {
                    connection.storeJustifications(baseJustifications);
                }

                log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());

                Iterations.closeCloseable(bodyResult);
                connection.commit();
            } finally {
                persistenceLock.unlock();
            }
        } catch(SailException | SQLException | ReasoningException ex) {
            log.error("REASONING ERROR: could not process rule, database state will be inconsistent! Message: {}",ex.getMessage());
            log.debug("Exception details:",ex);

            connection.rollback();
            sail.rollback();
            throw ex;
        } finally {
            connection.close();
            sail.close();
        }

    }
View Full Code Here

        // running the reasoner)
        KWRLProgramParserBase parser = new KWRLProgramParser(v, this.getClass().getResourceAsStream("test-001.kwrl"));
        Program p = parser.parseProgram();
        p.setName("test-001");

        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            // should not throw an exception and the program should have a database ID afterwards
            connection.storeProgram(p);
            connection.commit();
        } finally {
            connection.close();
        }

        // then get a connection to the repository and create a number of triples, some inferred and some base
        RepositoryConnection con = repository.getConnection();
        try {
            con.add(s1,p1,o1);
            con.add(s2,p1,o2);
            con.add(s3,p1,o3);

            con.add(s1,p2,o1,ctxi);
            con.add(s2,p2,o2,ctxi);
            con.add(s3,p2,o3,ctxi);

            con.commit();
        } finally {
            con.close();
        }

        connection = rpersistence.getConnection();
        try {
            // retrieve the persisted triples and put them into two sets to build justifications
            List<Statement> baseTriples = asList(connection.listTriples(null,null,null,v.convert(ctxb),false));
            List<Statement> infTriples = asList(connection.listTriples(null,null,null,v.convert(ctxi),true));

            Assert.assertEquals("number of base triples was not 3", 3, baseTriples.size());
            Assert.assertEquals("number of inferred triples was not 3", 3, infTriples.size());

            // we manually update the "inferred" flag for all inferred triples, since this is not possible through the
            // repository API
            PreparedStatement updateInferred = connection.getJDBCConnection().prepareStatement("UPDATE triples SET inferred = true WHERE id = ?");
            for(Statement stmt : infTriples) {
                KiWiTriple triple = (KiWiTriple)stmt;
                updateInferred.setLong(1,triple.getId());
                updateInferred.addBatch();
            }
            updateInferred.executeBatch();
            updateInferred.close();

            // now we create some justifications for the inferred triples and store them
            Set<Justification> justifications = new HashSet<Justification>();
            Justification j1 = new Justification();
            j1.getSupportingRules().add(p.getRules().get(0));
            j1.getSupportingRules().add(p.getRules().get(1));
            j1.getSupportingTriples().add((KiWiTriple) baseTriples.get(0));
            j1.getSupportingTriples().add((KiWiTriple) baseTriples.get(1));
            j1.setTriple((KiWiTriple) infTriples.get(0));
            justifications.add(j1);

            Justification j2 = new Justification();
            j2.getSupportingRules().add(p.getRules().get(1));
            j2.getSupportingTriples().add((KiWiTriple) baseTriples.get(1));
            j2.getSupportingTriples().add((KiWiTriple) baseTriples.get(2));
            j2.setTriple((KiWiTriple) infTriples.get(1));
            justifications.add(j2);

            connection.storeJustifications(justifications);
            connection.commit();

            // we should now have two justifications in the database
            PreparedStatement listJustifications = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_justifications");
            ResultSet resultListJustifications = listJustifications.executeQuery();

            Assert.assertTrue(resultListJustifications.next());
            Assert.assertEquals(2, resultListJustifications.getInt("count"));
            resultListJustifications.close();
            connection.commit();

            PreparedStatement listSupportingTriples = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_just_supp_triples");
            ResultSet resultListSupportingTriples = listSupportingTriples.executeQuery();

            Assert.assertTrue(resultListSupportingTriples.next());
            Assert.assertEquals(4, resultListSupportingTriples.getInt("count"));
            resultListSupportingTriples.close();
            connection.commit();

            PreparedStatement listSupportingRules = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_just_supp_rules");
            ResultSet resultListSupportingRules = listSupportingRules.executeQuery();

            Assert.assertTrue(resultListSupportingRules.next());
            Assert.assertEquals(3, resultListSupportingRules.getInt("count"));
            resultListSupportingRules.close();
            connection.commit();



            // *** check listing justifications by base triple (supporting triple)

            // there should now be two justifications based on triple baseTriples.get(1))
            List<Justification> supported1 = asList(connection.listJustificationsBySupporting((KiWiTriple) baseTriples.get(1)));
            Assert.assertEquals("number of justifications is wrong",2,supported1.size());
            Assert.assertThat("justifications differ", supported1, hasItems(j1,j2));

            // only j1 should be supported by triple baseTriples.get(0))
            List<Justification> supported2 = asList(connection.listJustificationsBySupporting((KiWiTriple) baseTriples.get(0)));
            Assert.assertEquals("number of justifications is wrong", 1, supported2.size());
            Assert.assertThat("justifications differ", supported2, allOf(hasItem(j1), not(hasItem(j2))));

            // only j2 should be supported by triple baseTriples.get(2))
            List<Justification> supported3 = asList(connection.listJustificationsBySupporting((KiWiTriple) baseTriples.get(2)));
            Assert.assertEquals("number of justifications is wrong", 1, supported3.size());
            Assert.assertThat("justifications differ", supported3, allOf(hasItem(j2), not(hasItem(j1))));

            // *** check listing justificatoins by supporting rule

            // there should now be two justifications based on triple p.getRules().get(1)
            List<Justification> supported4 = asList(connection.listJustificationsBySupporting(p.getRules().get(1)));
            Assert.assertEquals("number of justifications is wrong", 2, supported4.size());
            Assert.assertThat("justifications differ", supported4, hasItems(j1,j2));

            // only j1 should be supported by triple p.getRules().get(0)
            List<Justification> supported5 = asList(connection.listJustificationsBySupporting(p.getRules().get(0)));
            Assert.assertEquals("number of justifications is wrong", 1, supported5.size());
            Assert.assertThat("justifications differ", supported5, allOf(hasItem(j1), not(hasItem(j2))));


            // *** check listing justifications by supported (inferred) triple

            // there should now be one justification supporting infTriples.get(0)
            List<Justification> supported6 = asList(connection.listJustificationsForTriple((KiWiTriple) infTriples.get(0)));
            Assert.assertEquals("number of justifications is wrong", 1, supported6.size());
            Assert.assertThat("justifications differ", supported6, allOf(hasItem(j1), not(hasItem(j2))));

            // there should now be one justification supporting infTriples.get(1)
            List<Justification> supported7 = asList(connection.listJustificationsForTriple((KiWiTriple) infTriples.get(1)));
            Assert.assertEquals("number of justifications is wrong", 1, supported7.size());
            Assert.assertThat("justifications differ", supported7, allOf(hasItem(j2), not(hasItem(j1))));

            // there should now be no justification supporting infTriples.get(2)
            List<Justification> supported8 = asList(connection.listJustificationsForTriple((KiWiTriple) infTriples.get(2)));
            Assert.assertEquals("number of justifications is wrong", 0, supported8.size());


            // *** check listing unsupported triples
            List<KiWiTriple> unsupported = asList(connection.listUnsupportedTriples());
            Assert.assertEquals("number of unsupported triples is wrong",1,unsupported.size());
            Assert.assertThat("unsupported triples differ", unsupported, hasItem((KiWiTriple)infTriples.get(2)));


            // now we delete justification 2; as a consequence,
            // - there should be only once justification left
            // - there should be two unsupported triples
            connection.deleteJustifications(Collections.singleton(j2));


            // we should now have one justifications in the database
            resultListJustifications = listJustifications.executeQuery();

            Assert.assertTrue(resultListJustifications.next());
            Assert.assertEquals(1, resultListJustifications.getInt("count"));
            resultListJustifications.close();
            connection.commit();

            resultListSupportingTriples = listSupportingTriples.executeQuery();

            Assert.assertTrue(resultListSupportingTriples.next());
            Assert.assertEquals(2, resultListSupportingTriples.getInt("count"));
            resultListSupportingTriples.close();
            connection.commit();

            resultListSupportingRules = listSupportingRules.executeQuery();

            Assert.assertTrue(resultListSupportingRules.next());
            Assert.assertEquals(2, resultListSupportingRules.getInt("count"));
            resultListSupportingRules.close();
            connection.commit();

            List<KiWiTriple> unsupported2 = asList(connection.listUnsupportedTriples());
            Assert.assertEquals("number of unsupported triples is wrong",2,unsupported2.size());
            Assert.assertThat("unsupported triples differ", unsupported2, hasItem((KiWiTriple)infTriples.get(1)));


        } catch(BatchUpdateException ex) {
            if(ex.getNextException() != null) {
                ex.printStackTrace();
                throw ex.getNextException();
            } else {
                throw ex;
            }
        } finally {
            connection.close();
        }

    }
View Full Code Here

    public void testStoreLoadProgram() throws Exception {
        KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream("test-001.kwrl"));
        Program p = parser.parseProgram();
        p.setName("test-001");

        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            // should not throw an exception and the program should have a database ID afterwards
            connection.storeProgram(p);
            connection.commit();

            Assert.assertNotNull("program did not get a database ID",p.getId());

            // load the program by name and check if it is equal to the original program
            Program p1 = connection.loadProgram("test-001");
            connection.commit();

            Assert.assertNotNull("load program by name: loaded program is null",p1);
            Assert.assertEquals("load program by name: loaded program differs from original",p,p1);

            // load the program by name and check if it is equal to the original program
            Program p2 = connection.loadProgram(p.getId());
            connection.commit();

            Assert.assertNotNull("load program by ID: loaded program is null",p2);
            Assert.assertEquals("load program by ID: loaded program differs from original",p,p2);

        } finally {
            connection.close();
        }
    }
View Full Code Here

    public void testUpdateProgram() throws Exception {
        KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream("test-001.kwrl"));
        Program p = parser.parseProgram();
        p.setName("test-001");

        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            // should not throw an exception and the program should have a database ID afterwards
            connection.storeProgram(p);
            connection.commit();

            Assert.assertNotNull("program did not get a database ID",p.getId());


            // load the program by name and check if it is equal to the original program
            Program p1 = connection.loadProgram("test-001");
            connection.commit();

            Assert.assertNotNull("load program by name: loaded program is null",p1);
            Assert.assertEquals("load program by name: loaded program differs from original",p,p1);


            PreparedStatement listRules1 = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_rules");
            ResultSet resultListRules1 = listRules1.executeQuery();

            Assert.assertTrue(resultListRules1.next());
            Assert.assertEquals(5, resultListRules1.getInt("count"));
            resultListRules1.close();
            connection.commit();



            // now remove two rules from the original and update the existing program
            p.getRules().remove(p.getRules().size()-1);
            p.getRules().remove(p.getRules().size()-1);
            p.addNamespace("myns","http://example.com/myns");

            connection.updateProgram(p);

            // load the program by name and check if it is equal to the original program
            Program p2 = connection.loadProgram(p.getName());
            connection.commit();

            Assert.assertNotNull("load program by name: loaded program is null",p2);
            Assert.assertEquals("load program by name: loaded program differs from original",p,p2);

            PreparedStatement listRules2 = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_rules");
            ResultSet resultListRules2 = listRules2.executeQuery();

            Assert.assertTrue(resultListRules2.next());
            Assert.assertEquals(3, resultListRules2.getInt("count"));
            resultListRules2.close();
            connection.commit();



        } finally {
            connection.close();
        }
    }
View Full Code Here

     *
     * @throws Exception
     */
    @Test
    public void testListDeletePrograms() throws Exception {
        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            List<Program> programs = new ArrayList<Program>();
            for(String name : new String[] {"test-001", "test-002", "test-003", "test-004"}) {
                KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream(name+".kwrl"));
                Program p = parser.parseProgram();
                p.setName(name);
                connection.storeProgram(p);
                connection.commit();

                programs.add(p);
            }

            // now we should have a collection of 4 programs in the database
            PreparedStatement checkNodeStmt = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_programs");
            ResultSet result = checkNodeStmt.executeQuery();

            Assert.assertTrue(result.next());
            Assert.assertEquals(4, result.getInt("count"));
            result.close();
            connection.commit();

            // list programs should return the same number and equal programs
            List<Program> dbPrograms1 = asList(connection.listPrograms());
            Assert.assertEquals(4, dbPrograms1.size());
            Assert.assertEquals("list of original programs differs from list of database",programs,dbPrograms1);

            // delete all programs and check if the database does not contain any remaining entries in the different tables
            for(Program p : programs) {
                connection.deleteProgram(p);
                connection.commit();
            }
            List<Program> dbPrograms2 = asList(connection.listPrograms());
            Assert.assertEquals(0, dbPrograms2.size());

            PreparedStatement listPrograms = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_programs");
            ResultSet resultListPrograms = listPrograms.executeQuery();

            Assert.assertTrue(resultListPrograms.next());
            Assert.assertEquals(0, resultListPrograms.getInt("count"));
            resultListPrograms.close();
            connection.commit();

            PreparedStatement listRules = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_rules");
            ResultSet resultListRules = listRules.executeQuery();

            Assert.assertTrue(resultListRules.next());
            Assert.assertEquals(0, resultListRules.getInt("count"));
            resultListRules.close();
            connection.commit();

            PreparedStatement listNamespaces = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_program_namespaces");
            ResultSet resultListNamespaces = listNamespaces.executeQuery();

            Assert.assertTrue(resultListNamespaces.next());
            Assert.assertEquals(0, resultListNamespaces.getInt("count"));
            resultListNamespaces.close();
            connection.commit();

            PreparedStatement listProgramRules = connection.getJDBCConnection().prepareStatement("SELECT count(*) AS count FROM reasoner_program_rules");
            ResultSet resultListProgramRules = listProgramRules.executeQuery();

            Assert.assertTrue(resultListProgramRules.next());
            Assert.assertEquals(0, resultListProgramRules.getInt("count"));
            resultListProgramRules.close();
            connection.commit();

        } finally {
            connection.close();
        }
    }
View Full Code Here

        URI subject = v.createURI(SUBJECT1);
        URI predicate = v.createURI(PREDICATE2);
        Literal object = v.createLiteral(OBJECT3);

        Pattern p = new Pattern(new ResourceField(subject), new ResourceField(predicate), new LiteralField(object));
        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            List<QueryResult> results = asList(connection.query(Collections.singleton(p), null, null, null, true));
            Assert.assertEquals(1, results.size());
            Assert.assertEquals(1,results.get(0).getJustifications().size());

            KiWiTriple justification = results.get(0).getJustifications().iterator().next();
            Assert.assertEquals(subject, justification.getSubject());
            Assert.assertEquals(predicate, justification.getPredicate());
            Assert.assertEquals(object, justification.getObject());

            connection.commit();
        } finally {
            connection.close();
        }
    }
View Full Code Here

    public void testSingleVariablePattern() throws Exception {
        ValueFactory v = repository.getValueFactory();
        URI predicate = v.createURI(PREDICATE2);

        Pattern p = new Pattern(new VariableField("X"), new ResourceField(predicate), new VariableField("Y"));
        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            List<QueryResult> results = asList(connection.query(Collections.singleton(p), null, null, null, true));
            Assert.assertEquals(3, results.size());

            for(int i=0; i<3; i++) {
                Assert.assertEquals(1,results.get(i).getJustifications().size());

                KiWiTriple justification = results.get(i).getJustifications().iterator().next();
                Assert.assertEquals(predicate, justification.getPredicate());
            }

            connection.commit();
        } finally {
            connection.close();
        }
    }
View Full Code Here

        VariableField y =  new VariableField("Y");
        VariableField z =  new VariableField("Z");

        Pattern p1 = new Pattern(x, new ResourceField(predicate1), y);
        Pattern p2 = new Pattern(y, new ResourceField(predicate1), z);
        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            List<QueryResult> results = asList(connection.query(ImmutableSet.of(p1,p2), null, null, null, true));
            Assert.assertEquals(1, results.size());
            Assert.assertEquals(2, results.get(0).getJustifications().size());


            Assert.assertEquals(SUBJECT1, results.get(0).getBindings().get(x).stringValue());
            Assert.assertEquals(SUBJECT2, results.get(0).getBindings().get(y).stringValue());
            Assert.assertEquals(OBJECT2, results.get(0).getBindings().get(z).stringValue());

            KiWiTriple justification1 = results.get(0).getJustifications().iterator().next();
            Assert.assertEquals(predicate1, justification1.getPredicate());

            connection.commit();
        } finally {
            connection.close();
        }


    }
View Full Code Here

        // ($1 ex:symmetric $2) -> ($2 ex:symmetric $1)
        KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream("simple.kwrl"));
        Program p = parser.parseProgram();
        p.setName("simple");

        KiWiReasoningConnection connection = rpersistence.getConnection();
        try {
            // should not throw an exception and the program should have a database ID afterwards
            connection.storeProgram(p);
            connection.commit();
        } finally {
            connection.close();
        }

        // instantiate reasoning engine, will load the programs into memory
        engine = new ReasoningEngine(rpersistence,tsail,new ReasoningConfiguration());
View Full Code Here

TOP

Related Classes of org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection

Copyright © 2018 www.massapicom. 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.