Tvorba informačních systémů 1/54 Tvorba informačních systémů Michal Krátký, Miroslav Beneš Katedra informatiky VŠB Technická univerzita Ostrava Tvorba informačních systémů, 2008/2009
Tvorba informačních systémů 2/54 Úvod Obsah Datová vrstva, Open DataBase Connectivity (ODBC), Java Database Connectivity (JDBC), ADO.NET, Bezpečnost, SQL Injection, Transakce, Využití objektově-relačního modelu.
Tvorba informačních systémů 3/54 Úvod Datová vrstva Oddělení aplikace od přístupu do databáze. Standardizovaný dotazovací jazyk SQL. API v programovacím jazyce ODBC, JDBC, ADO.NET.
Tvorba informačních systémů 4/54 Open DataBase Connectivity (ODBC) Open DataBase Connectivity (ODBC) Standard pro přístup do databáze, SQL Access group, 1992. Umožňuje dotazovat data z libovolné aplikace bez ohledu v jaké databázi jsou uložena. Vložena mezivrstva - ovladač, překládající uživatelské dotazy na dotazy databáze. Správce ovladačů Driver Manager. SŘBD podporující přístup přes ODBC: ODBC-compliant. Od verze 2.0 je podporováno SQL. Standardy: X/Open a ISO: SQL Call Level Interface (SQL/CLI).
Tvorba informačních systémů 5/54 Open DataBase Connectivity (ODBC) Architektura ODBC
Tvorba informačních systémů 6/54 Open DataBase Connectivity (ODBC) Typy ovladačů Ovladače založené na souborech přímý přístup k datům (ovladače = zdroj dat), analýza a interpretace dotazů, dbase. Ovladače založené na SŘBD dotazy se předávají ke zpracování SŘBD, transformace ODBC SQL na konkrétní dialekt SQL.
Tvorba informačních systémů 7/54 Open DataBase Connectivity (ODBC) ODBC, provedení dotazu 1 Připojení k datovému zdroji. 2 Inicializace. 3 Vytvoření a provedení dotazu. 4 Získání výsledku. 5 Ukončení transakce. 6 Odpojení od datového zdroje.
Tvorba informačních systémů 8/54 Open DataBase Connectivity (ODBC) Připojení k datovému zdroji / / Alokace zastupce pro spravce ovladacu ODBC SQLHENV henv ; SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, & henv ) ; / / Deklarace pozadovane verze ODBC SQLSetEnvAttr ( henv, SQL_ATTR_ODBC_VERSION, ( SQLPOINTER ) SQL_OV_ODBC3, 0 ) ; / / Alokace zastupce pro databazove p r i p o j e n i SQLHDBC hdbc1 ; SQLAllocHandle (SQL_HANDLE_DBC, henv, & hdbc ) ; / / P r i p o j e n i k databazi, zdroj, u z i v a t e l, heslo SQLConnect ( hdbc, database, SQL_NTS, user, SQL_NTS, password, SQL_NTS ) ;
Tvorba informačních systémů 9/54 Open DataBase Connectivity (ODBC) Inicializace aplikace / / Z i s k a n i i n f o r m a c i o SRBD, ovladaci,... SQLCHAR dbms_name [ 4 0 ] ; SQLGetInfo ( hdbc, SQL_DBMS_NAME, dbms_name, sizeof (dbms_name ), NULL ) ; / / Alokace zastupce pro p r i k a z ( dotaz ) SQLHANDLE hstmt ; SQLAllocHandle (SQL_HANDLE_STMT, hdbc, & hstmt ) ;
Tvorba informačních systémů 10/54 Open DataBase Connectivity (ODBC) Vytvoření a provedení dotazu Přímé provedení dotazu SQLCHARquery [ ] = "INSERT INTO... " ; SQLExecDirect ( hstmt, query, SQL_NTS ) ; Připravený dotaz SQLCHAR query = "SELECT jmeno FROM uziv WHERE l o g i n =? " ; SQLPrepare ( hstmt, query, SQL_NTS ) ; SQLBindParameter ( hstmt, 1,... ) ; SQLExecute ( hstmt ) ;
Tvorba informačních systémů 11/54 Open DataBase Connectivity (ODBC) Získání výsledku / / Svazani promennych s vysledkem dotazu SQLCHAR jmeno [ 3 0 ] ; SQLINTEGER jmeno_ind ; SQLBindCol ( hstmt, 1, SQL_C_CHAR, jmeno, sizeof ( jmeno ), & jmeno_ind ) ; / / Ct eni vysledku while ( SQLFetch ( hstmt )! = SQL_NO_DATA ) { i f ( jmeno_ind! = SQL_NULL_DATA ) cout < < jmeno ; }
Tvorba informačních systémů 12/54 Open DataBase Connectivity (ODBC) Odpojení od zdroje dat / / Uvolneni zastupce prikazu SQLFreeHandle (SQL_HANDLE_STMT, hstmt ) ; / / Odpojeni od databaze SQLDisconnect ( hdbc ) ; / / Uvolneni zastupce prikazu SQLFreeHandle (SQL_HANDLE_DBC, hdbc ) ; / / Uvolneni zastupce p r o s t r e d i SQLFreeHandle (SQL_HANDLE_ENV, henv ) ;
Tvorba informačních systémů 13/54 Java Database Connectivity (JDBC) Java Database Connectivity (JDBC) Rozhraní pro unifikovaný přístup k datům na platformě Java. Použití i mimo klient-server SŘBD data ve formě tabulek (CSV, XLS,...) Ovladače jsou k dispozici pro většinu databázových systémů. Inspirováno rozhraním ODBC objektové rozhraní, možnost spolupráce s ODBC.
Tvorba informačních systémů 14/54 Java Database Connectivity (JDBC) Architektura JDBC
Tvorba informačních systémů 15/54 Java Database Connectivity (JDBC) Architektura JDBC
Tvorba informačních systémů 16/54 Java Database Connectivity (JDBC) JDBC ovladač Zprostředkování komunikace aplikace s konkrétním typem databáze. Implementován obvykle výrobcem databáze. Dotazovací jazyk SQL, provedení dotazu: předá se databázi, ovladač vyhodnotí dotaz přímo. Reprezentován specifickou třidou: sun.jdbc.odbc.jdbcodbcdriver, com.mysql.jdbc.driver.
Tvorba informačních systémů 17/54 Java Database Connectivity (JDBC) Typy JDBC ovladačů Typ 1: využívá ODBC (přes JDBC-ODBC bridge), nevýhody: obtížně konfigurovatelné. Typ 2: komunikace s nativním ovladačem, nevýhody: režie komunikace navíc. Typ 3: komunikuje s centrálním serverem (Network Server) sít ovým protokolem, vlastnost: pro rozsáhlé heterogenní systémy. Typ 4: založen čistě na jazyce Java, vlastnost: komunikuje přímo s databázovým serverem.
Tvorba informačních systémů 18/54 Java Database Connectivity (JDBC) Registrace ovladače Máme dvě alternativy registrace ovladače: Konkretní ovladač je pevně spojen s aplikací new com.mysql.jdbc.driver() Výběr ovladače za běhu aplikace S t r i n g drivername = "com. mysql. jdbc. D r i v e r " ; t r y { Class. forname ( drivername ) ; } catch ( ClassNotFoundExceptione ) { / / obsluha vyjimky }
Tvorba informačních systémů 19/54 Java Database Connectivity (JDBC) Identifikace spojení/přihlašovací řetězec Identifikace spojení: jdbc:[drivertype]:[database] Příklad: jdbc:mysql://localhost/tis?user=tis &password=tis &useunicode=true &characterencoding=iso-8859-2
Tvorba informačních systémů 20/54 Java Database Connectivity (JDBC) Připojení k databázi Connection con = DriverManager.getConnection( url_string, user_string, password_string ); Postup: 1 DriverManager se dotáže všech registrovaných ovladačů. 2 Rozpozná-li ovladač své url, vrátí objekt Connection.
Tvorba informačních systémů 21/54 Java Database Connectivity (JDBC) Objekt Statement Reprezentuje SQL příkaz, třídy: Statement PreparedStatement CallableStatement Vytvoření instance: Statement stmt= con.createstatement(); Provedení příkazu: ResultSet rs= stmt.executequery( "SELECT * FROM t"); int num = stmt.executeupdate( "DELETE * FROM t");
Tvorba informačních systémů 22/54 Java Database Connectivity (JDBC) Objekt ResultSet Reprezentuje výsledek dotazu SELECT: Statement stmt = con. createstatement ( ) ; ResultSet r s = stmt. executequery ( "SELECT FROM u z i v a t e l " ) ; while ( rs. next ( ) ) { S t r i n g l o g i n = rs. g e t S t r i n g ( " l o g i n " ) ; S t r i n g jmeno = rs. g e t S t r i n g ( " jmeno " ) ; } rs. close ( ) ; stmt. close ( ) ;
Tvorba informačních systémů 23/54 Java Database Connectivity (JDBC) Ošetření chyb public class SQLException extends Exception t r y {... } catch ( SQLException e ) { while ( e! = null ) { System. out. p r i n t l n ( e. getmessage ( ) ) ; System. out. p r i n t l n ( e. getsqlstate ( ) ) ; System. out. p r i n t l n ( e. geterrorcode ( ) ) ; e = e. getnextexception ( ) ; } }
Tvorba informačních systémů 24/54 Java Database Connectivity (JDBC) Parametrizované příkazy Možnost předkompilace opakované provedení příkazu. Bezpečnější než konkatenace řetězců. Parametry označené znakem?, indexované od 1, INSERT INTO u z i v a t e l ( l o g i n, jmeno ) VALUES(?,? )
Tvorba informačních systémů 25/54 Java Database Connectivity (JDBC) Parametrizované příkazy PreparedStatement pstmt = con. preparestatement ( "INSERT... VALUES(?,? ) " ) ; pstmt. clearparameters ( ) ; pstmt. s e t S t r i n g ( 1, " wal007 " ) ; pstmt. s e t N u l l ( 2, Types.VARCHAR) ;
Tvorba informačních systémů 26/54 Java Database Connectivity (JDBC) Příklad, Oracle 1/2 DriverManager. r e g i s t e r D r i v e r (new OracleDriver ( ) ) ; S t r i n g connstr = " jdbc : oracle : t h i n : @infra. cs. vsb. cz " + " :1521: d456 " ; Connection connection = DriverManager. getconnection ( connstr, " xxx ", " yyy " ) ; Statement statement = connection. createstatement ( ) ; boolean r e s u l t f = statement. execute ( "UPDATE PROBE_SET VALUE=1 WHERE ID = 2 " ) ;
Tvorba informačních systémů 27/54 Java Database Connectivity (JDBC) Příklad, Oracle 2/2 ResultSet r e s u l t S e t = null ; i f ( r e s u l t f ) { r e s u l t S e t = statement. getresultset ( ) ; } i n t updatecount = statement. getupdatecount ( ) ; statement. close ( ) ; connection. close ( ) ;
Tvorba informačních systémů 28/54 Java Database Connectivity (JDBC) Příklad - JSTL < % / / D e f i n i c e datoveho z d r oje a dotazu %> < s q l : setdatasource d r i v e r = " org. g j t.mm. mysql. D r i v e r " u r l = " jdbc : mysql : / / win456. vsb. cz / t i s?useunicode= t r u e&" + " characterencoding=iso8859 2" user= " xxx " password= " xxx " / > <c : set var= " query " value= "SELECT FROM student " / > < s q l : query var= " students " s q l = " $ { query } " / > < % / / Prezentace vysledku %> <table border=1> < t r bgcolor= " #0099FF" > < td>login : < / td> < td><a href= " <c : out value= " $ { sortfnameurl } " / > " > F i r s t name< / a>< / td> < td><a href= " <c : out value= " $ { sortlnameurl } " / > " >Last name< / a>< / td> < / t r > <c : foreach var= " row " items= " $ { students. rows } " > < t r > < td><c : out value= " $ { row. l o g i n } " / >< / td>...
Tvorba informačních systémů 29/54 ADO.NET ADO.NET Přístup k datům na platformě.net. Možnost práce s XML daty. Propojení na další komponenty.net.
Tvorba informačních systémů 30/54 ADO.NET Architektura ADO.NET 1/2 Komponenty zaměřené na obsah: DataSet objekt v paměti obsahující data z datového zdroje, DataTable, DataRow, DataColumn reprezentace tabulky, záznamu a atributu. DataRelation vytvoření vazby mezi dvěma instancemi DataSet definováním vazebního atributu DataColumn.
Tvorba informačních systémů 31/54 ADO.NET Architektura ADO.NET 2/2 Komponenty pro správu Connection reprezentuje spojení k SŘBD, Command příkaz nad vytvořeným spojením, DataReader získání výsledku dotazu, DataAdapter objekt mezi DataSet a datovým zdrojem. Úkolem je naplnit DataSet a aktualizovat datový zdroj.
Tvorba informačních systémů 32/54 ADO.NET Architektura ADO.NET
Tvorba informačních systémů 33/54 ADO.NET Komponenty ADO.NET DataReader rychlý přístup k datům, pouze čtení směrem vpřed. DataSet obsahuje kopii dat z datového zdroje, čtení a zápis dat a schématu ve formátu XML. DataAdapter abstrakce přípojení a příkazu, naplnění a synchronizace dat pro DataSet.
Tvorba informačních systémů 34/54 ADO.NET Příklad, Oracle 1/3 using System. Data ; using System. Data. O r a c l e C l i e n t ;... OracleConnection connection = new OracleConnection ( " Data Source= i n f o ; User ID=xxx ; Password=yyy " ) ; connection. Open ( ) ; OracleCommand command = new OracleCommand ( " s e l e c t from student ", connection ) ; OracleDataAdapter dbadapter = new OracleDataAdapter ( command ) ; DataSet dataset = new DataSet ( " student " ) ;
Tvorba informačních systémů 35/54 ADO.NET Příklad, Oracle 2/3 t r y { dbadapter. F i l l ( dataset ) ; } catch ( Exception exc ) { throw new Exception ( exc. Message ) ; }....
Tvorba informačních systémů 36/54 ADO.NET Příklad, Oracle 3/3.... / / draw the r e s u l t System. Windows. Forms. DataGrid studentdatagrid= new System. Windows. Forms. DataGrid ( ) ; studentdatagrid. DataSource = dataset ; studentdatagrid. Update ( ) ;... connection. Close ( ) ;
Tvorba informačních systémů 37/54 ADO.NET ASP.NET, příklad AspNetExampleApp 1/3 CREATE TABLE PaperVersion ( i d INT NOT NULL PRIMARY KEY IDENTITY, date DATETIME NOT NULL, i d F i l e T y p e INT REFERENCES FileType ) ; CREATE INDEX PaperVersion_ id ON PaperVersion ( i d ) ; CREATE TABLE FileType ( i d INT NOT NULL PRIMARY KEY IDENTITY, extension VARCHAR( 5 ) NOT NULL, mime VARCHAR( 2 0 ) NOT NULL ) ; CREATE INDEX FileType_id ON FileType ( i d ) ; INSERT INTO FileType Values ( pdf, a p p l i c a t i o n / pdf ) ;... INSERT INTO PaperVersion Values ( 2/12/2008, 1 ) ;...
Tvorba informačních systémů 38/54 ADO.NET ASP.NET, příklad 2/3 Příklad: AspNetExampleApp, Default.aspx
Tvorba informačních systémů 39/54 ADO.NET ASP.NET, příklad 3/3 <%@ Page Language= "C# " AutoEventWireup= " t r u e " CodeBehind= " D e f a u l t. aspx. cs " I n h e r i t s = " AspNetExampleApp. _Default " %> <html xmlns= " h t t p : / / www. w3. org /1999/ xhtml " > <body><form i d = " form1 " runat=" server " > <asp : DropDownList ID= " DropDownList1 " runat = " server " DataSourceID= " SDS_FileType " DataTextField=" extension " DataValueField= " i d " > < / asp : DropDownList> <asp : SqlDataSource ID= " SDS_FileType " runat = " server " ConnectionString="<%$ ConnectionStrings : ConnectionString %>" SelectCommand= "SELECT FROM [ F i l e T y p e _ t e s t ] " > < / asp : SqlDataSource> < / form>< / body>< / html> Poznámka: Není vhodné, aby se ve třídě sloužící pro prezentaci (náležející k View) vyskytoval SQL příkaz. Proto v případě ASP.NET využíváme ObjectDataSource za kterým se pak skrývají naše třídy pro objektově-relační mapování.
Tvorba informačních systémů 40/54 Datová vrstva a bezpečnost Datová vrstva a bezpečnost - SQL Injection 1/2 Mějme dotaz: S t r i n g s t r = "SELECT uniqueid FROM user WHERE username = " + username + " AND password = " + password + " ; " ; Pokud username=" or 1=1 --", pak: SELECT uniqueid FROM user WHERE username= OR 1=1 AND password = heslo Získáme jedinečná čísla všech uživatelů. http://portal.gov.cz/ukaz.php?id=1 http://.../ukaz.php?id=1 %20OR%201=1%20--
Tvorba informačních systémů 41/54 Datová vrstva a bezpečnost Datová vrstva a bezpečnost - SQL Injection 2/2 Mějme dotaz: S t r i n g s t r = "SELECT uniqueid, email FROM uziv WHERE username = " + username " " ; Pokud username=" or a = a", pak: SELECT uniqueid, email FROM u z i v WHERE username = or a = a Získáme jedinečná čísla všech uživatelů.
Tvorba informačních systémů 42/54 Datová vrstva a bezpečnost Obrana proti SQL Injection Testování hodnot datových typů, např. nahrazení apostrofu dvěma apostrofy. Použít parametrizované SQL příkazy: S t r i n g strquery = "SELECT uniqueid FROM users WHERE username=@username AND password=@password " ; V případě JDBC pak použijeme pro vytvoření dotazu třídy SqlCommand a SqlParameter. Ve vhodných případech využíváme uložených procedur.
Tvorba informačních systémů 43/54 Datová vrstva a bezpečnost SQL Injection, Reference http://www.security-portal.cz/sql-injection.html http://packetstormsecurity.org/papers/bypass/ sql-injection.html http://en.wikipedia.org/wiki/sql_injection
Tvorba informačních systémů 44/54 Transakce v datové vrstvě Transakce, JDBC 1/2 Implicitně je v JDBC každý zaslaný příkaz proveden jako samostatná transakce. Pokud chceme více příkazu provést v jedné transakci musíme nastavit autocommit na false.
Tvorba informačních systémů 45/54 Transakce v datové vrstvě Transakce, JDBC 2/2 / / p r i k a z y jsou potvrzeny az zaslanim commit con. setautocommit ( false ) ; t r y { PreparedStatement updatesales = con. preparestatement ( "UPDATE COFFEES SET SALES =? WHERE COF_NAME LIKE? " ) ;... / / nastaveni parametru updatesales. executeupdate ( ) ; PreparedStatement updatetotal = con. preparestatement ( "UPDATE COFFEES SET TOTAL = TOTAL +? WHERE NAME LIKE? " ) ;... / / nastaveni parametru updatetotal. executeupdate ( ) ; con. commit ( ) ; / / p o t v r z e n i prikazu } catch ( SQLExceptione ) { con. r o l l b a c k ( ) ; } / / obnoveni stavu pred zacatkem transakce con. setautocommit ( true ) ;
Tvorba informačních systémů 46/54 Transakce v datové vrstvě Transakce, ADO.NET 1/2 t r y { Connect ( ) ; / / nastaveni mconnection SqlTransaction SqlTransaction = mconnection. BeginTransaction ( I s o l a t i o n L e v e l. S e r i a l i z a b l e ) ; } / / p r v n i : p r i k a z i n s e r t SqlCommand command = new SqlCommand ( null, mconnection, s q l T r a n s a c t i o n ) ; i n t insertno = command. ExecuteNonQuery ( ) ; / / druhy : p r i k a z update i n t updateno = command. ExecuteNonQuery ( ) ; msqltransaction. Commit ( ) ; mconnection. Close ( ) ;
Tvorba informačních systémů 47/54 Transakce v datové vrstvě Transakce, ADO.NET 2/2 catch ( Exception e ) { msqltransaction. Rollback ( ) ; mconnection. Close ( ) ; throw e ; }
Tvorba informačních systémů 48/54 Využití objektově-relačního modelu Využití objektově-relačního modelu 1/5 Definice typů v Oracle: CREATE OR REPLACE TYPE TAddress AS OBJECT ( s t r e e t VARCHAR2( 3 0 ), c i t y VARCHAR2( 3 0 ), PSC NUMBER( 5 ) ) ; CREATE OR REPLACE TYPE TPerson AS OBJECT ( l o g i n VARCHAR2( 6 ), fname VARCHAR2( 2 0 ), sname VARCHAR2( 2 0 ), address TAddress, b i r t h DATE, t e l 1 NUMBER( 2 0 ), t e l 2 NUMBER( 2 0 ), www VARCHAR2( 5 0 ), email VARCHAR2( 3 0 ), passwd VARCHAR2( 1 0) ) NOT FINAL NOT INSTANTIABLE ;
Tvorba informačních systémů 49/54 Využití objektově-relačního modelu Využití objektově-relačního modelu 2/5 CREATE OR REPLACE TYPE TGroup AS OBJECT ( name VARCHAR2( 4 ) ) ; / CREATE OR REPLACE TYPE TStudent UNDER TPerson ( grup REF TGroup, year NUMBER( 2 ), STATIC FUNCTION a u t h e n t i c a t i o n ( l o g i n VARCHAR2, ) ; / password VARCHAR2) RETURN REF TStudent
Tvorba informačních systémů 50/54 Využití objektově-relačního modelu Využití objektově-relačního modelu 3/5 Definice tabulek: CREATE TABLE Group_table OF TGroup (CONSTRAINT IO_GROUP_1 name UNIQUE NOT NULL ) ; CREATE TABLE Student_ table OF TStudent (CONSTRAINT IO_STUDENT_1 l o g i n UNIQUE NOT NULL, CONSTRAINT IO_STUDENT_2 fname NOT NULL, CONSTRAINT IO_STUDENT_3 sname NOT NULL, CONSTRAINT IO_STUDENT_4 address NOT NULL, CONSTRAINT IO_STUDENT_5 b i r t h NOT NULL, CONSTRAINT IO_STUDENT_6 passwd NOT NULL, CONSTRAINT IO_STUDENT_7 grup NOT NULL REFERENCES Group_table, CONSTRAINT IO_STUDENT_8 year NOT NULL ) ;
Tvorba informačních systémů 51/54 Využití objektově-relačního modelu Využití objektově-relačního modelu 4/5 Vložení záznamů z PL/SQL bloku: INSERT INTO Group_table VALUES( L101 ) ; INSERT INTO Group_table VALUES( L201 ) ; INSERT INTO Group_table VALUES( L305 ) ; DECLARE g REF TGroup ; BEGIN SELECT REF( gt ) INTO g FROM Group_table gt WHERE gt. name = L305 ; INSERT into Student_ table values ( sob015, Franta, Sobota, TAddress ( Stiborova 15, Ostrava,78300), 13.08.1988, 0987621845, 0987621845, NULL, cool. cool@centrum. cz, 11111, g, 3 ) ;... END; / commit ;
Tvorba informačních systémů 52/54 Využití objektově-relačního modelu Využití objektově-relačního modelu 5/5 Datové typy jsou do jisté míry pouze syntaktickou záležitostí, významná ovšem je možnost použít metody. CREATE OR REPLACE TYPE BODY TStudent AS STATIC FUNCTION a u t h e n t i c a t i o n ( l o g i n VARCHAR2, password VARCHAR2) RETURN REF TStudent IS studentref REF TStudent ; BEGIN SELECT REF( s ) into studentref from Student_ table s where ( s. l o g i n l i k e l o g i n ) and ( s. passwd l i k e password ) ; RETURN studentref ; END a u t h e n t i c a t i o n ; END; Volání metody z JDBC:... S t r i n g s t a t = "SELECT TStudent. a u t h e n t i c a t i o n (?,?) FROM" + " Student_table s " ; PreparedStatement pstmt = connection. preparestatement ( s t a t ) ;...
Tvorba informačních systémů 53/54 Využití objektově-relačního modelu Implementace přístupu k databázi Implementace objektového rozhraní k relační databázi, tzv: objektově-relační mapování: Pro každý záznam a tabulku vytvoříme třídu, např. Student a StudentTable. Viz příklady ze cvičení. Vhodné je automatické generování z SQL skriptu. Můžeme využít rámec, tvořící mezivrstvu mezi aplikací a JDBC/ADO.NET ovladačem: Hibernate, http://www.hibernate.org/
Tvorba informačních systémů 54/54 Využití objektově-relačního modelu Reference ODBC http://www.microsoft.com/data/odbc/ http://www.unixodbc.org/ JDBC http://java.sun.com/products/jdbc ADO.NET http://www.msdn.com/