Вы находитесь на странице: 1из 47

4.

Network communication and


services

Service and Process Programming


Arturo Bernal
Nacho Iborra
IES San Vicente

This work is licensed under the Creative Commons AttributionNonCommercial-ShareAlike 4.0 International License. To view
a copy of this license, visit
http://creativecommons.org/licenses/by-nc-sa/4.0/

Table of Contents
4. Network communication and services
1. Introduction....................................................................................................................3
1.1. Introduction to sockets...........................................................................................3
1.2. Client-server communication.................................................................................3
1.3. Socket types...........................................................................................................4
2. Basic usage of Java sockets.........................................................................................5
2.1. Using TCP sockets.................................................................................................5
2.2. Using UDP sockets................................................................................................9
2.3. More about the InetAddress class.......................................................................12
2.4. Connecting multiple clients. Sockets and threads...............................................12
3. Some special connections...........................................................................................15
3.1. Multicast sockets..................................................................................................15
3.2. Object serialization...............................................................................................16
4. FTP and SMTP client...................................................................................................20
4.1 FTP Client.............................................................................................................20
4.2 SMTP Client..........................................................................................................23
5. HTTP(S) and Web Services........................................................................................25
5.1 Opening and reading a HTTP connection.............................................................25
5.2 Basic Web Service access....................................................................................28
5.3 JSON processing..................................................................................................28
5.4 Accessing web services from a different thread...................................................31
5.5 GET, POST, PUT, DELETE...................................................................................33
5.6 Emulating an AJAX call.........................................................................................44
5.7 Maintaining session with cookies..........................................................................44
5.7 Sending files..........................................................................................................46

Service and Process Programming Network communication and services

1. Introduction
In this unit we are going to focus on how applications communicate through a network
(either a local network or the Internet). We will read about some low-level communication
techniques and some other high-level services.
First, we are going to see how to communicate two Java applications through a network by
using sockets. We will see what a socket is, what types of sockets we can use and how to
use them.
Then, we will explain some higher level network services, such as e-mail, ftp access or
web services, and how to deal with them in Java.
In addition to this, we will explain some secure programming techniques, in order to
authenticate in a remote server, or encrypt the information that we send through the
network.

1.1. Introduction to sockets


A socket is a communication system between processes running on different machines in
a network. These processes send and receive information through a connector, and this
connector is commonly called socket.

1.1.1. Sockets and port numbers


Each socket has a port number associated to it. This number identifies the process that is
sending or receiving the information through this socket. So, when a local process wants
to communicate with a remote process, they both establish their own port numbers, and
whenever the information is sent to each other, the computer knows which process must
receive it by checking the destination port.
For instance, if a machine A has a process PA listening on port 30, and a machine
B has another process PB listening on port 44, whenever a message gets to
machine B indicating port number 44, then this message will be received by
process PB (the machine will redirect the message to that process). On the other
side, whenever a message gets to machine A indicating port number 30, it will be
received by process PA.

1.2. Client-server communication


Client-server communication is a concrete type of network communication where:

One side is set as a server, this is, it keeps listening to a given port number, waiting
for the other side to send requests

The other side is set as a client. It must know the server address, and the port
number to connect to, and then it can send requests to it.

When a client connects to a server, they establish a socket communication, from which the
client sends requests to the server, and the server sends the appropriate responses to the
client. For instance, in a web application, we must know the server address (typically its
domain name), and its port number (the default port number is 80 for web connections, if
we do not specify any). Then, we connect to the server, and we can ask it to send us web
pages, documents and other resources.
Service and Process Programming Network communication and services

We have said before that both sides need to have a port number associated to the
connection. The port number of the server must be known (for instance, port 80 for web
communications, or port 21 for FTP access), but the client port number is randomly
assigned, so that the server will be able to send its responses to the client.
For instance, if we try to create a connection to a web server hosted in
www.myserver.com, we will set port 80 as our destination port. Then, the server will
accept our connection, and automatically a random port number (for instance,
9241), will be assigned to our client. From then on, both client and server can send
messages to each other
Actually, when the connection between client and server is established, the original server
port is released to listen for more connections, and another socket is used to communicate
with the client. So the client will use the original server port to connect to it, and another
(random) port to send data to the server (although client does not need to know this last
port when it connects to the server).

1.3. Socket types


There are two basic types of sockets:

TCP sockets, which are connection-oriented sockets. They establish reliable


connections, where the delivery of every piece of message is guaranteed. This type
of connection is, however, slower than UDP connections that we will see next,
because the reception of every data package must be confirmed. So they are used
when the integrity of the data being sent is more important than the transfer speed.
FTP applications are examples of TCP connections, because the integrity of the
files that we download/upload is very important.

UDP sockets, which are non-connection-oriented. Connections are not reliable, and
so, there might be some pieces of messages that do not get to its destination. We
will use this type of connection when the speed is more important than the integrity
of the data.
For instance, a live video streaming application uses UDP, because the speed to
send and receive the video is more important than losing some frames.

Service and Process Programming Network communication and services

2. Basic usage of Java sockets


In this section we are going to learn how to deal with the two main types of sockets (TCP
and UDP) in Java. The core of Java sockets is defined in the java.net package. There, we
can find some useful classes, as we will see now.

2.1. Using TCP sockets


If we want to work with TCP sockets, we will need to handle these two classes:

ServerSocket class, that will be used to implement a socket in the server side, that
will be listening to client connection requests.

Socket class, that will hold the communication between both sides. We will use a
Socket object in each side.

In order to connect a client to a server, we will follow these steps:


In the server side:
1. Create a ServerSocket object in the server, specifying the desired port number.
Regarding port numbers, you must know that ports from 0 to 1023 are
usually reserved to other services, such as FTP, HTTP, SMTP... So it is a
good idea to choose a port higher than 1023 for our applications.
2. Wait until a client connects, with the accept method.
3. Once the connection is established, we will have a Socket object to communicate
with that client.
4. Then, we can create input and/or output streams on each side to send or receive
data to/from the client.
5. When we finish the communication, we must close everything: input stream, output
stream, socket... and server socket, if we don't want to accept more connections.
This can be easily done by putting all the closeable objects in the try clause (what is
called the try-with-resource clause).
Let's see all these steps in a piece of code:
ServerSocket server = null;
Socket service = null;
DataInputStream socketIn = null;
DataOutputStream socketOut = null;
try (

)
{

try-with-resource clause. Objects


inside this clause are automatically
closed when the "try" finishes

ServerSocket server = new ServerSocket(portNumber);


Socket service = server.accept();
DataInputStream sIn = new DataInputStream(service.getInputStream());
DataOutputStream sOut = new DataOutputStream(service.getOutputStream());

... // Communication process


} catch (IOException e) {
System.out.println(e);
}

In the client side:


Service and Process Programming Network communication and services

1. Create a Socket object with the server address and port.


2. When this instruction is completed, we have our connection to the server ready,
although it may throw an exception if something goes wrong.
3. Then, we can also create input and/or output streams to send or receive data
to/from the server.
If we put all of this together in a piece of code:
try (
Socket mySocket = new Socket("address", portNumber);
DataInputStream sIn = new DataInputStream(mySocket.getInputStream());
DataOutputStream sOut = new DataOutputStream(mySocket.getOutputStream());

)
{

... // Communication process


} catch (IOException e) {
System.out.println(e);
}

