PA165/Cvičení Remoting: Porovnání verzí

Z FI WIKI
Přejít na: navigace, hledání
(REST server)
 
(Není zobrazeno 58 mezilehlých verzí od 2 dalších uživatelů.)
Řádka 1: Řádka 1:
=== JavaRMI ===
+
[[Kategorie:Java EE]]
 +
[[Kategorie:Java]]
 +
[[Kategorie:PA165]]
 +
 
 +
= JavaRMI =
  
 
Nejdřív si předvedeme JavaRMI.  
 
Nejdřív si předvedeme JavaRMI.  
Řádka 17: Řádka 21:
 
</java>
 
</java>
  
==== Klient ====
+
== Klient ==
  
 
Vytvořte si projekt, přidejte k němu JAR s rozhraním a vytvořte klienta:
 
Vytvořte si projekt, přidejte k němu JAR s rozhraním a vytvořte klienta:
Řádka 47: Řádka 51:
 
</java>
 
</java>
  
Klienta spusťte vůči serveru cvičícího.
+
Klienta spusťte vůči serveru cvičícího na '''nymfe31.fi.muni.cz'''.
  
==== Server ====
+
 
 +
(Pro ty, kdo neumí zkompilovat a spustit javovou třídu, tak z příkazového řádku se to udělá takto:
 +
  <nowiki>
 +
  mkdir rmicviceni
 +
  cd rmicviceni
 +
  wget http://acrab.ics.muni.cz/~makub/rmi/tabule.jar
 +
  mkdir -p cz/muni/fi/pa165/rmi
 +
  vi cz/muni/fi/pa165/rmi/Pisalek.java
 +
  javac -cp tabule.jar:. cz/muni/fi/pa165/rmi/Pisalek.java
 +
  java -cp tabule.jar:. cz.muni.fi.pa165.rmi.Pisalek nymfe31 'neco chytreho'
 +
  </nowiki>
 +
 
 +
nebo v NetBeans si vytvoříte nový projekt pomocí ''File - New Project - Categories: Java - Projects: Java Application'', v dialogu '''Create Main Class'' zadat ''cz.muni.fi.pa165.rmi.Pisalek'', pak v ''Libraries'' zvolit ''Add JAR/Folder'', přidat soubor tabule.jar, přepsat třídu Pisalek, na projektu pak zvolit ''Properties - Run - Arguments'' a do políčka napsat ''nymfe31 'neco chytreho' ''. Pak spustit projekt. Z příkazové řádky je to jednodušší, že.
 +
)
 +
 
 +
== Server ==
  
 
Utvořte dvojice se sousedem a vyzkoušejte komunikaci s jeho serverem.  
 
Utvořte dvojice se sousedem a vyzkoušejte komunikaci s jeho serverem.  
Řádka 84: Řádka 103:
 
}
 
}
 
</java>
 
</java>
která musí být spuštěna se správnými parametry:
+
která musí být zkompilována
 +
<nowiki>javac -cp tabule.jar:. cz/muni/fi/pa165/rmi/TabuleImpl.java</nowiki>
 +
a spuštěna se správnými parametry:
 
  <nowiki>java -Djava.rmi.server.codebase=http://acrab.ics.muni.cz/~makub/rmi/tabule.jar -cp tabule.jar:.  cz.muni.fi.pa165.rmi.TabuleImpl</nowiki>
 
  <nowiki>java -Djava.rmi.server.codebase=http://acrab.ics.muni.cz/~makub/rmi/tabule.jar -cp tabule.jar:.  cz.muni.fi.pa165.rmi.TabuleImpl</nowiki>
 +
 +
Pozor, v B130 v listopadu 2012 je nesmyslně zkonfigurovaný /etc/hosts, váže název stroje na lokální adresu, takže nelze kontaktovat jiné stroje přes RMI. Proto je potřeba použít následující:
 +
 +
<nowiki>
 +
MYADDR=`ifconfig | awk -F':' '/inet addr/&&!/127.0.0.1/{split($2,_," ");print _[1]}'`
 +
echo $MYADDR
 +
java -Djava.rmi.server.codebase=http://acrab.ics.muni.cz/~makub/rmi/tabule.jar \
 +
-Djava.rmi.server.hostname=$MYADDR -cp tabule.jar:.  cz.muni.fi.pa165.rmi.TabuleImpl
 +
</nowiki>
  
 
Metoda v rozhraní Tabule nepřijímá žádné třídy, které by JVM na straně serveru neznal. Pokud by tomu tak bylo, i klient by musel vystavit takové třídy někde na webu a musel by být spuštěn s parametrem <code>-Djava.rmi.server.codebase</code>, aby si server mohl třídy stáhnout.
 
Metoda v rozhraní Tabule nepřijímá žádné třídy, které by JVM na straně serveru neznal. Pokud by tomu tak bylo, i klient by musel vystavit takové třídy někde na webu a musel by být spuštěn s parametrem <code>-Djava.rmi.server.codebase</code>, aby si server mohl třídy stáhnout.
 +
 +
= JSON-RPC =
 +
 +
Ukážeme si vzdálené volání procedur (RPC) pomocí protokolu JSON-RPC 2.0. K implementaci použijeme knihovnu [http://code.google.com/p/jsonrpc4j/ jsonrpc4j].
 +
 +
 +
== Rozhraní služby ==
 +
 +
Stáhněte si Mavenový projekt [http://acrab.ics.muni.cz/~makub/rmi/tabule-json-rpc.zip tabule-json-rpc.zip],
 +
rozzipujte, a nainstalujte, bud pomocí '''mvn install''' nebo otevřením v NetBeans a zvolením '''Build'''.
 +
 +
Projekt obsahuje definici rozhraní a vyjímky:
 +
 +
'''Tabule.java'''
 +
<java>
 +
package cz.muni.fi.pa165.jsonrpc;
 +
 +
public interface Tabule {
 +
    void zverejni(String text) throws Vyjimka;
 +
}
 +
</java>
 +
'''Vyjimka.java'''
 +
<java>
 +
package cz.muni.fi.pa165.jsonrpc;
 +
 +
public class Vyjimka extends Exception {
 +
 +
    public Vyjimka(String message) {
 +
        super(message);
 +
    }
 +
 +
}
 +
</java>
 +
 +
== JSON-RPC server ==
 +
 +
Vytvořte si nový projekt typu [[Maven]] s názvem '''tabule-json-rpc-server''' a do pom.xml připište závislost na projektu tabule-json-rpc a knihovnách pro JSON-RPC:
 +
 +
<xml>
 +
    <name>tabule-json-rpc-server</name>
 +
    <properties>
 +
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 +
    </properties>
 +
   
 +
    <repositories>
 +
        <repository>
 +
            <id>jsonrpc4j-webdav-maven-repo</id>
 +
            <name>jsonrpc4j maven repository</name>
 +
            <url>http://jsonrpc4j.googlecode.com/svn/maven/repo/</url>
 +
            <layout>default</layout>
 +
        </repository>
 +
    </repositories>
 +
    <dependencies>
 +
        <dependency>
 +
            <groupId>cz.muni.fi.pa165</groupId>
 +
            <artifactId>tabule-json-rpc</artifactId>
 +
            <version>1.0</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>com.googlecode</groupId>
 +
            <artifactId>jsonrpc4j</artifactId>
 +
            <version>0.25</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>javax.portlet</groupId>
 +
            <artifactId>portlet-api</artifactId>
 +
            <version>2.0</version>
 +
            <scope>provided</scope>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>org.apache.tomcat</groupId>
 +
            <artifactId>tomcat-servlet-api</artifactId>
 +
            <version>7.0.27</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>org.simpleframework</groupId>
 +
            <artifactId>simple</artifactId>
 +
            <version>4.1.21</version>
 +
        </dependency>
 +
    </dependencies>
 +
</xml>
 +
 +
Vytvořte následující třídy v balíku '''cz.muni.fi.pa165.jsonrpc''':
 +
 +
'''TabuleImpl.java'''
 +
<java>
 +
package cz.muni.fi.pa165.jsonrpc;
 +
 +
public class TabuleImpl implements Tabule {
 +
 +
    @Override
 +
    public void zverejni(String text) throws Vyjimka {
 +
        if (text == null || text.isEmpty()) throw new Vyjimka("text je prázdný");
 +
        System.out.println(SimpleServer.getClient()+": " + text);
 +
    }
 +
}
 +
</java>
 +
'''SimpleServer.java'''
 +
<java>
 +
package cz.muni.fi.pa165.jsonrpc;
 +
 +
import com.googlecode.jsonrpc4j.JsonRpcServer;
 +
import java.io.IOException;
 +
import java.net.InetSocketAddress;
 +
import java.util.logging.*;
 +
import org.simpleframework.http.Request;
 +
import org.simpleframework.http.Response;
 +
import org.simpleframework.http.core.Container;
 +
import org.simpleframework.transport.connect.Connection;
 +
import org.simpleframework.transport.connect.SocketConnection;
 +
 +
public class SimpleServer implements Container {
 +
 +
    private JsonRpcServer jsonRpcServer;
 +
   
 +
    public SimpleServer(JsonRpcServer jsonRpcServer) {
 +
        this.jsonRpcServer = jsonRpcServer;
 +
    }
 +
 +
    private static ThreadLocal<String> client = new ThreadLocal<String>();
 +
   
 +
    static String getClient() {
 +
        return client.get();
 +
    }
 +
 +
    @Override
 +
    public void handle(Request request, Response response) {
 +
        try {
 +
            if ("POST".equals(request.getMethod())) {
 +
                response.set("Content-Type", "application/json");
 +
                client.set(request.getClientAddress().getHostName());
 +
                jsonRpcServer.handle(request.getInputStream(), response.getOutputStream());
 +
            }
 +
        } catch (IOException e) {
 +
            throw new RuntimeException(e);
 +
        }
 +
    }
 +
 +
    public static void main(String[] args) throws IOException {
 +
        //enable logging
 +
        Logger log = LogManager.getLogManager().getLogger("");
 +
        log.setLevel(Level.ALL);
 +
        for (Handler h : log.getHandlers()) h.setLevel(Level.FINEST);
 +
        //start server
 +
        Logger.getLogger(SimpleServer.class.getName()).info("starting server");
 +
        JsonRpcServer tabule = new JsonRpcServer(new TabuleImpl(), Tabule.class);
 +
        Connection connection = new SocketConnection(new SimpleServer(tabule));
 +
        connection.connect(new InetSocketAddress(1420));
 +
    }
 +
 +
}
 +
</java>
 +
 +
Spusťte server.
 +
 +
== JSON-RPC klient ==
 +
 +
Vytvořte nový projekt typu Maven jménem '''tabule-json-rpc-klient''' a do pom.xml připište:
 +
<xml>
 +
    <repositories>
 +
        <repository>
 +
            <id>jsonrpc4j-webdav-maven-repo</id>
 +
            <name>jsonrpc4j maven repository</name>
 +
            <url>http://jsonrpc4j.googlecode.com/svn/maven/repo/</url>
 +
            <layout>default</layout>
 +
        </repository>
 +
    </repositories>
 +
    <dependencies>
 +
        <dependency>
 +
            <groupId>cz.muni.fi.pa165</groupId>
 +
            <artifactId>tabule-json-rpc</artifactId>
 +
            <version>1.0</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>com.googlecode</groupId>
 +
            <artifactId>jsonrpc4j</artifactId>
 +
            <version>0.25</version>
 +
        </dependency>
 +
    </dependencies>
 +
</xml>
 +
 +
Vytvořte třídu
 +
 +
'''SimpleClient.java'''
 +
<java>
 +
package cz.muni.fi.pa165.jsonrpc;
 +
 +
import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
 +
import com.googlecode.jsonrpc4j.ProxyUtil;
 +
 +
import java.net.MalformedURLException;
 +
import java.net.URL;
 +
 +
public class SimpleClient {
 +
 +
    public static void main(String[] args) throws MalformedURLException, Vyjimka {
 +
 +
        JsonRpcHttpClient client = new JsonRpcHttpClient(new URL("http://localhost:1420/"));
 +
        Tabule tabule = ProxyUtil.createClientProxy(SimpleClient.class.getClassLoader(), Tabule.class, client);
 +
 +
        tabule.zverejni("ahoj");
 +
        //tabule.zverejni(null);
 +
    }
 +
}
 +
</java>
 +
 +
Spusťte klienta. Pak zkuste odkomentovat řádek s '''tabule.zverejni(null);''' a spusťte ho znovu.
 +
 +
== Samostatný úkol - změna rozhraní ==
 +
 +
V původním projektu tabule-json-rpc přidejte do rozhraní Tabule nějakou další metodu, dejte '''Build'''.
 +
 +
V projektu tabule-json-rpc-server implementujte metodu ve třídě TabuleImpl a znovu spusťte server.
 +
 +
V projektu tabule-json-rpc-klient zavolejte novou metodu.
 +
 +
Pozorujte, jak vypadají JSON-RPC zprávy v logu serveru.
 +
 +
= SOAP/WSDL Web Services =
 +
 +
Ukážeme si web services pomocí [http://cxf.apache.org/ Apache CXF].
 +
 +
 +
== SOAP server ==
 +
Vytvořte si nový projekt typu Maven zvaný třeba '''soap-cxf-server'''. Do pom.xml připište:
 +
<xml>
 +
  <properties>
 +
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 +
        <cxf.version>2.7.0</cxf.version>
 +
    </properties>
 +
 +
    <dependencies>
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
 +
            <version>${cxf.version}</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-rt-transports-http</artifactId>
 +
            <version>${cxf.version}</version>
 +
        </dependency>
 +
        <!-- Jetty is needed if you're are not using the CXFServlet -->
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
 +
            <version>${cxf.version}</version>
 +
        </dependency>
 +
        <!-- SLF4J binding -->
 +
        <dependency>
 +
            <groupId>org.slf4j</groupId>
 +
            <artifactId>slf4j-jdk14</artifactId>
 +
            <version>1.7.2</version>
 +
        </dependency>
 +
    </dependencies>
 +
</xml>
 +
 +
Vytvořte package '''cz.muni.fi.pa165.soap''' a v něm tyto třídy:
 +
<java>
 +
package cz.muni.fi.pa165.soap;
 +
 +
import javax.xml.bind.annotation.XmlAccessType;
 +
import javax.xml.bind.annotation.XmlAccessorType;
 +
import javax.xml.ws.WebFault;
 +
 +
@WebFault(name = "Vyjimka")
 +
@XmlAccessorType(XmlAccessType.FIELD)
 +
public class Vyjimka extends RuntimeException {
 +
    String duvod;
 +
 +
    public Vyjimka(String message,String duvod) {
 +
        super(message);
 +
        this.duvod = duvod;
 +
    }
 +
}
 +
</java>
 +
 +
<java>
 +
package cz.muni.fi.pa165.soap;
 +
 +
import javax.jws.WebService;
 +
import javax.jws.WebParam;
 +
 +
@WebService
 +
public interface Tabule {
 +
   
 +
    void zverejni(@WebParam(name = "text")String text) throws Vyjimka;
 +
 +
}
 +
</java>
 +
 +
<java>
 +
package cz.muni.fi.pa165.soap;
 +
 +
import javax.annotation.Resource;
 +
import javax.jws.WebService;
 +
import javax.servlet.http.HttpServletRequest;
 +
import javax.xml.ws.WebServiceContext;
 +
import javax.xml.ws.handler.MessageContext;
 +
 +
@WebService(endpointInterface = "cz.muni.fi.pa165.soap.Tabule", serviceName = "Tabule")
 +
public class TabuleImpl implements Tabule {
 +
 +
    @Resource
 +
    WebServiceContext wsCtx;
 +
 +
    public void zverejni(String text) throws Vyjimka {
 +
        if (text == null || text.isEmpty()) throw new Vyjimka("empty text", "retezec prazdny");
 +
 +
        System.out.println(getClient() + ":" + text);
 +
    }
 +
 +
    private String getClient() {
 +
        MessageContext msgCtx = wsCtx.getMessageContext();
 +
        HttpServletRequest req = (HttpServletRequest) msgCtx.get(MessageContext.SERVLET_REQUEST);
 +
        return req.getRemoteHost();
 +
    }
 +
 +
}
 +
</java>
 +
 +
<java>
 +
package cz.muni.fi.pa165.soap;
 +
 +
import javax.xml.ws.Endpoint;
 +
 +
public class Server {
 +
    public static void main(String[] args) throws InterruptedException {
 +
        String address = "http://localhost:9000/tabule";
 +
        Endpoint.publish(address, new TabuleImpl());
 +
        System.out.println("Server ready...");
 +
    }
 +
}
 +
</java>
 +
 +
Spusťte třídu '''cz.muni.fi.pa165.soap.Server''' a navštivte URL [http://localhost:9000/tabule?wsdl http://localhost:9000/tabule?wsdl].
 +
 +
== SOAP klient ==
 +
 +
Aniž uzavřete předchozí projekt, vytvořte nový projekt zvaný třeba '''soap-cxf-klient'''. Děláme to tak proto, abychom viděli,
 +
že jediným pojítkem mezi serverem a klientem je WSDL popis služby.
 +
 +
Do pom.xml připište
 +
<xml>
 +
    <properties>
 +
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 +
        <cxf.version>2.7.0</cxf.version>
 +
    </properties>
 +
 +
    <dependencies>
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
 +
            <version>${cxf.version}</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-rt-transports-http</artifactId>
 +
            <version>${cxf.version}</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
 +
            <version>${cxf.version}</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>org.apache.cxf</groupId>
 +
            <artifactId>cxf-common-utilities</artifactId>
 +
            <version>2.5.6</version>
 +
        </dependency>
 +
    </dependencies>
 +
 +
    <build>
 +
        <plugins>
 +
            <plugin>
 +
                <groupId>org.apache.cxf</groupId>
 +
                <artifactId>cxf-codegen-plugin</artifactId>
 +
                <version>${cxf.version}</version>
 +
                <executions>
 +
                    <execution>
 +
                        <id>generate-sources</id>
 +
                        <configuration>
 +
                            <wsdlOptions>
 +
                                <wsdlOption>
 +
                                    <wsdl>http://localhost:9000/tabule?wsdl</wsdl>
 +
                                </wsdlOption>
 +
                            </wsdlOptions>
 +
                        </configuration>
 +
                        <goals>
 +
                            <goal>wsdl2java</goal>
 +
                        </goals>
 +
                    </execution>
 +
                </executions>
 +
            </plugin>
 +
            <!-- nastaveni verze zdrojaku -->
 +
            <plugin>
 +
                <groupId>org.apache.maven.plugins</groupId>
 +
                <artifactId>maven-compiler-plugin</artifactId>
 +
                <version>2.5.1</version>
 +
                <configuration>
 +
                    <source>6</source>
 +
                    <target>6</target>
 +
                </configuration>
 +
            </plugin>
 +
            <!-- zavislosti na JARech -->
 +
            <plugin>
 +
                <groupId>org.apache.maven.plugins</groupId>
 +
                <artifactId>maven-dependency-plugin</artifactId>
 +
                <configuration>
 +
                    <outputDirectory>
 +
                        ${project.build.directory}
 +
                    </outputDirectory>
 +
                </configuration>
 +
            </plugin>
 +
        </plugins>
 +
    </build>
 +
</xml>
 +
 +
Přidejte třídu:
 +
<java>
 +
package cz.muni.fi.pa165.soap;
 +
 +
public class Klient {
 +
    public static void main(String[] args) {
 +
        Tabule tabule = new Tabule_Service().getTabuleImplPort();
 +
        try {
 +
           
 +
            tabule.zverejni("ahoj");
 +
            System.out.println("zaslano");
 +
           
 +
            //tabule.zverejni("");
 +
 +
        } catch (Vyjimka_Exception e) {
 +
            e.printStackTrace();
 +
            System.out.println(e.getFaultInfo().getDuvod());
 +
        }
 +
    }
 +
}
 +
</java>
 +
 +
V NetBeans dejte '''Build'''. Alternativně
 +
můžete z příkazového řádku spustit '''mvn generate-sources''' a '''mvn package'''.
 +
 +
Spusťte třídu Klient.
 +
 +
Pro kopii všech knihoven potřebných pro spuštění z příkazového řádku zadejte příkaz '''mvn dependency:copy-dependencies'''.
 +
 +
URL na službu se bere z WSDL popisu služby. Pokud ho potřebujete změnit, lze to udělat takto:
 +
<java>
 +
((BindingProvider) tabule).getRequestContext()
 +
                .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://nymfe31.fi.muni.cz:9000/tabule");
 +
</java>
 +
 +
= REST =
 +
 +
== REST server ==
 +
 +
Vytvořte si nový projekt typu Maven - Web Application, jménem třeba rest-tabule-server.
 +
 +
Do pom.xml připište:
 +
 +
<xml>
 +
<packaging>war</packaging>
 +
    <properties>
 +
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 +
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 +
    </properties>
 +
    <dependencies>
 +
        <!-- Jackson JSON processor -->
 +
        <dependency>
 +
            <groupId>com.fasterxml.jackson.core</groupId>
 +
            <artifactId>jackson-databind</artifactId>
 +
            <version>2.1.1</version>
 +
        </dependency>
 +
        <!-- servlet API 3.0 -->
 +
        <dependency>
 +
            <groupId>javax</groupId>
 +
            <artifactId>javaee-web-api</artifactId>
 +
            <version>6.0</version>
 +
            <scope>provided</scope>
 +
        </dependency>
 +
        <!-- JSTL , just for escaping HTML -->
 +
        <dependency>
 +
            <groupId>taglibs</groupId>
 +
            <artifactId>standard</artifactId>
 +
            <version>1.1.2</version>
 +
        </dependency>
 +
    </dependencies>
 +
 +
    <build>
 +
        <plugins>
 +
            <!-- Java language version -->
 +
            <plugin>
 +
                <groupId>org.apache.maven.plugins</groupId>
 +
                <artifactId>maven-compiler-plugin</artifactId>
 +
                <configuration>
 +
                    <source>6</source>
 +
                    <target>6</target>
 +
                </configuration>
 +
            </plugin>
 +
            <!-- Servlet 3.0 without web.xml -->
 +
            <plugin>
 +
                <groupId>org.apache.maven.plugins</groupId>
 +
                <artifactId>maven-war-plugin</artifactId>
 +
                <version>2.1.1</version>
 +
                <configuration>
 +
                    <failOnMissingWebXml>false</failOnMissingWebXml>
 +
                </configuration>
 +
            </plugin>
 +
        </plugins>
 +
    </build>
 +
</xml>
 +
 +
Vytvořte si nový servlet
 +
<java>
 +
package cz.muni.fi.pa165.mavenproject2;
 +
 +
import com.fasterxml.jackson.databind.JsonNode;
 +
import com.fasterxml.jackson.databind.ObjectMapper;
 +
import org.apache.taglibs.standard.functions.Functions;
 +
 +
import javax.servlet.ServletException;
 +
import javax.servlet.annotation.WebServlet;
 +
import javax.servlet.http.HttpServlet;
 +
import javax.servlet.http.HttpServletRequest;
 +
import javax.servlet.http.HttpServletResponse;
 +
import java.io.IOException;
 +
import java.io.PrintWriter;
 +
import java.text.SimpleDateFormat;
 +
import java.util.*;
 +
 +
@WebServlet(urlPatterns = "/tabule/*")
 +
public class TabuleServlet extends HttpServlet {
 +
 +
    final List<Map<String, String>> zaznamy = Collections.synchronizedList(new ArrayList<Map<String, String>>());
 +
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 +
 +
    @Override
 +
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 +
        String pathInfo = request.getPathInfo();
 +
        if (pathInfo != null && pathInfo.matches("/\\d+")) {
 +
            response.setContentType("application/json");
 +
            ObjectMapper mapper = new ObjectMapper();
 +
            mapper.writeValue(response.getOutputStream(), zaznamy.get(Integer.parseInt(pathInfo.substring(1))));
 +
        } else {
 +
            if ("html".equals(request.getParameter("type"))) {
 +
                response.setContentType("text/html;charset=utf-8");
 +
                PrintWriter out = response.getWriter();
 +
                response.setHeader("Refresh", "5");
 +
                out.println("<h1>Tabule na " + request.getContextPath() + "/tabule</h2>");
 +
                out.println("<table border=\"1\">");
 +
                synchronized (zaznamy) {
 +
                    for (int i = zaznamy.size(); --i >= 0;) {
 +
                        Map<String, String> m = zaznamy.get(i);
 +
                        out.println("<tr><td><a href=\""+request.getContextPath()+"/tabule/"+i+"\">" + i + "</a>"
 +
                                + "<td>" + m.get("kdy")
 +
                                + "<td>" + m.get("kdo") + "<td>" + Functions.escapeXml(m.get("co")));
 +
                    }
 +
                }
 +
                out.println("</table>");
 +
            } else {
 +
                response.setContentType("application/json");
 +
                ObjectMapper mapper = new ObjectMapper();
 +
                mapper.writeValue(response.getOutputStream(), zaznamy);
 +
            }
 +
        }
 +
    }
 +
 +
    @Override
 +
    protected synchronized void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 +
        request.setCharacterEncoding("utf-8");
 +
        String zverejni = request.getParameter("zverejni");
 +
        if (zverejni != null) {
 +
            Map<String, String> m = new LinkedHashMap<String, String>();
 +
            m.put("kdy", sdf.format(new Date()));
 +
            m.put("kdo", request.getRemoteHost());
 +
            m.put("co", zverejni);
 +
            zaznamy.add(m);
 +
            response.sendRedirect(request.getContextPath() + "/tabule/" + (zaznamy.size() - 1));
 +
        } else if (request.getContentType().startsWith("application/json")) {
 +
            ObjectMapper mapper = new ObjectMapper();
 +
            JsonNode jsonNode = mapper.readValue(request.getInputStream(), JsonNode.class);
 +
            JsonNode jsonZverejni = jsonNode.get("zverejni");
 +
            if (!jsonNode.isMissingNode()) {
 +
                Map<String, String> m = new LinkedHashMap<String, String>();
 +
                m.put("kdy", sdf.format(new Date()));
 +
                m.put("kdo", request.getRemoteHost());
 +
                m.put("co", jsonZverejni.textValue());
 +
                zaznamy.add(m);
 +
                response.sendRedirect(request.getContextPath() + "/tabule/" + (zaznamy.size() - 1));
 +
            } else {
 +
                System.err.println("Missing key");
 +
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing required data key.");
 +
            }
 +
        } else {
 +
            System.err.println("Unknown request type " + request.getContentType());
 +
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown request type.");
 +
        }
 +
    }
 +
}
 +
</java>
 +
 +
Po spuštění by na URL http://localhost:8080/tabule/?type=html měla být viditelná HTMl verze tabule, zatím prázdná. Na http://localhost:8080/tabule/ pak JSON data, zatím také prázdná.
 +
 +
== REST klient ==
 +
 +
Vytvořte si nový projekt typu Maven - Java Application, jménem třeba rest-tabule-klient.
 +
 +
Do pom.xml připište
 +
 +
<xml>
 +
<properties>
 +
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 +
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 +
    </properties>
 +
 +
    <build>
 +
        <plugins>
 +
            <!-- Java language version -->
 +
            <plugin>
 +
                <groupId>org.apache.maven.plugins</groupId>
 +
                <artifactId>maven-compiler-plugin</artifactId>
 +
                <configuration>
 +
                    <source>6</source>
 +
                    <target>6</target>
 +
                </configuration>
 +
            </plugin>
 +
        </plugins>
 +
    </build>
 +
 +
    <dependencies>
 +
        <dependency>
 +
            <groupId>org.springframework</groupId>
 +
            <artifactId>spring-web</artifactId>
 +
            <version>3.1.3.RELEASE</version>
 +
        </dependency>
 +
        <dependency>
 +
            <groupId>com.fasterxml.jackson.core</groupId>
 +
            <artifactId>jackson-databind</artifactId>
 +
            <version>2.1.1</version>
 +
        </dependency>
 +
    </dependencies>
 +
</xml>
 +
 +
Vyvtořte si REST klienta, který přidá záznam a získá všechny záznamy:
 +
 +
<java>
 +
package cz.muni.fi.pa165.rest.klient;
 +
 +
import com.fasterxml.jackson.databind.JsonNode;
 +
import org.springframework.web.client.RestTemplate;
 +
 +
import java.net.URI;
 +
import java.util.HashMap;
 +
import java.util.Map;
 +
 +
public class TabuleRestKlient {
 +
   
 +
    static String STROJ = "localhost";
 +
    static int PORT = 8080;
 +
    static String webapp = "rest-tabule-server";
 +
   
 +
    public static void main(String[] args) {
 +
        RestTemplate rt = new RestTemplate();
 +
 +
        String url = "http://"+STROJ+":"+PORT+"/"+webapp+"/tabule";
 +
       
 +
        //POST pro přidání
 +
        URI uri = rt.postForLocation(url+"?zverejni={text}", null, "příliš žluťoučký kůň");
 +
        System.out.println("uri nového záznamu = " + uri);
 +
 +
        //GET pro získání všech záznamů
 +
        JsonNode jsonNode = rt.getForObject(url, JsonNode.class);
 +
 +
        for(JsonNode m : jsonNode) {
 +
            System.out.println("záznam = " + m);
 +
        }
 +
    }
 +
}
 +
</java>
 +
 +
Používáme [http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/remoting.html#rest-resttemplate Spring 3.x RestTemplate] ([http://static.springsource.org/spring/docs/current/javadoc-api/index.html?org/springframework/web/client/RestTemplate.html javadoc pro RestTemplate]).
 +
 +
Po spuštění na výše uvedeném URL můžete pozorovat přibývající záznamy.
 +
 +
Všimněte si, že kódování češtiny moc nefunguje, zkusíme to opravit.
 +
 +
Změníme kód klienta tak, aby místo URL parametru používal tělo POST requestu ve formátu JSON:
 +
 +
<java>
 +
        //POST pro přidání
 +
        Map<String,String> map = new HashMap<String, String>();
 +
        map.put("zverejni","příliš žlutoučká kráva");
 +
        URI uri = rt.postForLocation(url, map);
 +
 +
</java>

Aktuální verze z 4. 12. 2012, 18:10


JavaRMI

Nejdřív si předvedeme JavaRMI.

Na tabule.jar je JAR soubor s následující definicí:

 
package cz.muni.fi.pa165.rmi;
 
import java.rmi.Remote;
import java.rmi.RemoteException;
 
public interface Tabule extends Remote {
 
    void zverejni(String text) throws RemoteException;
    
}

Klient

Vytvořte si projekt, přidejte k němu JAR s rozhraním a vytvořte klienta:

 
package cz.muni.fi.pa165.rmi;
 
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.NotBoundException;
 
public class Pisalek {
 
    public static void main(String[] args) throws RemoteException, NotBoundException {
        if(args.length!=2) {
            System.out.println("Usage: java "+Pisalek.class.getName()+" host text");
            System.exit(1);
        }
        String host = args[0];
        String text = args[1];
 
        Registry remregistry = LocateRegistry.getRegistry(host);
        Tabule tabule = (Tabule) remregistry.lookup("tabule");
 
        tabule.zverejni(text);
    }
}

Klienta spusťte vůči serveru cvičícího na nymfe31.fi.muni.cz.


(Pro ty, kdo neumí zkompilovat a spustit javovou třídu, tak z příkazového řádku se to udělá takto:

 
  mkdir rmicviceni
  cd rmicviceni
  wget http://acrab.ics.muni.cz/~makub/rmi/tabule.jar
  mkdir -p cz/muni/fi/pa165/rmi
  vi cz/muni/fi/pa165/rmi/Pisalek.java
  javac -cp tabule.jar:. cz/muni/fi/pa165/rmi/Pisalek.java
  java -cp tabule.jar:. cz.muni.fi.pa165.rmi.Pisalek nymfe31 'neco chytreho'
  

nebo v NetBeans si vytvoříte nový projekt pomocí File - New Project - Categories: Java - Projects: Java Application, v dialogu 'Create Main Class zadat cz.muni.fi.pa165.rmi.Pisalek, pak v Libraries zvolit Add JAR/Folder, přidat soubor tabule.jar, přepsat třídu Pisalek, na projektu pak zvolit Properties - Run - Arguments a do políčka napsat nymfe31 'neco chytreho' . Pak spustit projekt. Z příkazové řádky je to jednodušší, že. )

Server

Utvořte dvojice se sousedem a vyzkoušejte komunikaci s jeho serverem.

Na straně serveru je třeba spustit na pozadí program

rmiregistry &

mít implementaci serveru:

 
package cz.muni.fi.pa165.rmi;
 
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UnicastRemoteObject;
 
public class TabuleImpl implements Tabule {
 
    synchronized public void zverejni(String text) throws RemoteException {
        try {
            System.out.println(UnicastRemoteObject.getClientHost() + ": " + text);
        } catch (ServerNotActiveException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) throws RemoteException {
        System.out.println("startuji Tabuli");
        Tabule stub = (Tabule) UnicastRemoteObject.exportObject(new TabuleImpl(), 0);
        LocateRegistry.getRegistry().rebind("tabule", stub);
    }
 
}

která musí být zkompilována

javac -cp tabule.jar:. cz/muni/fi/pa165/rmi/TabuleImpl.java

a spuštěna se správnými parametry:

java -Djava.rmi.server.codebase=http://acrab.ics.muni.cz/~makub/rmi/tabule.jar -cp tabule.jar:.  cz.muni.fi.pa165.rmi.TabuleImpl

Pozor, v B130 v listopadu 2012 je nesmyslně zkonfigurovaný /etc/hosts, váže název stroje na lokální adresu, takže nelze kontaktovat jiné stroje přes RMI. Proto je potřeba použít následující:

MYADDR=`ifconfig | awk -F':' '/inet addr/&&!/127.0.0.1/{split($2,_," ");print _[1]}'`
echo $MYADDR
java -Djava.rmi.server.codebase=http://acrab.ics.muni.cz/~makub/rmi/tabule.jar \
 -Djava.rmi.server.hostname=$MYADDR -cp tabule.jar:.  cz.muni.fi.pa165.rmi.TabuleImpl

Metoda v rozhraní Tabule nepřijímá žádné třídy, které by JVM na straně serveru neznal. Pokud by tomu tak bylo, i klient by musel vystavit takové třídy někde na webu a musel by být spuštěn s parametrem -Djava.rmi.server.codebase, aby si server mohl třídy stáhnout.

JSON-RPC

Ukážeme si vzdálené volání procedur (RPC) pomocí protokolu JSON-RPC 2.0. K implementaci použijeme knihovnu jsonrpc4j.


Rozhraní služby

Stáhněte si Mavenový projekt tabule-json-rpc.zip, rozzipujte, a nainstalujte, bud pomocí mvn install nebo otevřením v NetBeans a zvolením Build.

Projekt obsahuje definici rozhraní a vyjímky:

Tabule.java

 
package cz.muni.fi.pa165.jsonrpc;
 
public interface Tabule {
    void zverejni(String text) throws Vyjimka;
}

Vyjimka.java

 
package cz.muni.fi.pa165.jsonrpc;
 
public class Vyjimka extends Exception {
 
    public Vyjimka(String message) {
        super(message);
    }
 
}

JSON-RPC server

Vytvořte si nový projekt typu Maven s názvem tabule-json-rpc-server a do pom.xml připište závislost na projektu tabule-json-rpc a knihovnách pro JSON-RPC:

 
    <name>tabule-json-rpc-server</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <repositories>
        <repository>
            <id>jsonrpc4j-webdav-maven-repo</id>
            <name>jsonrpc4j maven repository</name>
            <url>http://jsonrpc4j.googlecode.com/svn/maven/repo/</url>
            <layout>default</layout>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>cz.muni.fi.pa165</groupId>
            <artifactId>tabule-json-rpc</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode</groupId>
            <artifactId>jsonrpc4j</artifactId>
            <version>0.25</version>
        </dependency>
        <dependency>
            <groupId>javax.portlet</groupId>
            <artifactId>portlet-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>7.0.27</version>
        </dependency>
        <dependency>
            <groupId>org.simpleframework</groupId>
            <artifactId>simple</artifactId>
            <version>4.1.21</version>
        </dependency>
    </dependencies>

Vytvořte následující třídy v balíku cz.muni.fi.pa165.jsonrpc:

TabuleImpl.java

 
package cz.muni.fi.pa165.jsonrpc;
 
public class TabuleImpl implements Tabule {
 
    @Override
    public void zverejni(String text) throws Vyjimka {
        if (text == null || text.isEmpty()) throw new Vyjimka("text je prázdný");
        System.out.println(SimpleServer.getClient()+": " + text);
    }
}

SimpleServer.java

 
package cz.muni.fi.pa165.jsonrpc;
 
import com.googlecode.jsonrpc4j.JsonRpcServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.logging.*;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Container;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
 
public class SimpleServer implements Container {
 
    private JsonRpcServer jsonRpcServer;
    
    public SimpleServer(JsonRpcServer jsonRpcServer) {
        this.jsonRpcServer = jsonRpcServer;
    }
 
    private static ThreadLocal<String> client = new ThreadLocal<String>();
    
    static String getClient() {
        return client.get();
    }
 
    @Override
    public void handle(Request request, Response response) {
        try {
            if ("POST".equals(request.getMethod())) {
                response.set("Content-Type", "application/json");
                client.set(request.getClientAddress().getHostName());
                jsonRpcServer.handle(request.getInputStream(), response.getOutputStream());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 
    public static void main(String[] args) throws IOException {
        //enable logging
        Logger log = LogManager.getLogManager().getLogger("");
        log.setLevel(Level.ALL);
        for (Handler h : log.getHandlers()) h.setLevel(Level.FINEST);
        //start server
        Logger.getLogger(SimpleServer.class.getName()).info("starting server");
        JsonRpcServer tabule = new JsonRpcServer(new TabuleImpl(), Tabule.class);
        Connection connection = new SocketConnection(new SimpleServer(tabule));
        connection.connect(new InetSocketAddress(1420));
    }
 
}

Spusťte server.

JSON-RPC klient

Vytvořte nový projekt typu Maven jménem tabule-json-rpc-klient a do pom.xml připište:

 
     <repositories>
        <repository>
            <id>jsonrpc4j-webdav-maven-repo</id>
            <name>jsonrpc4j maven repository</name>
            <url>http://jsonrpc4j.googlecode.com/svn/maven/repo/</url>
            <layout>default</layout>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>cz.muni.fi.pa165</groupId>
            <artifactId>tabule-json-rpc</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode</groupId>
            <artifactId>jsonrpc4j</artifactId>
            <version>0.25</version>
        </dependency>
    </dependencies>

Vytvořte třídu

SimpleClient.java

 
package cz.muni.fi.pa165.jsonrpc;
 
import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
import com.googlecode.jsonrpc4j.ProxyUtil;
 
import java.net.MalformedURLException;
import java.net.URL;
 
public class SimpleClient {
 
    public static void main(String[] args) throws MalformedURLException, Vyjimka {
 
        JsonRpcHttpClient client = new JsonRpcHttpClient(new URL("http://localhost:1420/"));
        Tabule tabule = ProxyUtil.createClientProxy(SimpleClient.class.getClassLoader(), Tabule.class, client);
 
        tabule.zverejni("ahoj");
        //tabule.zverejni(null);
    }
}

Spusťte klienta. Pak zkuste odkomentovat řádek s tabule.zverejni(null); a spusťte ho znovu.

Samostatný úkol - změna rozhraní

V původním projektu tabule-json-rpc přidejte do rozhraní Tabule nějakou další metodu, dejte Build.

V projektu tabule-json-rpc-server implementujte metodu ve třídě TabuleImpl a znovu spusťte server.

V projektu tabule-json-rpc-klient zavolejte novou metodu.

Pozorujte, jak vypadají JSON-RPC zprávy v logu serveru.

SOAP/WSDL Web Services

Ukážeme si web services pomocí Apache CXF.


SOAP server

Vytvořte si nový projekt typu Maven zvaný třeba soap-cxf-server. Do pom.xml připište:

 
   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <cxf.version>2.7.0</cxf.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <!-- Jetty is needed if you're are not using the CXFServlet -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <!-- SLF4J binding -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>

Vytvořte package cz.muni.fi.pa165.soap a v něm tyto třídy:

 
package cz.muni.fi.pa165.soap;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.ws.WebFault;
 
@WebFault(name = "Vyjimka")
@XmlAccessorType(XmlAccessType.FIELD)
public class Vyjimka extends RuntimeException {
    String duvod;
 
    public Vyjimka(String message,String duvod) {
        super(message);
        this.duvod = duvod;
    }
}
 
package cz.muni.fi.pa165.soap;
 
import javax.jws.WebService;
import javax.jws.WebParam;
 
@WebService
public interface Tabule {
    
    void zverejni(@WebParam(name = "text")String text) throws Vyjimka;
 
}
 
package cz.muni.fi.pa165.soap;
 
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.servlet.http.HttpServletRequest;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
 
@WebService(endpointInterface = "cz.muni.fi.pa165.soap.Tabule", serviceName = "Tabule")
public class TabuleImpl implements Tabule {
 
    @Resource
    WebServiceContext wsCtx;
 
    public void zverejni(String text) throws Vyjimka {
        if (text == null || text.isEmpty()) throw new Vyjimka("empty text", "retezec prazdny");
 
        System.out.println(getClient() + ":" + text);
    }
 
    private String getClient() {
        MessageContext msgCtx = wsCtx.getMessageContext();
        HttpServletRequest req = (HttpServletRequest) msgCtx.get(MessageContext.SERVLET_REQUEST);
        return req.getRemoteHost();
    }
 
}
 
package cz.muni.fi.pa165.soap;
 
import javax.xml.ws.Endpoint;
 
public class Server {
    public static void main(String[] args) throws InterruptedException {
        String address = "http://localhost:9000/tabule";
        Endpoint.publish(address, new TabuleImpl());
        System.out.println("Server ready...");
    }
}

Spusťte třídu cz.muni.fi.pa165.soap.Server a navštivte URL http://localhost:9000/tabule?wsdl.

SOAP klient

Aniž uzavřete předchozí projekt, vytvořte nový projekt zvaný třeba soap-cxf-klient. Děláme to tak proto, abychom viděli, že jediným pojítkem mezi serverem a klientem je WSDL popis služby.

Do pom.xml připište

 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <cxf.version>2.7.0</cxf.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-common-utilities</artifactId>
            <version>2.5.6</version>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>${cxf.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <configuration>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>http://localhost:9000/tabule?wsdl</wsdl>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- nastaveni verze zdrojaku -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
            <!-- zavislosti na JARech -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <outputDirectory>
                        ${project.build.directory}
                    </outputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

Přidejte třídu:

 
package cz.muni.fi.pa165.soap;
 
public class Klient {
    public static void main(String[] args) {
        Tabule tabule = new Tabule_Service().getTabuleImplPort();
        try {
            
            tabule.zverejni("ahoj");
            System.out.println("zaslano"); 
            
            //tabule.zverejni("");
 
        } catch (Vyjimka_Exception e) {
            e.printStackTrace();
            System.out.println(e.getFaultInfo().getDuvod());
        }
    }
}

V NetBeans dejte Build. Alternativně můžete z příkazového řádku spustit mvn generate-sources a mvn package.

Spusťte třídu Klient.

Pro kopii všech knihoven potřebných pro spuštění z příkazového řádku zadejte příkaz mvn dependency:copy-dependencies.

URL na službu se bere z WSDL popisu služby. Pokud ho potřebujete změnit, lze to udělat takto:

 
((BindingProvider) tabule).getRequestContext()
                .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://nymfe31.fi.muni.cz:9000/tabule");

REST

REST server

Vytvořte si nový projekt typu Maven - Web Application, jménem třeba rest-tabule-server.

Do pom.xml připište:

 
<packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <!-- Jackson JSON processor -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!-- servlet API 3.0 -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSTL , just for escaping HTML -->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <!-- Java language version -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
            <!-- Servlet 3.0 without web.xml -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1.1</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

Vytvořte si nový servlet

 
package cz.muni.fi.pa165.mavenproject2;
 
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.taglibs.standard.functions.Functions;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.*;
 
@WebServlet(urlPatterns = "/tabule/*")
public class TabuleServlet extends HttpServlet {
 
    final List<Map<String, String>> zaznamy = Collections.synchronizedList(new ArrayList<Map<String, String>>());
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String pathInfo = request.getPathInfo();
        if (pathInfo != null && pathInfo.matches("/\\d+")) {
            response.setContentType("application/json");
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), zaznamy.get(Integer.parseInt(pathInfo.substring(1))));
        } else {
            if ("html".equals(request.getParameter("type"))) {
                response.setContentType("text/html;charset=utf-8");
                PrintWriter out = response.getWriter();
                response.setHeader("Refresh", "5");
                out.println("<h1>Tabule na " + request.getContextPath() + "/tabule</h2>");
                out.println("<table border=\"1\">");
                synchronized (zaznamy) {
                    for (int i = zaznamy.size(); --i >= 0;) {
                        Map<String, String> m = zaznamy.get(i);
                        out.println("<tr><td><a href=\""+request.getContextPath()+"/tabule/"+i+"\">" + i + "</a>"
                                + "<td>" + m.get("kdy")
                                + "<td>" + m.get("kdo") + "<td>" + Functions.escapeXml(m.get("co")));
                    }
                }
                out.println("</table>");
            } else {
                response.setContentType("application/json");
                ObjectMapper mapper = new ObjectMapper();
                mapper.writeValue(response.getOutputStream(), zaznamy);
            }
        }
    }
 
    @Override
    protected synchronized void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        String zverejni = request.getParameter("zverejni");
        if (zverejni != null) {
            Map<String, String> m = new LinkedHashMap<String, String>();
            m.put("kdy", sdf.format(new Date()));
            m.put("kdo", request.getRemoteHost());
            m.put("co", zverejni);
            zaznamy.add(m);
            response.sendRedirect(request.getContextPath() + "/tabule/" + (zaznamy.size() - 1));
        } else if (request.getContentType().startsWith("application/json")) {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode jsonNode = mapper.readValue(request.getInputStream(), JsonNode.class);
            JsonNode jsonZverejni = jsonNode.get("zverejni");
            if (!jsonNode.isMissingNode()) {
                Map<String, String> m = new LinkedHashMap<String, String>();
                m.put("kdy", sdf.format(new Date()));
                m.put("kdo", request.getRemoteHost());
                m.put("co", jsonZverejni.textValue());
                zaznamy.add(m);
                response.sendRedirect(request.getContextPath() + "/tabule/" + (zaznamy.size() - 1));
            } else {
                System.err.println("Missing key");
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing required data key.");
            }
        } else {
            System.err.println("Unknown request type " + request.getContentType());
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown request type.");
        }
    }
}

Po spuštění by na URL http://localhost:8080/tabule/?type=html měla být viditelná HTMl verze tabule, zatím prázdná. Na http://localhost:8080/tabule/ pak JSON data, zatím také prázdná.

REST klient

Vytvořte si nový projekt typu Maven - Java Application, jménem třeba rest-tabule-klient.

Do pom.xml připište

 
 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
 
    <build>
        <plugins>
            <!-- Java language version -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>

Vyvtořte si REST klienta, který přidá záznam a získá všechny záznamy:

 
package cz.muni.fi.pa165.rest.klient;
 
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.web.client.RestTemplate;
 
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
 
public class TabuleRestKlient {
    
    static String STROJ = "localhost";
    static int PORT = 8080;
    static String webapp = "rest-tabule-server";
    
    public static void main(String[] args) {
        RestTemplate rt = new RestTemplate();
 
        String url = "http://"+STROJ+":"+PORT+"/"+webapp+"/tabule";
        
        //POST pro přidání
        URI uri = rt.postForLocation(url+"?zverejni={text}", null, "příliš žluťoučký kůň");
        System.out.println("uri nového záznamu = " + uri);
 
        //GET pro získání všech záznamů
        JsonNode jsonNode = rt.getForObject(url, JsonNode.class);
 
        for(JsonNode m : jsonNode) {
            System.out.println("záznam = " + m);
        }
    }
}

Používáme Spring 3.x RestTemplate (javadoc pro RestTemplate).

Po spuštění na výše uvedeném URL můžete pozorovat přibývající záznamy.

Všimněte si, že kódování češtiny moc nefunguje, zkusíme to opravit.

Změníme kód klienta tak, aby místo URL parametru používal tělo POST requestu ve formátu JSON:

 
        //POST pro přidání
        Map<String,String> map = new HashMap<String, String>();
        map.put("zverejni","příliš žlutoučká kráva");
        URI uri = rt.postForLocation(url, map);