Kategória: Java külső könyvtárak.
A más számítógépek számára hálózaton keresztül nyújtott szolgáltatások összefoglaló neve web szolgáltatás (angolul web service). Ebben a részben érintjük a JAX-WS-t, ami egy korai, már eltűnőben levő megvalósítás, a ma prosperáló JAX-RS-t, valamint kitérünk az e-mail küldésre is.
Javasolt oldalak a témával kapcsolatban.
- https://www.javatpoint.com/web-services-tutorial (sokat segített nekem a megértésben)
- https://www.w3schools.com/xml/xml_services.asp (kissé felületes)
- https://docs.oracle.com/javaee/5/tutorial/doc/bnayk.html (ebben még benne van a SAAJ), https://docs.oracle.com/javaee/5/tutorial/doc/bnayk.html (ebből kikerült a SAAJ, de már benne van a JAX-RS)
- https://www.guru99.com/web-services-tutorial.html (részletesen belemegy az egyes technológiákba)
JAX-WS
Egykor ez volt 'A' web szolgáltatás a Java-ban, ma már eltűnőben van, helyét átvette a JAX-RS. De mivel még mindig létezik, áttekintjük a lényeget. Előtte tekintsünk át néhány fontosabb, vonatkozó rövidítést:
- RPC: Remote Procedure Call (Távoli Eljáráshívás). Ahogy a neve is mutatja, arról van szó, hogy egy eljárást távolról hívunk meg.
- JAX-RPC: Java API for XML-based RPC (Java API XML-alapú RPC-hez). Ez egy kezdeti távoli metódus hívás Java-n belül. Túlhaladott, helyét átvette a JAX-WS.
- WSDL: Web Service Description Language (Web Szolgáltatás Leíró Nyelv). Ez egy (általában generált) XML dokumentum, ami leírja a web szolgáltatást: mi a szolgáltatás (többnyire függvény) neve, mik a paraméterek, mi a visszatérési érték stb.
- SOAP: Simple Object Access Protocol (Egyszerű Objektum Elérő Protokoll). Ez valójában az az XML formázott üzenet, amely a tulajdonképpen hívást tartalmazza. Tehát pl. ez tartalmazza a hívott szolgáltatás konkrét paramétereit. Az üzenet maga két részből áll, fejléc (header) és törzs (body), amit egy boríték (envelope) tart egyben. Maga a hívás többnyire HTTP-n keresztül történik (bár történhet más protokollon keresztül is); ennek az az előnye, hogy még szigorúan limitált hálózati beállítások mellett is ez általában nyitva van, elérhető.
- UDDI: Universal Description, Discovery, and Integration (Univerzális Leírás, Felfedezés és Integrálás). Ennek segítségével lehet lekérni a weben fellelhető web szolgáltatásokat, ill. lehet regisztrálni a miénket.
- SAAJ: SOAP with Attachments API for Java (SOAP Csatolmányokkal API Java-hoz). A kezdetben ezzel lehetett SOAP üzeneteket létrehozni. Helyét átvette a JAX-WS.
- JAX-WS: Java API for XML Web Services (Java API XML Web Szolgáltatásokhoz). Valójában tartalmazza a fent felsorolt összes technológiát. Technikailag a programozónak igazából néhány annotációt kell csak megadnia és per sor kódot megírnia, a többit elvégzi a keretrendszer. A fent említett JAX-RPC-t is tartalmazza, de van egy másik módszer is, a dokumentum alapú metódushívás. Programban ehhez egyetlen helyen kell megadni, hogy RCP vagy dokumentum legyen. Az egyes lehetőségek összehasonlításáról itt olvashatunk részletesen: https://www.ibm.com/developerworks/library/ws-whichwsdl/.
Lássunk egy példát! Ebben létrehozunk egy olyan web szolgáltatást, ami egy stringet vár paraméterül és egy stringet ad vissza, és mindössze annyit csinál, hogy elé írja azt, hogy hello. Nem kell hozzá külső könyvtár, a standard Java mindent tartalmaz. Először a szervert valósítjuk meg, majd a klienst. A szerver három részből áll: az interfészből, a megvalósításból és a publikáló kódból. Megfelelő forrásfájlba helyezve valósítsuk meg ezeket a következőképpen:
package webserviceexample;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
@WebService
@SOAPBinding(style = Style.RPC)
public interface JaxwsServer {
@WebMethod
String sayHello(String name);
}
package webserviceexample;
import javax.jws.WebService;
@WebService(endpointInterface = "webserviceexample.JaxwsServer")
public class JaxwsServerImpl implements JaxwsServer {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}
package webserviceexample;
import javax.xml.ws.Endpoint;
public class JaxwsPublisher {
public static void main(String[] args) {
Endpoint.publish("http://localhost:1977/hello", new JaxwsServerImpl());
System.out.println("Service started.");
}
}
Indítsuk el a fő programot, majd töltsük be a http://localhost:1977/hello?wsdl oldalt egy böngészővel. Ott láthatjuk a wsdl formázott web szolgáltatás leírást. Kísérletezzünk: állítsuk le a programot, a Style.RPC-t írjuk át erre: Style.DOCUMENT, majd indítsuk újra, és töltsük be ismét a fenti oldalt. Az RPC helyett a dokumentum stílusú leírást találjuk, ami kicsit más, nemcsak a formájában, hanem sokkal inkább a hívás mechanizmusában; a részletekbe itt ne megyünk bele.
Akár RPC, akár dokumentum stípusban publikáltuk a web szolgáltatást, a kliens hívó kódja ugyanaz:
package webserviceexample;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class JaxwsClient {
public static void main(String[] args) {
try {
URL url = new URL("http://localhost:1977/hello?wsdl");
QName qname = new QName("http://webserviceexample/", "JaxwsServerImplService");
Service service = Service.create(url, qname);
JaxwsServer hello = service.getPort(JaxwsServer.class);
String result = hello.sayHello("Csaba");
System.out.println(result);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
A QName két paraméterét a fenti URL-en elérhető WSDL-ből tudjuk kiolvasni:
- az első paraméter a targetNamespace,
- a második a name.
A példában az egyszerűség érdekében a kliens ugyanott van, ahol a szerver. A valóságban ez inkább a következőképpen néz(ett) ki:
- Van egy közös interfész, ami a példában a JaxwsServer osztályt tartalmazza.
- A szerver a másik két szerver osztályt tartalmazza, plusz hivatkozik az interfészre.
- A kliens a fenti kliens osztályt tartalmazza, és ez is hivatkozik az interfészre.
- A szerver és a kliens kód egymástól független, akár a világ két távoli pontján történhet a fejlesztés és a futtatás is.
JAX-RS
A mai webes szolgáltatások túlnyomó többsége ún. REST lekérdezésen alapul: HTTP kérések, ahol a válasz tipikusan JSON formázottan érkezik. A JAX-RS része a szabványnak, a megvalósításhoz viszont külső könyvtárakat használunk, melyek közül kettő terjedt el: a RESTEasy (a JBoss terméke) és a Jersey (a GlassFish terméke).
Ahhoz, hogy futtatni tudjuk, szükségünk van webszerverre. Ezt részletesen az Enterprise Java oldalon tárgyaljuk; most a példában egy beépített módon is használható web szervert, a Jetty-t fogjuk segítségül hívni. A JAX-RS megvalósítások közül csak a Jersey-t mutatom be, melynek két oka van: egyrészt a lényegi rész ugyanaz mindkettőnél, másrészt sajnos csak ezt sikerült működésre bírnom.
Lássuk először a pom.xml fájlt:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hu.faragocsaba</groupId>
<artifactId>jettyjerseyexample</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.12.RC2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.4.12.RC2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.4.12.RC2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.25</version>
</dependency>
</dependencies>
</project>
Látható, hogy a Jetty és a Jersey oldalról is több függőséget kell beimportálnunk.
A Jetty szervert a főprogramban valósítjuk meg:
package hu.faragocsaba.jetty;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;
public class JettyServer {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
context.setContextPath("/jettyjerseyexample");
server.setHandler(context);
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/rest/*");
servletHolder.setInitOrder(1);
servletHolder.setInitParameter("jersey.config.server.provider.packages", "hu.faragocsaba.rest");
server.start();
server.join();
}
}
A jersey.config.server.provider.packages beállításnál jelezzük, hogy a hu.faragocsaba.rest csomagban találjuk a megvalósítást.
package hu.faragocsaba.rest;
import javax.ws.rs.*;
@Path("/hello")
public class Hello {
@GET
@Path("/sayhello/{name}")
public String sayHello(@PathParam("name") String name) {
String result = "Hello " + name;
return result;
}
}
Igazából ez utóbbi a lényeg. Ha mindent jól csináltunk és elindítjuk a fő programot, akkor tetszőleges böngészővel megnyitva a http://localhost:8080/jettyjerseyexample/rest/hello/sayhello/Csaba oldalt kiírja azt, hogy Hello Csaba.
Megjegyzések:
- A példában HTTP GET lekérést látunk. Lehetőségünk van POST, PUT és DELETE műveletek végrehajtására is a megfelelő annotáció megadásával.
- A példában az paramétert az elérési útvonalban adjuk meg (@PathParam). További lehetőségek: @QueryParam (a GET kéréseknél a ? utáni, & jellel elválasztott paraméterek), @HeaderParam (HTTP fejléc információk), @CookieParam (süti paraméterek), @MatrixParam (pontosvesszővel elválasztott paraméterek), @FormParam (a HTTP POST esetében a formanyomtatvány paraméterei).
- A példában a paraméter és a visszatérés típusa is egyszerű szöveg volt. A megfelelő függvény elé, pl. a @GET után írt @Consumes és a @Produces annotációkkal megadhatjuk a típust. pl. @Produces(MediaType.APPLICATION_JSON). (Ennek akkor van jelentősége, ha a visszatérési értéke nem String, hanem egy objektum, és automatikusan átalakítja JSON formázott stringgé.)
- A visszatérési típus gyakran Response, ami gyakorlatilag egy megfelelő metaadatokkal ellátott HTTP válasz, HTTP kóddal, miegymással, kb. így: return Response.status(Response.Status.OK).entity(result).type(MediaType.APPLICATION_JSON).build();.
További források (melyek egy része nálam nem működött):
- JAX-RS
- https://hu.wikipedia.org/wiki/JAX-RS, https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services
- https://docs.oracle.com/javaee/6/tutorial/doc/giepu.html (hivatalos specifikáció)
- https://www.javatpoint.com/jax-rs-tutorials (jó áttekintés)
- https://www.mkyong.com/tutorials/jax-rs-tutorials/ (mindenféle kombinációra ad elvileg működő példát (ami lehet, hogy nem működik))
- https://www.baeldung.com/jax-rs-spec-and-implementations
- https://www.journaldev.com/9170/restful-web-services-tutorial-java
- Jersey
- https://jersey.github.io/
- https://www.vogella.com/tutorials/REST/article.html
- https://howtodoinjava.com/jersey-jax-rs-tutorials/
- https://crunchify.com/how-to-build-restful-service-with-java-using-jax-rs-and-jersey/
- https://www.mkyong.com/webservices/jax-rs/jersey-hello-world-example/
- https://www.javaguides.net/2018/06/jersey-rest-hello-world-example.html
- https://www.journaldev.com/498/jersey-java-tutorial
- RESTEasy
- https://resteasy.github.io/
- https://javainterviewpoint.com/resteasy-hello-world-example/
- https://howtodoinjava.com/restful-web-service/
- https://www.journaldev.com/9189/resteasy-tutorial-eclipse-tomcats
- http://zetcode.com/jaxrs/resteasytomcatcdi/
- https://www.baeldung.com/resteasy-tutorial
- http://www.mastertheboss.com/jboss-frameworks/resteasy/resteasy-tutorial
- http://zetcode.com/jaxrs/resteasycrud/s
- https://www.mkyong.com/webservices/jax-rs/resteasy-hello-world-example/
- Jetty
E-mailt a Javamail segítségével tudunk küldeni. Ez a Java EE részét képezi, a standard Java-ban pedig a következőképpen tudjuk használni:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hu.faragocsaba</groupId>
<artifactId>mailexample</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
</project>
Forráskód, ami elküld egy üzenetet a Gmail szerver segítségével:
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
public class GmailExample {
private static final String smtpServer = "smtp.gmail.com";
private static final String username = "[username]@gmail.com";
private static final String password = "[password]";
private static final String recipient = "[recipient]@gmail.com";
public static void main(String[] args) throws AddressException, MessagingException {
Properties properties = new Properties();
properties.put("mail.smtp.port", "587");
properties.put("mail.smtp.auth", "true");
properties.put("mail.smtp.starttls.enable", "true");
Session session = Session.getDefaultInstance(properties);
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(username));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
message.setSubject("test");
message.setText("Test e-mail.");
Transport transport = session.getTransport("smtp");
transport.connect(smtpServer, username, password);
transport.sendMessage(message, message.getAllRecipients());
System.out.println("Done");
}
}
Ahhoz, hogy használni tudjuk, a Gmail-ben be kell állítani azt, hogy a fiókhoz a kevésbé biztonságos szoftverek (mint a fenti is) hozzáférjenek: https://www.google.com/settings/security/lesssecureapps.
A további lehetőségekhez (pl. csatolmányok kezelése stb.) az alábbi leírásokban találunk információt:
- https://www.journaldev.com/2532/javamail-example-send-mail-in-java-smtp
- https://www.javatpoint.com/java-mail-api-tutorial
- https://www.mkyong.com/java/java-how-to-send-email/
- https://www.tutorialspoint.com/java/java_sending_email.htm
- https://www.baeldung.com/java-email
Megjegyzés: volt olyan számítógép, amelyről nem működött a fenti példa; valószínűleg központilag le volt tiltva a megfelelő port.