2.1.1. Example
Let's implement our first, complete example. Whenever we create a client-server
application, we normally create two separate projects: one for the server and another one
for the client, so that we can distribute both sides of the application separately. In this
case, we are going to create a client that says "Hello" to server, and a server that, when
receives this message, sends an answer with "Goodbye" to the client.
In the client side, our code should look like this:
public class Greet_Client
{
public static void main(String[] args)
{
try (
Socket mySocket = new Socket("localhost", 2000);
DataInputStream socketIn =
new DataInputStream(mySocket.getInputStream());
DataOutputStream socketOut =
new DataOutputStream(mySocket.getOutputStream());
)
{
socketOut.writeUTF("Hello");
String response = socketIn.readUTF();
System.out.println("Received: " + response);

} catch (IOException e) {
System.out.println(e);
}

In the server side, the code is:


public class Greet_Server
{
public static void main(String[] args)
{
try (
ServerSocket server = new ServerSocket(2000);
Socket service = server.accept();

Service and Process Programming Network communication and services

)
{

DataInputStream socketIn =
new DataInputStream(service.getInputStream());
DataOutputStream socketOut =
new DataOutputStream(service.getOutputStream());
// Read the message from the client
String message = socketIn.readUTF();
// Print the message
System.out.println("Received: " + message);
// Answer goodbye to the client
socketOut.writeUTF("Goodbye");

} catch (IOException e) {
System.out.println(e);
}

If we want to run this example (or any other client-server application), we must start by
running the server, and when it's waiting for connections, then we run the client. The
output should be the text "Received: Hello" in the server console, and the text "Received:
Goodbye" in the client console.

2.1.2. Some implementation issues


If we take a look at previous examples:

Note that we have used a DataInputStream object for reading and a


DataOutputStream object for writing. We could use other objects for reading or
writing, such as BufferedReader or PrintStream. With these two objects, we could
send and receive text messages line by line, instead of using UTF strings.

If the communication between client and server is not synchronous (i.e., the client
can send messages to the server at any time and/or there are multiple clients
communicating with server at any time), we will need to open the connections
(socket, input and/or output streams) inside the try clause (not inside the try-withresource clause), and close them in a finally clause. We will see an example of this
when using threads to deal with multiple connections (section 2.4).
try (ServerSocket server = new ServerSocket(2000))
{
service = ...
socketIn = ...
socketOut = ...
...
} catch (IOException e) {
System.out.println(e);
} finally {
try {
if (socketOut != null)
socketOut.close();
} catch (IOException ex) {}
try {
if (socketIn != null)
socketIn.close();
} catch (IOException ex) {}
try {
if (service != null)

Service and Process Programming Network communication and services

service.close();
} catch (IOException ex) {}

Also, if we have a graphical application, and we use the sockets or streams from
different events, we will not be able to use the try-with-resource clause, and we will
need to use several try clauses to use the socket, or the streams, or close them, in
different parts of our application. We will see this in exercise 5.

2.1.3. Some useful methods


Regarding the classes that we have just learnt, there are some useful constructors and
methods that we may need to have on hand (apart from the ones used in previous
examples):
ServerSocket(int port, int max)

Creates a server socket with the specified


input port and the maximum number of
simultaneous connections specified in the 2 nd
parameter

Socket(InetAddress addr, int port)

Creates a socket to connect to the specified


InetAddress object and port.

int getLocalPort()
int getPort()

These methods belong to Socket class, and


return the port number assigned to this
socket in the local machine and remote
machine, respectively.

readByte()
readChar()
readDouble()
readFloat()
readUTF()

These methods belong to DataInputStream


class, and they can be used to read specific
data types from the socket. The last one
returns a String in UTF-8 format.

writeBytes(String)
writeChars(String)
writeDouble(double)
writeFloat(float)
writeUTF(String)

These methods belong to DataOutputStream


class, and they write specific data types to
the socket (the last one writes a String in
UTF-8 format)

print(String message)
println(String message)

These methods belong to PrintStream class,


and allows us to write messages to the
socket as if we were writing them in the
console.

readLine()

This method belongs to BufferedReader


class, and allows us to read messages from
the socket line by line, as long as the other
side writes them line by line as well.

Exercise 1
Create an "echo" client-server application using Java sockets, by creating these two
separate projects (one for the client and another one for the server):

Create a project for the server side called Echo_Server. Define a server that will be
listening to port 6000, and when he gets a connection, it will constantly read a
message from the client, and send the same message back to it, converted into

Service and Process Programming Network communication and services

uppercase. For instance, if it receives the message "Hello", it will return "HELLO" to
the client.

Create a project for the client side called Echo_Client. Define a socket that will
connect to the server (use "localhost" as server name, if you are running both
projects in the same machine). When the connection is set, the client constantly ask
the user to enter a message, and then it will send it to the server, waiting for the
corresponding echo.

The communication process will finish when the server receives the message "bye".

2.2. Using UDP sockets


When we use UDP connections, we do not need to establish a previous connection
between client and server. Every time that we need to send a message between them, we
will need to specify the address and port number of the receiver, and it will get the sender
address and port from the message as well. We will work with DatagramSocket and
DatagramPacket classes.
The steps that we need to follow regarding UDP connections are:
1. Create a DatagramSocket specifying a local IP address and port. Regarding the
server, we usually specify a port number known by the client(s), although there are
several ways of creating the socket:
Connecting to localhost at any available port
DatagramSocket socket = new DatagramSocket();

Connecting to localhost at a given port number


DatagramSocket socket = new DatagramSocket(portNumber);

Connecting to a given host and port number


InetAddress address = ...
DatagramSocket socket = new DatagramSocket(portNumber, address);

2. As soon as we receive a packet (DatagramPacket), we can send messages to the


address and port stored in this packet, by using DatagramPacket objects as well.
We create a DatagramPacket object by specifying the message to be sent (as a
byte array), its length, and the destination address and port number. Then, we call
its send() message to send the datagram to the specified destination. To receive it,
we create an empty DatagramPacket object and call its receive method to fill it with
data.
String text = "Hello";
byte[] message = text.getBytes();
DatagramPacket packetS = new DatagramPacket(message, message.length,
InetAddress.getLocalHost(), 2000);
socket.send(packetS);
byte[] buffer = new byte[1024];
DatagramPacket packetR = new DatagramPacket(buffer, buffer.length);
socket.receive(packetR);

3. We can also specify a timeout in the socket (in milliseconds), so that if the receive
method waits for more than the specified timeout to get a response, then an
InterruptedIOException is thrown and we can go on.

Service and Process Programming Network communication and services

socket.setSoTimeout(2000); // 2 seconds

try
{

socket.receive(packetR);
} catch (InterruptedIOException e) {

4. When we finish the communication, we must close the socket established. We can
also put the socket in the try clause so that it will auto close when the clause
finishes.

2.2.1. Example
We are going to implement the same example shown in subsection 2.1.1, but in this case
we will use UDP protocol.
The client side would be like this:
public class GreetUDP_Client
{
public static void main(String[] args)
{
try (DatagramSocket mySocket = new DatagramSocket())
{
// Create the packet to be sent
String text = "Hello";
byte[] message = text.getBytes();
DatagramPacket packetS = new DatagramPacket(message, message.length,
InetAddress.getLocalHost(), 2000);
mySocket.send(packetS);
// Receive the response
byte[] buffer = new byte[1024];
DatagramPacket packetR = new DatagramPacket(buffer, buffer.length);
mySocket.receive(packetR);
System.out.println("Received: " +
new String(packetR.getData()).trim());
} catch (IOException e) {
System.out.println(e);
}
}

And the server side would be like this:


public class GreetUDP_Server
{
public static void main(String[] args)
{
try (DatagramSocket mySocket =
new DatagramSocket(2000, InetAddress.getLocalHost()))
{
// Receive the text
byte[] buffer = new byte[1024];
DatagramPacket packetR = new DatagramPacket(buffer, buffer.length);
mySocket.receive(packetR);
System.out.println("Received: " +
new String(packetR.getData()).trim());

Service and Process Programming Network communication and services

10

// Get host and port from the message


int destPort = packetR.getPort();
InetAddress destAddr = packetR.getAddress();
// Create the response to be sent
String text = "Goodbye";
byte[] message = text.getBytes();
DatagramPacket packetS = new DatagramPacket(message, message.length,
destAddr, destPort);
mySocket.send(packetS);
} catch (IOException e) {
System.out.println(e);
}
}

2.2.2. Some implementation issues


As you can see, a datagram is composed of:

The information or message to be sent

The message length

Destination IP and port number

Local IP and port number (added automatically when creating the datagram)

Note that, when we create a datagram packet to be sent, we convert the message (String)
into a byte array, with the getBytes() method. And when we want to receive a message, we
use the getData() method to get the bytes of the message, and then we create a String
with that array and trim it to clean the edges.
Besides, note that, when the server receives the packet from the client, it can retrieve the
client host name and port number with the methods getPort() and getAddress(), since this
information is included in the datagram automatically.

Exercise 2
Create a UDP client-server application with the following projects:

A project called UDPDictionary_Client that will send to the server a word typed by
the user.
Try to set a timeout in the client (for instance, 5 seconds), by using the
setSoTimeout method from the socket. If this timeout expires, an
InterruptedIOException will be thrown, and the client must print a message on
the screen with the text "No translation found".

A project called UDPDictionary_Server that will run on port 6000. It will have a
collection (hash table or something similar) with some words in English (keys) and
their corresponding Spanish translation (values). The server will read the word sent
by the client, and it will return the Spanish translation of that word. If the word can't
be found in the collection, the server will not return anything.

Service and Process Programming Network communication and services

11

2.3. More about the InetAddress class


You should have read about the InetAddress class before in this unit, although we have
not explained it in detail. It representes an IP address in Java, but it also has methods that
connect to a DNS server and resolve a hostname. In other words, we can use an object of
this class to connect to a remote server, either by its IP address (not very usual) or by its
domain name, that is automatically converted into an IP address.
To create an InetAddress object, we must use one of its static factory methods. The most
common is
InetAddress address = InetAddress.getByName("www.myserver.com");

This method makes a connection to the local DNS server to look up the name and its
corresponding IP address. We can also use this method to do a reverse lookup, this is, get
the hostname from the IP address:
InetAddress address = InetAddress.getByName("201.114.121.65");

In both cases, we can check both the hostname and the IP address stored in the
InetAddress object by calling the methods getHostName() and getHostAddress(),
respectively.

2.4. Connecting multiple clients. Sockets and threads


A server that only accepts one client is not a usual server. In real world, a server must be
ready to accept multiple clients. To do this, we need to create a thread in the server to deal
with each client. So, if we are working with a TCP application, the server code will only
create the ServerSocket object and go into a loop that creates a socket with each client,
and a thread that deals with it:
try (ServerSocket server = new ServerSocket(PORT))
{
System.out.println("Listening...");
while (true)
{
Socket service = server.accept();
System.out.println("Connection established");
ServerThread st = new ServerThread(service);
st.start();
}
} catch (IOException e) {
System.out.println(e);
}

In the thread, we usually implement all the communication with the client (the input and
output streams handling that we did in the server class before):
public class ServerThread extends Thread
{
Socket service;
public ServerThread(Socket s)
{
service = s;
}
@Override
public void run()

Service and Process Programming Network communication and services

12

{
DataInputStream socketIn = null;
DataOutputStream socketOut = null;
try
{
socketIn = new DataInputStream(service.getInputStream());
socketOut = new DataOutputStream(service.getOutputStream());
// Communication with client

} catch (IOException e) {
System.out.println(e);
} finally {
try {
if (socketOut != null)
socketOut.close();
} catch (IOException ex) {}
try {
if (socketIn != null)
socketIn.close();
} catch (IOException ex) {}
try {
if (service != null)
service.close();
} catch (IOException ex) {}
}

Note that, in this case, we can't use the try clause to define the socket and input streams
inside (i.e. use the try-with-resource clause), because the Socket object is created in the
server main object, and passed to the thread, so it would be closed by the server before
the thread could use it. So, we can use a finally clause to close the socket and the input
and output streams from the thread.
If we work with UDP servers, we do not need to use any thread, since every datagram
sent or received has no relationship with the others, and we do not need to establish a
different connection with each client. We only need to define a loop where the server
receives datagrams, gets the remote IP and port number, and sends a response to it.
while (true)
{
byte[] sent = new byte[1024];
byte[] received = new byte[1024];
DatagramPacket datagramReceived = new DatagramPacket(received,
received.length);
mySocket.receive(datagramReceived);
...
InetAddress remoteIP = datagramReceived.getAddress();
int remotePort = datagramReceived.getPort();
DatagramPacket datagramSent = new DatagramPacket (sent,
sent.length, remoteIP, remotePort);
}

mySocket.send(datagramSent);

Service and Process Programming Network communication and services

13

Exercise 3
Improve exercise 1 with these two changes:

Make the client-server connection independent from the machines where they are
placed. This is, you must not use "localhost" as the server address. Instead of this,
let the user specify the server address.

Allow more than one client connecting to the server. Then, the server will have to
echo the messages from different clients, sending each one its answers.

Call the new projects EchoImproved_Server and EchoImproved_Client.

Service and Process Programming Network communication and services

14

3. Some special connections


In this section we are going to deal with some special types of connections, such as
multicast connections and object serialization.

3.1. Multicast sockets


We use multicast sockets when we want to send the same information to a group of clients
at the same time. Before doing this, we need to create a multicast group, by assigning all
the components of the group the same IP address (of class D), and the same UDP port
number. Regarding the multicast IP addresses, we can use any from the range 224.0.0.0
to 239.255.255.255, although the first one (224.0.0.0) is reserved and it should not be
used.
So the general structure of a multicast communication between server and clients is the
same for server and client (the only differences are in what each side must do when
receiving or sending packets from the group):
MulticastSocket ms = new MulticastSocket(6000);
InetAddress groupAddr = InetAddress.getByName("225.0.0.1");
ms.joinGroup(groupAddr);
...
byte[] buffer = new byte[1024];
DatagramPacket packetR = new DatagramPacket(buffer, buffer.length);
ms.receive(packetR);
...
String message = "Welcome to this group";
DatagramPacket packetS = new DatagramPacket(message.getBytes(),
message.length(), groupAddr, groupPort);
ms.send(packetS);
...
ms.leaveGroup(groupAddr);
ms.close();

As you can see, multicast process relies on UDP protocol, so data transfers are not
reliable (it would be inefficient to wait for all the clients in the group to confirm each
reception).
Also keep in mind that, in some cases, you will not need any server side. For instance, if
we implement some kind of simple chat, where every client connects and sends messages
to everyone in the group, the server would not be necessary, since we would only need to
send our messages and receive everything that is sent to the group.

Exercise 4
Create a multicast application with the following projects:

A server project called MulticastMessage_Server that asks the user to enter


messages from the keyboard, and sends them to all the clients connected to the
group. The process will finish when the user types "finish".

A client project called MulticastMessage_Client that connects to the server and


prints in the console every message received from the server.

Service and Process Programming Network communication and services

15

Exercise 5
Create a multicast application that implements a chat.

The client side will be a JavaFX application


MulticastChat_Client with the following appearance:

in

project

called

At the beginning, it will ask the user to introduce his nickname at the top of the
window, and then it will try to connect to server. Once it connects to the server, the
user will be able to send messages with the lower text field. It will receive the
messages sent by everyone and will print them in the main text area. As soon as
the window client is closed, the connection must be closed as well.
To receive messages from the group periodically, you will need to implement any of
the thread-safe methods of communicating with a JavaFX application seen in Unit 3
(section 5). For instance, you can implement a Service that periodically receives
messages from the group and prints them in the text area.

In this case, we will not need any server side, since every client will send messages
to the group and receive messages from it.

3.2. Object serialization


When we talk about serializing an object, we actually talk about converting it into a bit
sequence, so that it can be sent through a connection or stream. In Java, we can serialize
any simple value (int, char, double...) or any object that implements the Serializable
interface. If it is a compound object, all of its attributes must be simple, or implement this
interface.
To send or receive objects from a stream, we use ObjectInputStream and
ObjectOutputStream classes. We can create them from basic InputStream or
OutputStream objets or methods, and when we have them, we can use their methods
readObject and writeObject to read or write serializable objects from/to a given stream.

Service and Process Programming Network communication and services

16

3.2.1. Sharing classes and objects between server and client


The classes that we serialize and send between server and client must be shared between
both projects. To do this, one option is to create a separate project with all the classes
involved in the serialization process. This project should not be a Java application, but a
class library, as there will not be any main class:

Then, we add this project as a library of both projects (client and server), from the
Properties panel of each project: we right-click on the project name, select Properties
option and then click on Libraries option on the left list. Finally, we click on the Add
Project... button and select the project where the serializable classes are placed.

3.2.2. Serialization through TCP sockets


Given a TCP socket, we can create, for instance, an ObjectOutputStream from it and send
an object through it:
ObjectOutputStream objOut = new ObjectOutputStream(socket.getOutputStream());
objOut.writeObject(myObject);

In the same way, we can create an ObjectInputStream in a socket, and read objects from
it:
ObjectInputStream objIn = new ObjectInputStream(socket.getInputStream());
MyObject obj = objIn.readObject();

Exercise 6
Create a serialization application with the following projects:

We are going to work with user data. So, create a new project (class library). Call it
UserData_Model, and define a User class inside, with the following attributes: the
user login and password (Strings), and the registration date, and the appropriate
constructors, getters and setters. The constructor must leave the login and

Service and Process Programming Network communication and services

17

password empty (""), and the registration date will be automatically filled with
current date.

A server project called UserData_Server. When a client connects, the server will
create a new User (from User class explained before). Then, it will send that User
object to the client, and wait for a response.

The client project will be called UserData_Client. It will connect to the server,
receive the User object, and then it will ask the user to fill its login and password.
Once this data is completed, the client will send the User object back to the server.

When the server receives the complete User object, it will print its information in the
console.

3.2.3. Serialization through UDP sockets


If we are working with UDP sockets, then we need to convert our serializable objects into
byte arrays, so that we can put them in the datagram packets. We will use
ByteArrayInputStream and ByteArrayOutputStream objects to read and write objects in
this case.
So, to write a serializable object in a UDP socket, we follow these steps:
// Write the object in a ByteArrayOutputStream
ByteArrayOutputStream bs = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(bs);
objOut.writeObject(myObject);
// Get the byte array from the written object
byte[] bytes = bs.toByteArray();
// Now, we just send the byte array as we did before with UDP sockets

If we want to read a serializable object from a UDP socket, then we do this:


// Receive the byte array, as we did before with UDP sockets
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
socket.receive(packet);
// Assign the byte array to a ByteArrayInputStream, and read the object
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(bis);
MyObject obj = in.readObject();

Exercise 7
In this exercise we are going to simulate a simplified version of an auction. To do this, we
will store the information of the product that we want to sell in a Product class (remember
to create a separate project (class library) to handle this class). The attributes that we want
to store are the product name, buyer's name and the product price. Initially, the server will
create a Product with its product name and an initial auction price (buyer's name will be
initially empty). Then, the server will wait for 3 different clients to connect, and it will send
the product to them. Each client will see the product information in the console, and then
they will type their name and the amount that he wants to pay for the product, in one line,
separated by a whitespace. For instance:
nacho 150
The server will pick up the responses from all the clients, and it will take the maximum
amount from them. Then, it will update product information with the current price and

Service and Process Programming Network communication and services

18

buyer's name, and will send the updated product to the clients, to make them know who
won the auction.
Here you can see an example of how it should work...
1. Initially, the server creates a product, with a name and initial price. For instance,
"Xbox One", 100 euros.
2. Then, it will wait for 3 clients to connect
3. Next, it will send the whole Product object to each client. So they will see this
information on their consoles:
Product name: Xbox One
Product initial price: 100 euros
4. Then, each client will be asked to introduce his name and offer, in the same format
explained before. For instance, we might have these three offers from the 3 different
clients:
nacho 150
arturo 170
ana 120
5. The server will receive these 3 messages, compare them and pick up the one with
the highest offer. Then, it will update the product data with the buyer's name and
price, and send the information back to the clients. So, the clients would see this
information as the final result:
Final price: 170 euros
Buyer's name: arturo
Call the projects Auction_Model (class library to store the Product class),
Auction_Server and Auction_Client. You can also add any additional class or method
that you may need (for instance, to manage clients connected to server)

Service and Process Programming Network communication and services

19

4. FTP and SMTP client


In this section we'll see how Java can connect to FTP and SMTP (email) as a client.

4.1 FTP Client


To access a FTP server through Java we'll use Apache's FTPClient
class which is included in Apache's Commons Net library.

4.1.1 Connection
The most simple way to connect to an existing FTP server through FTP protocol (you can
connect to an FTP server through HTTP protocol also) is opening a connection like this:
FTPClient ftp = new FTPClient();
try {
ftp.setControlEncoding("UTF-8"); // Important (before connect)
ftp.connect("172.16.208.128");
System.out.print(ftp.getReplyString());
ftp.enterLocalPassiveMode(); // If the server is in another network
if(!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
ftp.disconnect();
System.err.println("Error connecting to FTP");
}
} catch (IOException e) {
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {}
}
}

As you can see, we open a connection specifying the server IP or domain name (localhost
if it's running on the same machine). Then we print the response from the server and
check the reply code (indicating an error or success) using the class FTPReply to know if
the connection was successful or not.
IMPORTANT: The FTP server can close a connection if it has been inactive for a period of
time, so it's not guaranteed the connection will still be open anytime.

4.1.2 File listing


To list the contents of a directory, first we'll have to log in, and then we can use the
listFiles() method that returns an array of FTPFile objects, representing each one of them
a file (or subdirectory, or link) inside the directory:
if(ftp.login("arturo", "arturo")) {
FTPFile[] files = ftp.listFiles();
for(FTPFile file: files) {
String type = file.isDirectory()?
"Directory":file.isSymbolicLink()?
"Link":"File";
System.out.println(type + " -> " + file.getName());
}
}

Service and Process Programming Network communication and services

20

4.1.3 Uploading files


Before uploading a file to the server we must know if that file is in binary or text format and
set the FTP default file type to be transferred previous to send it. This is accomplished with
setFileType(int type) method. Valid values are FTP.ASCII_FILE_TYPE (default) or
FTP.BINARY_FILE_TYPE (recommended if you don't know the type of a file).
Other useful methods are changeWorkingDirectory(String pathname) that changes the
current working directory in the FTP server and changeToParentDirectory() which
changes to the parent directory.
public static void uploadFile(boolean isText, String filePath,
String nameInServer) {
if(!connect()) { // This is a method we have created to open a connection
System.err.println("Cannot upload file, error connecting!");
return;
}
try(FileInputStream in = new FileInputStream(filePath)) {
ftp.setFileType(
isText?FTP.ASCII_FILE_TYPE:FTP.BINARY_FILE_TYPE);
if(!ftp.storeFile(nameInServer, in)) {
System.err.println("Error uploading file " + filePath +
" (" + ftp.getReplyString() + ")");
} else {
System.out.println("File " + filePath +
" uploaded with name " + nameInServer);
}
} catch (IOException e) {
System.err.println("Error uploading file " + filePath
+ e.getMessage());
}
}

4.1.4 Downloading files


To download a file is a very similar process but using a FileOutputStream on the local
filename and the method retrieveFile.
public static void downloadFile(boolean isText, String nameInServer, String nameLocal) {
if(!connect()) {
System.err.println("Cannot download file, error connecting!");
return;
}
try(FileOutputStream out = new FileOutputStream(nameLocal)) {
ftp.setFileType(isText?FTP.ASCII_FILE_TYPE:FTP.BINARY_FILE_TYPE);
if(!ftp.retrieveFile(nameInServer, out)) {
System.err.println("Error downloading file " + nameInServer +
" (" + ftp.getReplyString() + ")");
} else {
System.out.println("File " + nameInServer +
" downloaded with name " + nameLocal);
}
} catch (IOException e) {
System.err.println("Error downloading file " + nameInServer +
e.getMessage());
}
}

Service and Process Programming Network communication and services

21

4.1.5 Other operations

rename(String from, String to) Changes a remote file's name.

deleteFile(String pathname) Deletes a remote file.

removeDirectory(String pathname) Deletes a directory, only if it's empty.

makeDirectory(String pathname) Creates a new subdirectory.

printWorkingDirectory() Gets the name of the current working directory.

listNames() Gets (only the names) the list of files inside the current directory.

Exercise 8
Create a JavaFX (by code or FXML) application called FTPManager. It will be a simple
FTP client, which you'll use to connect to a server (can be localhost or a virtual machine
IP), list its contents on a ListView and do several actions. The application aspect should be
more or less like this:

As you can see, once you enter a server address, a login name and password, and click
the Connect button, the application will list the working directory contents. The other
buttons will perform the following actions:

Upload: Will open a FileChooser dialog to select a file that will be uploaded to the
FTP server (current directory).

Dowload/Enter: Only active when something in the list is selected. Two situations
can happen.
If the selected item is a directory, the application will change to that directory
listing its files.
If it's a normal file, the application will open a DirectoryChooser dialog to select a
local directory and download the file there.

Go up: Will change to the parent directory and list its contents.

Service and Process Programming Network communication and services

22

Delete: Only active when something in the list is selected. Will delete the file.

There will be a label where you'll show every action's result, whether it's a success or an
failure. When it's needed, refresh the list's contents.

4.2 SMTP Client


SMTP is a protocol used for email transmissions between servers. The default ports used
in this protocol are 25 and 587 (this one for submitting a new email). For receiving emails,
IMAP and POP3 protocols are used.

4.2.1 Initial steps


To simplify things, we're going to use Google's GMail server to send emails from our Java
application.
First, it should be pointed out that since 2015, Google doesn't allow simple authentication
(username and password) by default to log in its accounts. Developers must use OAuth2
and in this link you'll see an example of how to use it.
However, we can still use the old and more simple method (but less secure) to connect to
GMail server (and compatible with other SMTP servers). To enable it, you must connect to
the GMail account you wish to use for sending emails (you can create a new account), and
follow this link:
https://www.google.com/settings/security/lesssecureapps
Then, you'll have to activate the option to allow the less secure access for applications.

Now, we'll need to download jaxa.mail and activation JAR libraries to include in our project
library path.
Javax.mail JAR download.
Java Activation Framework download.

4.2.2 Sending an email


To send an email we'll make use of Properties class to define the configuration for the
SMTP connection. Then, we'll open a connection with the server and send a simple email
with subject, body, and 3 destination addresses (one directly and the other 2 with copy).
public class Main {
final
final
final
final

static
static
static
static

String
String
String
String

senderEmail = " 2dam.iessanvicente@gmail.com";


senderPassword = "iessanvicente";
emailSMTPserver = "smtp.gmail.com";
emailServerPort = "587";

public static void main(String[] args) {


Properties props = new Properties();

Service and Process Programming Network communication and services

23

props.put("mail.smtp.user", senderEmail);
props.put("mail.smtp.host", emailSMTPserver);
props.put("mail.smtp.port", emailServerPort);
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.socketFactory.port", emailServerPort);
props.put("mail.smtp.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.connectiontimeout", "5000"); // Timeout
try {
Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
senderEmail, senderPassword);
}
};
//Open connection
Session session = Session.getInstance(props, auth);
Message msg = new MimeMessage(session);
msg.setText("Sample message");
msg.setSubject("Sample subject");
msg.setFrom(new InternetAddress(senderEmail));
msg.addRecipient(Message.RecipientType.TO,
new InternetAddress("arturo@iessanvicente.com"));
msg.addRecipients(Message.RecipientType.CC, new Address[] {
new InternetAddress("noexiste@iessanvicente.com"),
new InternetAddress("noexiste2@iessanvicente.com")
});
Transport.send(msg);
System.out.println("sent successfully");
} catch (Exception ex) {
System.err.println("Error occurred while sending.!");
}
}

Exercise 9
Create a JavaFX application called
MailSenderFX that will send an email.
This application will connect to a Gmail
account and send an email to any other
account, The account can be one that you
have
created
or
the
following:
2dam.iessanvicente@gmail.com
Pass: iessanvicente
The application will inform the user if
everything went well or there was any
error.

Service and Process Programming Network communication and services

24

5. HTTP(S) and Web Services


Nowadays, most applications rely on web services to access remote and centralized data
stored in a remote server through the World Wide Web. A web service is a technology
which uses a series of standards in order to communicate an application running in the
server (using any technology like PHP, Ruby, Java, .NET, ) with a client application that
can be running in any device and be written also in any language.
In this part we'll mainly focus in accessing REST web services using Java as a client,
simulate AJAX calls (like in Javascript), manage a session using cookies, and using
secure connections through HTTPS protocol checking the authenticity of a certificate.

5.1 Opening and reading a HTTP connection


There are many ways to connect to a web via the HTTP protocol in Java. One is by using
the native classes derived from UrlConnection, and the other is by using an external library
that simplifies the job.
Because one of the main uses of Java is for Android development, we'll look at what's
recommended there. There's a library called Apache Http Client, that was included in the
Android libraries but it's not supported anymore (and even if you still can use it, it's not
recommended). The recommended option is to use URLConnection class and its
derivatives like HttpURLConnection and HttpsURLConnection.

5.1.1 Using URLConnection


This is the most low-level connection method, meaning that the programmer will be
controlling every aspect of the connection but in contrast the resulting code will be larger
and uglier. It's recommended in Android because if used properly, it's the faster and the
least memory, processor (and battery) consuming method.
The class URL is used to represent the remote resource in the World Wide Web we'll be
accessing.
URL google = new URL("http://www.google.es");

This object will return a URLConnection object when we connect to it.


URLConnection conn = google.openConnection();

To get the response body (content), the connection provides an InputStream for that
purpose. It's also recommended to retrieve the charset encoding from the response
headers, in order to read everything (like accents) properly.
// Get charset encoding (UTF-8, ISO,...)
public static String getCharset(String contentType) {
for (String param : contentType.replace(" ", "").split(";")) {
if (param.startsWith("charset=")) {
return param.split("=", 2)[1];
}
}
}

return null; // Probably binary content

Service and Process Programming Network communication and services

25

public static void main(String[] args) {


BufferedReader bufInput = null;
try {
URL google = new URL("http://www.google.es");
URLConnection conn = google.openConnection();
String charset = getCharset(conn.getHeaderField("Content-Type"));
bufInput = new BufferedReader(
new InputStreamReader(conn.getInputStream(), charset));
String line;
while((line = bufInput.readLine()) != null) {
System.out.println(line);
}
} catch (MalformedURLException e) {...} catch (IOException e) { ...
} finally {
if(bufInput != null) {
try {
bufInput.close();
} catch (IOException e) {...}
}
}
}

5.1.2 HttpURLConnection and following redirections


The class HttpURLConnection provides additional methods like following redirections
automatically or getting the response code (such as 404 for Not Found).
To get an HttpULRConnection and follow redirections automatically you should call this
method:
URL sanvi = new URL("http://iessanvicente.com");
HttpURLConnection conn = (HttpURLConnection)sanvi.openConnection();
conn.setInstanceFollowRedirects(true);

It doesn't always work. For example, this connection returns code 301 (Moved
Permanently) and doesn't redirect to the new location automatically.
You can check what the response (and its headers) is by using available methods:
System.out.println(conn.getResponseCode());
System.out.println(conn.getResponseMessage());
System.out.println(conn.getHeaderFields());

With this information, we could manage manually these redirections (with the risk of falling
into a redirection loop), even if there are many, like this:
URL url = new URL("http://iessanvicente.com");
HttpURLConnection conn;
do {
conn = (HttpURLConnection)url.openConnection();
if(conn.getResponseCode() == 301) {
url = new URL(conn.getHeaderField("Location"));
}
} while(conn.getResponseCode() == 301);

Service and Process Programming Network communication and services

26

5.1.3 Using an external Library (AsyncHttpClient)


There are libraries like AsyncHttpClient that simplifies a lot
HTTP connections. If the project is not using tools like Maven
or Gradle, you'll have to download and add the necessary
libraries (JAR) manually.
public static void main(String[] args) {
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
Future<Response> f =
asyncHttpClient.prepareGet("http://www.google.es").execute();
try {
System.out.println(f.get().getResponseBody());
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
asyncHttpClient.close();
}

5.1.4 Which one we'll be using and why?


As always, using a library that simplifies the job have a lot of advantages. However this
library must be in active development and should offer good documentation to be useful,
or else the disadvantages could be worse than advantages. Other disadvantages include
that if the library doesn't support a functionality you need, can be more difficult to make it
work for that task (or maybe it supports it but it's not well documented and hard to find how
to do what you want).
Having that in mind, we'll use the native HttpURLConnection (as recommended in
Android) for many reasons. It's easy to find help when there's some problem (widely used),
we can do anything with it (low level access), and there's no need to include a library that
maybe is not supported in our platform or the developer stops maintaining it. In contrast,
our code will be bigger and more difficult to maintain so we need to be careful and keep a
clean and modularized code.

Exercise 10
Create a console Java application named LinkSearch that will ask you for an address and
print all the links (<a>) detected in the response. If you want, it's a good idea to create an
auxiliary class that extends from BufferedReader as we saw on uni t 1, to only filter those
links from the output.
This is part of the output that https://iessanvicente.com should show:

Service and Process Programming Network communication and services

27

5.2 Basic Web Service access


To access a REST (Representational State Transfer) web service we need an URL (which
represents a resource being accessed), and an operation (GET, POST, PUT, DELETE) to
do with that resource, along with additional data needed for the operation.
The simplest operation is GET, that is usually used for searching and getting information
about something that already exists. In a GET operation data (if necessary) is sent in the
URL like this http://domain/resource?data1=value1&data2=value2.
This is a really basic PHP web service that will read two numbers passed in the url (GET)
and will print (response) the result of that sum:
echo intval($_GET['num1']) + intval($_GET['num2']);

And this is how we can call it from Java and obtain the result:

private static String getSumFromService(int n1, int n2) {


BufferedReader bufInput = null;
String result;
try {
URL google = new URL("http://localhost/services/sum-service.php?num1="
+ n1 + "&num2=" + n2);
URLConnection conn = google.openConnection();
bufInput = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
result = bufInput.readLine();
} catch (IOException e) {
return "Error";
} finally {
if(bufInput != null) {
try {
bufInput.close();
} catch (IOException e) { return "Error"; }
}
}
}

return result == null?"Error":result;

public static void main(String[] args) {


System.out.println(getSumFromService(3, 5));
}

5.3 JSON processing


Nowadays, most web services send and receive information in JSON format (XML is
almost abandoned for this use). This format is native of Javascript but most languages like
Java have the necessary tools to process it.
The basic information about JSON and the available tools for each
language can be found at http://www.json.org/. To process this

Service and Process Programming Network communication and services

28

information we can use the org.json API (also present in Android) or other options like
Google's GSON, but there are a lot of options.
Let's see an example. Imagine that we receive this information from a web web service in
JSON format:
{

"error":false,
"person": {
"name":"Peter",
"age":30,
"address":[
{"city":"London","street":"Some street 24"},
{"city":"New York","street":"Other street 12"}
]
}

5.3.1 Using org.json


This library can be used, for example, in Android Studio without having to add any external
JAR. We'll have to load first the root JSON object and then navigate from there analyzing
its information or creating Java objects from corresponding JSON ones.
It's really useful to create a constructor in every class we need that receives a JsonObject
containing the necessary fields and creates the object from there:
public class Address {
String city;
String street;
public Address(JSONObject addressJson) {
city = addressJson.getString("city");
street = addressJson.getString("street");
}
@Override
public String toString() {
return street + " ( " + city + " )";
}
}
public class Person {
String name;
int age;
List<Address> address;
public Person(JSONObject personJson) {
name = personJson.getString("name");
age = personJson.getInt("age");
JSONArray addressArray = personJson.getJSONArray("address");
address = new ArrayList<>();
addressArray.forEach(a -> {
address.add(new Address((JSONObject)a));
});

Service and Process Programming Network communication and services

29

}
@Override
public String toString() {
String str = "Name -> " + name + "\nage -> " + age +
"\nAddress ->";
for(Address a: address)
str += "\n\t" + a.toString();
}

return str;

Finally, we need to connect to the web service, get its response and process the string
containing the resulting JSON code (GetServiceResponse is a custom class for connect
to a web service and return its response):
public static void main(String[] args) {
GetServiceResponse resp = new
GetServiceResponse("http://localhost/services/example1.php");

String json = resp.getResponse();


if(json != null) {
// We load the main (root) object
JSONObject object =
(JSONObject) new JSONTokener(resp.getResponse()).nextValue();
if(!object.getBoolean("error")) {
Person p = new Person(object.getJSONObject("person"));
System.out.println(p.toString());
} else {
System.out.println("There was an error in the request");
}
}

This is what this program should print:


Name -> Peter
age -> 30
Address ->
Some street 24 ( London )
Other street 12 ( New York )

5.3.2 Using GSON


GSON will try to automatically convert from JSON to a native Java object. So it will need a
class that contains the same fields as the JSON response (same name). There's no need
to create any specific constructor with this method.
Now we need an additional class that maps the initial JSON response format:
public class GetPersonResponse {
boolean error;
Person person;
public boolean getError() {
return error;
}
public Person getPerson() {
return person;
}

Service and Process Programming Network communication and services

30

If the field names are correctly names , it will map everything automatically:
public static void main(String[] args) {
GetServiceResponse resp =
new GetServiceResponse("http://localhost/services/example1.php");
String json = resp.getResponse();
if(json != null) {
Gson gson = new Gson();
GetPersonResponse personResp =
gson.fromJson(resp.getResponse(), GetPersonResponse.class);
if(!personResp.getError()) {
System.out.println(personResp.getPerson().toString());
} else {
System.out.println("There was an error in the request");
}
}
}

If a class property's name doesn't match the JSON field name, we can tell the GSON
parser that it has to assign that field by using an annotation with that property:
@SerializedName("error")
boolean haserror; // In JSON it will be named "error"

GSON tutorial

5.4 Accessing web services from a different thread


Connecting to a web service and getting a response can be a costly operation, specially if
the Internet connection is not the best and/or the server is overloaded. If we access a web
service in the main thread will mean that the application will be blocked (unresponsive)
until we get the result.
The best way to deal with web services (or any other remote connection) is by using a
separate thread to start the connection and then process the results when they're
received. If processing those results imply changing the view in a JavaFX application, we
can use a Service or the Platform.runLater() method (or AsyncTask in Android).
public class GetSumService extends Service<Integer> {
int n1, n2;
public GetSumService(int n1, int n2) {
super();
this.n1 = n1;
this.n2 = n2;
}
@Override
protected Task<Integer> createTask() {
return new Task<Integer>() {
@Override
protected Integer call() throws Exception {
BufferedReader bufInput = null;
Integer result = 0;
try {
URL google =

Service and Process Programming Network communication and services

31

new URL("http://localhost/services/sum-service.php?num1=" + n1 + "&num2=" + n2);


URLConnection conn = google.openConnection();
bufInput = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
result = Integer.parseInt(bufInput.readLine());
} catch (IOException e) {} finally {
if(bufInput != null) {
try {
bufInput.close();
} catch (IOException e) {}
}
}

}
}

Thread.sleep(5000); // simulate a 5 seconds delay!


return result;

};

This is the view:

In the application's controller, when we click Add button, we'll create and start the
service, and update the corresponding label when its finished:
public class FXWebServiceController implements Initializable {
@FXML
@FXML
@FXML
@FXML

private
private
private
private

Label resultLabel;
TextField num1;
TextField num2;
Button addButton;

@FXML private ImageView imgLoad;


GetSumService gss;
@FXML
private void sumNumbers(ActionEvent event) {
gss = new GetSumService(
Integer.parseInt(num1.getText()),
Integer.parseInt(num2.getText()));
gss.start();
addButton.setDisable(true);
imgLoad.setVisible(true);

Service and Process Programming Network communication and services

32

resultLabel.setVisible(false);

gss.setOnSucceeded(e -> {
resultLabel.setText("Result: " + gss.getValue());
addButton.setDisable(false);
imgLoad.setVisible(false);
resultLabel.setVisible(true);
});

@Override
public void initialize(URL url, ResourceBundle rb) {
}
}
This is how it will look like when processing the request (the wheel is a
downloaded animated gif):

5.5 GET, POST, PUT, DELETE


The 4 common operations in REST web services are GET, POST, PUT and DELETE.
While you can do everything just with GET and POST and calling different services for
different actions (add, update, or delete an item for example), it's recommended an cleaner
to access the same web service (or resource) and specify the operation you want to do.
These are the meanings of these operations or methods:

GET: Retrieves data and usually won't modify anything. It's equivalent to SELECT
in SQL. When using this method, the data will usually be sent in the URL.

POST: Operation intended to insert new data (it doesn't have to) and store it in the
server. It's usually equivalent to INSERT in SQL.

PUT: Operation that updates existing data in the server. Similar to UPDATE in SQL.

DELETE: Operation that deletes existing data in the server (like DELETE in SQL).

As said before, the most common used methods are GET (data in the URL and limited in
length) and POST (data sent in the body of the request, can be much larger), but that
depends on how the web services are configured. If the server is prepared to accept those
Service and Process Programming Network communication and services

33

4 methods for operations, for example, with /product resource to operate with products
stored in the database, some frameworks like AngularJS in Javascript offer a really
simplified way to make these operations.
Let's see how to interact with web services that support these operations in Java:

5.5.1 Upgrading Product Manager to use web services


To help with the explanation we'll modify the application Product Manager used in unit 2,
so that products are stored in a server with a database and operations with those products
will be done through web services in PHP (Using Phalcon in Microframework mode).
In the server (which is the same computer where the application
runs in our example) there is a database with a table for
products and a table for categories:
And the structure of both tables (category and product):

To interact with the database and create web services, we'll use the Phalcon framework in
PHP, which is a complete and powerful framework, and the fastest in PHP because it's
compiled from C language and loaded as a library (binary). Other frameworks are written
in PHP directly, which is much slower than compiled code. You should be able to download
everything (java, php and sql) from Aula Virtual to try this application.

5.5.2 Generic web service access


To avoid repeating code as much as possible, we have created a class with a static
method for accessing a web service and getting a response. This method, called
getResponse, receives 3 parameters: the url to connect, the data to send in the body of
the request (or null if there's no data), and the operation or method (GET, POST, PUT,
DELETE).
Also, some HTTP headers have been established so that the request is similar to what a
web browser would send, like the origin domain (in this case localhost), the preferred
language (Spanish), the possibility to compress data (gzip) in order to save bandwidth, a
time out for the connection, or the data type used for communication (application/json).
public class ServiceUtils {
// Get charset encoding (UTF-8, ISO,...)
public static String getCharset(String contentType) {
for (String param : contentType.replace(" ", "").split(";")) {
if (param.startsWith("charset=")) {
return param.split("=", 2)[1];
}
}

Service and Process Programming Network communication and services

34

return null; // Probably binary content


}
public static String getResponse(String url, String data, String method) {
BufferedReader bufInput = null;
StringJoiner result = new StringJoiner("\n");
try {
URL urlConn = new URL(url);
HttpURLConnection conn =
(HttpURLConnection) urlConn.openConnection();
conn.setReadTimeout(20000 /*milliseconds*/);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod(method);
conn.setRequestProperty("Host", "localhost");
conn.setRequestProperty("Connection", "keep-alive");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Origin", "http://localhost");
conn.setRequestProperty("Accept-Encoding", "gzip,deflate,sdch");
conn.setRequestProperty("Accept-Language", "es-ES,es;q=0.8");
conn.setRequestProperty("Accept-Charset", "UTF-8");
conn.setRequestProperty("User-Agent", "Java");
conn.setRequestProperty("X-Requested-With", "XMLHttpRequest");
if (data != null) {
conn.setRequestProperty("Content-Type",
"application/json; charset=UTF-8");
conn.setRequestProperty("Content-Length",
Integer.toString(data.length()));
conn.setDoOutput(true);
//Send request
DataOutputStream wr =
new DataOutputStream(conn.getOutputStream());
wr.write(data.getBytes());
wr.flush();
wr.close();
}
String charset = getCharset(conn.getHeaderField("Content-Type"));
if (charset != null) {
InputStream input = conn.getInputStream();
if ("gzip".equals(conn.getContentEncoding())) {
input = new GZIPInputStream(input);
}
bufInput = new BufferedReader(
new InputStreamReader(input));
String line;
while((line = bufInput.readLine()) != null) {
result.add(line);
}

}
} catch (IOException e) {
} finally {
if (bufInput != null) {
try {
bufInput.close();

Service and Process Programming Network communication and services

35

} catch (IOException e) { }
}

return result.toString();
}

5.5.3 GET
GET operations are intended to retrieve existing objects from the database (SELECT). In
this case, there are 2 web services:

A service using the resource /category that gets all categories in the database:

A service using the resource /product that receives a category id and returns all
products that are part of that category (Notice that when we receive a List of
objects, we need com.google.gson.reflect.TypeToken and java.lang.reflect.Type
classes if we intend to use GSON library):

This is the JavaFX Service that will get the categories:

And this is the service that will get the products from a category:

Service and Process Programming Network communication and services

36

To end this GET example, we'll see how we request products when a new category is
selected in the application:

Exercise 11
Create a project named GetCompanies in JavaFX. It will use these two web services
using GET:

http://<IP address>/exercises/company Will return all the companies with this


JSON format:

[
{
"id": "1",
"cif": "C2314234Y",
"name": "Crazy Stuff Inc.",
"address": "Madness Street 15"
},
{
"id": "2",

Service and Process Programming Network communication and services

37

"cif": "T1342536Y",
"name": "Silly & Dumb LTD",
"address": "Idont Know Street, 1324"
},
...
]

http://<IP address>/exercises/company/{idCompany} Will return the


information of a company in this format (if there's information you won't need, like
idCompany, just ignore it and don't include it in Java's class):

{
"ok": true,
"error": "",
"company": {
"id": "3",
"cif": "V3241569E",
"name": "Maniacs International",
"address": "Happy Stress Street, 99",
"employees": [
{
"id": "5",
"nif": "46374869U",
"name": "Cocaine Rupert",
"age": "37",
"idCompany": "3"
},
{
"id": "6",
"nif": "12425364K",
"name": "Happysad Windows",
"age": "47",
"idCompany": "3"
}
]
}
}

First, it will load all companies on the top list from the adequate web service. Then, when a
user selects a company, it will retrieve its information and employees from the other web
service, showing them in the bottom list. This application will look more or less like this:

Service and Process Programming Network communication and services

38

5.5.4 POST
Using POST, data is sent in the body of the request. This is done by using the output
stream of the connection to send that data. The format can be URL
(key=value&key2=value2) or like in this case, JSON (in String format).
This is the web service that will process the information and insert a new product. It just
returns (prints) an integer indicating the id of the newly created product (or -1 if there was
an error). It could return a JSON object with more information if we wanted to:

And this is the service in Java that will call this web service and return its response:

Finally, this is the code in the view's controller to start this service and process its result:

Service and Process Programming Network communication and services

39

Exercise 12
Update the project GetCompanies from exercise 11 and insert an Add button for adding
employees. This button (only active when a company is selected) will open a new window
containing a form to add a new employee.

To create new window using another FXML (or building the scene by code), you can do it
like this:
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
Parent root = FXMLLoader.load(getClass().getResource("AddEmployee.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Add employee");
stage.setScene(scene);
stage.show();
stage.setOnHidden((e) -> {
// Update the employees list somehow...
});

Service and Process Programming Network communication and services

40

To cancel and close the window opened (at least using FXML):
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.close();

This form will have to be sent to this web service by POST:


http://<IP address>/exercises/company/{idCompany}
And you need to send this information (there can be more fields and they'll be ignored):
{
"age": 32,
"nif": "12324354T",
"name": "Delete Meplease"
}

This web service will return the id of the new employee if everything was correct, or -1 if
something failed. It will also return an error message:
{
"id": "12",
"error": ""
}

5.5.5 PUT
This HTTP method is a mix between GET and POST, and it's usually equivalent to the
UPDATE instruction in SQL. The information to get the object (id for example) that will be
modified from the database is sent in the URL (like GET), and the data to modify from that
object is sent in the body (like POST).
This is the web service that will process the information and update an existing. It just
returns true or false indicating if the operation was successful or not. It could return a
JSON object with more information (like error messages) if we wanted to:

This is the JavaFX Service created to connect to this web service:

Service and Process Programming Network communication and services

41

And finally, this is how we create and start the Service from the controller:

5.5.6 DELETE
The DELETE operation works like GET (variables in URL), but instead of returning objects
that meet some conditions, it deletes them. First, we'll see the web service used:

Service and Process Programming Network communication and services

42

This is the JavaFX Service that will connect to this web service:

And finally, how we create and start the service from the controller:

Exercise 13
Update the GetCompanies project from exercises 11 and 12. You'll have to add two more
buttons:

Update: Only active when an employee is selected. It will open a window with a
form (similar to Add) to edit an employee's information. It will connect to whi web
service and send information by PUT method:
http://<IP address>/exercises/employee/{idEmployee}
The information sent in JSON will have the same format that when you add an
employee (see exercise 3), and the response will be a JSON object with two
fields ok (boolean) and error (String).

Delete: Will open a dialog to ask the user if he/she wants to delete the selected
employee (see ProductManager example application from unit 2, or the updated
example in this unit). If positive answer is given it will call this web service (using
DELETE method):

Service and Process Programming Network communication and services

43

http://<IP address>/exercises/employee/{idEmployee}
The response will be a JSON object with the same format as before (update).

5.6 Emulating an AJAX call


Some frameworks which run on the server side, differentiate a normal HTTP request from
an AJAX one (done with Javascript). This is because when it's a normal request means
that a browser is the client doing that petition and a full HTML document (view) must be
served. When it's an AJAX call, it usually means that only a fragment of HTML or JSON is
served, and not a full HTML document.
For example, with the Phalcon framework, is useful to know when it's a normal or an AJAX
request in order to load the full framework (more powerful but heavier) or a micro
framework (less functionality but more than enough for web services).
This is how Phalcon can detect an AJAX call:

The server knows


when it's an AJAX call (from Javascript) checking the X-Requested-With header that must
contain the value XMLHttpRequest. All we need to do to emulate these calls and make
the server believe that we are requesting it from JavaScript and getting a JSON response
instead of a full HTML document is adding this header to the connection:

5.7 Maintaining session with cookies


Even if we think that we don't need to handle cookies in our application, there's one case
when it's almost obligatory (unless we want to send login information every time we use a
web service), and it's when there's some type of authentication at the beginning. The
Service and Process Programming Network communication and services

44

server will store information indicating that the login was successful in a session and will
answer the client returning a cookie with the session id. That's what the client has to send
to the server automatically whenever it starts a connection, so the server can check if a
login has been previously successful and which user is logged in (among any other
information that wants to store in the session).
Imagine that we have these 2 web services:

The first one will store the name of the user in a session variable called user, and will
print that name. When we call the second service, it checks if that session variable exists
and prints its value or No if it doesn't exist (session usually expires in a few minutes if the
client doesn't connect to the server meanwhile). If we don't store the session id
(automatically generated by the server and sent with headers as a cookie) in the client, we
won't sent it in the second request and the server will create a new session meaning that it
will be empty and that variable won't exist. This is the default behaviour:

Only by adding a line at the beginning of the application in order to create a


cookie manager is enough for Java to automatically handle cookies like a web browser
(more or less) and the session will work normally.

Service and Process Programming Network communication and services

45

5.8 Sending files


For files, which some of the are binary and contain non printable characters, sending their
contents inside a JSON structure is not possible. However, you can convert a file into a
printable string using an encoding mechanism, in this case we'll use Base64.
First of all, let's take a look at the web service that will receive the file (in this case it will
expect to receive an image, but we could check) and store it in the server.

Important!: The directories where we are going to store files in the server must have write
permissions for everybody as Apache uses its own user in the system.
As you can see, it will receive the file data encoded inside data field, and at least the file
name it had in the client inside the name field. It will decode the file's data and store it in
the server where the web service is located, inside public/img subdirectory.
We're going to use the class ServiceUtils that we created before to make things easier.
First, let's take a look at the class used to represent a file object that will be serialized into
JSON by the Gson library:

As you can notice. We store file's data inside a String, because that's what the Base64
encoder will generate when using its method encodeToString(), and it's the most
Service and Process Programming Network communication and services

46

appropriate format to include in the JSON body. We also store file's name and the
directory where it was located (absolute path).
Finally. Let's take a look at an example of how to send the image to the web service in the
main method:

Curiosities:
If you don't use a framework like Phalcon. The recommended way in PHP to get the JSON
sent (instead of a URL formatted string as usual with GET and POST), is like this:
json_decode(file_get_contents("php://input"));
If we want to send the image inside an URL formatted string (var1=value1&var2=value..)
instead of using JSON, we'll have to use Java's urlEncoder:
An then in PHP, we'll use this to convert from url base64 to normal base64:
$base64=strtr($base64urlData, '-_', '+/');
$decoded = base64_decode($base64);

Exercise 14
Create a JavaFX project called PhotoUploader
that will upload a photo with a title and a
description to a web service using POST:
http://<IP address>/photoplaces/photo
Use the AJAX call header or you won't get the
desired result this time. The JSON response
object will contain: ok boolean, and error
String.
This is the JSON format you'll have to send:
{
"name": "IMG_20130831_174624.jpg",
"title": "Lovaly place",
"desc": "place description",
data: base 64 encoded data.
}

Access this address in your browser to check


all uploaded images and delete them:
http://<IP address>/photoplaces/

Service and Process Programming Network communication and services

47

Вам также может понравиться