The objective of the information on this page is to explain how to build a (Web) application with the JEE platform, able to visualize the weather forecast of a worldwide known city. The main challenge is to post a query on a weather server, to get the answer, to analyse it and to publish it on our local Web server (glasfish5). We mix Java, Python and the Servlets technology inside the JEE 8 platform.
The lecture is accompanied by demos / practical work / implementations of concrete examples... otherwise the lecture will be to much! We start in Section 1 with revision material related to Java.
The main document, in PDF format is available here. I will adapt it according to your skills and our progress.
The codes for the weather station example are available by clicking on the following links: ValMeteoJour.java, EnsValMeteoYanis.java, EnsValMeteo.java, VueStationMeteo.java, StationMeteoConstantes.java. They illustrate, as explained during the lecture, the options with the concepts of inheritance and interface. Compile them as follows:
$ javac EnsValMeteoYanis.java $ javac EnsValMeteo.java
As an extension of the previous example, you may have a look to the World Weather Forecast web site, namely the WWIS data interface.
You will get the possibility to build your own graphical interface which will propose to be informed about the weather situation in main cities in the world. You will need to do a curl/wget networking query to parse the XML for the code of cities, then to parse the result. Here is an example of a curl call and its result for the New-York city:
$ curl http://worldweather.wmo.int/fr/json/278_fr.xml {"city":{"lang":"fr","cityName":"New York City, New York","cityLatitude":"40.650000000","cityLongitude":"-73.780000000","cityId":278,"isCapital":false,"stationName":"New York City, New York","tourismURL":"","tourismBoardName":"","isDep":false,"timeZone":"-0400","isDST":"Y","member":{"memId":93,"memName":"Etats-Unis","shortMemName":"","url":"www.nws.noaa.gov\/","orgName":"National Weather Service","logo":"usa_logo.png","ra":4},"forecast":{"issueDate":"2019-04-17 22:40:00","timeZone":"Local","forecastDay":[{"forecastDate":"2019-04-18","wxdesc":"","weather":"pluie fine","minTemp":"9","maxTemp":"17","minTempF":"48","maxTempF":"63","weatherIcon":1502},{"forecastDate":"2019-04-19","wxdesc":"","weather":"Petite averse","minTemp":"14","maxTemp":"21","minTempF":"57","maxTempF":"69","weatherIcon":1201},{"forecastDate":"2019-04-20","wxdesc":"","weather":"Averse de pluie","minTemp":"16","maxTemp":"19","minTempF":"60","maxTempF":"66","weatherIcon":901},{"forecastDate":"2019-04-21","wxdesc":"","weather":"Petite averse","minTemp":"11","maxTemp":"17","minTempF":"52","maxTempF":"63","weatherIcon":1201},{"forecastDate":"2019-04-22","wxdesc":"","weather":"Ensoleill\u00e9","minTemp":"11","maxTemp":"21","minTempF":"52","maxTempF":"70","weatherIcon":2402},{"forecastDate":"2019-04-23","wxdesc":"","weather":"P\u00e9riode ensoleill\u00e9e","minTemp":"12","maxTemp":"18","minTempF":"54","maxTempF":"65","weatherIcon":2201},{"forecastDate":"2019-04-24","wxdesc":"","weather":"Petite averse","minTemp":"10","maxTemp":"16","minTempF":"50","maxTempF":"61","weatherIcon":1201}]},"climate":{"raintype":"Rainfall","raindef":"","rainunit":"inch","datab":1981,"datae":2010,"tempb":"","tempe":"","rdayb":"","rdaye":"","rainfallb":"","rainfalle":"","climatefromclino":"","climateMonth":[{"month":1,"maxTemp":"3.5","minTemp":"-2.8","meanTemp":null,"maxTempF":"38.3","minTempF":"26.9","meanTempF":null,"raindays":"10.4","rainfall":"92.7","climateFromMemDate":"2014-01-06"},{"month":2,"maxTemp":"5.3","minTemp":"-1.7","meanTemp":null,"maxTempF":"41.6","minTempF":"28.9","meanTempF":null,"raindays":"9.2","rainfall":"78.5","climateFromMemDate":"2014-01-06"},{"month":3,"maxTemp":"9.8","minTemp":"1.8","meanTemp":null,"maxTempF":"49.7","minTempF":"35.2","meanTempF":null,"raindays":"10.9","rainfall":"110.7","climateFromMemDate":"2014-01-06"},{"month":4,"maxTemp":"16.2","minTemp":"7.1","meanTemp":null,"maxTempF":"61.2","minTempF":"44.8","meanTempF":null,"raindays":"11.5","rainfall":"114.3","climateFromMemDate":"2014-01-06"},{"month":5,"maxTemp":"21.6","minTemp":"12.2","meanTemp":null,"maxTempF":"70.8","minTempF":"54.0","meanTempF":null,"raindays":"11.1","rainfall":"106.4","climateFromMemDate":"2014-01-06"},{"month":6,"maxTemp":"26.3","minTemp":"17.6","meanTemp":null,"maxTempF":"79.3","minTempF":"63.6","meanTempF":null,"raindays":"11.2","rainfall":"112.0","climateFromMemDate":"2014-01-06"},{"month":7,"maxTemp":"28.9","minTemp":"20.4","meanTemp":null,"maxTempF":"84.1","minTempF":"68.8","meanTempF":null,"raindays":"10.4","rainfall":"116.8","climateFromMemDate":"2014-01-06"},{"month":8,"maxTemp":"28.1","minTemp":"19.9","meanTemp":null,"maxTempF":"82.6","minTempF":"67.8","meanTempF":null,"raindays":"9.5","rainfall":"112.8","climateFromMemDate":"2014-01-06"},{"month":9,"maxTemp":"24.0","minTemp":"16.0","meanTemp":null,"maxTempF":"75.2","minTempF":"60.8","meanTempF":null,"raindays":"8.7","rainfall":"108.7","climateFromMemDate":"2014-01-06"},{"month":10,"maxTemp":"17.7","minTemp":"10.0","meanTemp":null,"maxTempF":"63.8","minTempF":"50.0","meanTempF":null,"raindays":"8.9","rainfall":"111.8","climateFromMemDate":"2014-01-06"},{"month":11,"maxTemp":"12.1","minTemp":"5.3","meanTemp":null,"maxTempF":"53.8","minTempF":"41.6","meanTempF":null,"raindays":"9.6","rainfall":"102.1","climateFromMemDate":"2014-01-06"},{"month":12,"maxTemp":"6.1","minTemp":"0.0","meanTemp":null,"maxTempF":"43.0","minTempF":"32.0","meanTempF":null,"raindays":"10.6","rainfall":"101.6","climateFromMemDate":"2014-01-06"}]}}}macbook-de-christophe:~ christophecerin$
Let's solve the problem! The Java code to start a curl query is available in this file ProcessBuilderTest.java.
This code downloads the file 278_fr.json, which is a JSON file and it encodes also a Python dictionary. The data file is available.
The Python code that parses the previous dictionary is available with the file 278.py.
The result for the execution of the Python code is:
MacBook-de-Christophe:Cities christophecerin$ python 278.py New York City, New York 2019-04-27 Min Temp: 9 Max Temp: 14 Forecast: Période ensoleillée 2019-04-28 Min Temp: 9 Max Temp: 14 Forecast: Pluie 2019-04-29 Min Temp: 5 Max Temp: 14 Forecast: Ensoleillé 2019-04-30 Min Temp: 10 Max Temp: 18 Forecast: Petite averse 2019-05-01 Min Temp: 11 Max Temp: 17 Forecast: Petite averse 2019-05-02 Min Temp: 12 Max Temp: 16 Forecast: Petite averse 2019-05-03 Min Temp: 11 Max Temp: 16 Forecast: Petite averse MacBook-de-Christophe:Cities christophecerin$
For the installation, and if you have multiple distribution of the JDK, as with my installation:
$ ls /Library/Java/JavaVirtualMachines jdk-10.0.1.jdkjdk-12.0.1.jdkjdk1.8.0_181.jdk jdk-11.0.1.jdkjdk1.8.0_131.jdkjdk1.8.0_191.jdk
you probably need to customize the file glassfish5/glassfish/bin/asadmin of Glassfish5 as follows, here to use JDK 1.8.0_191 which is compatible with JEE 8:
$ tail -14 asadmin # Always use JDK 1.6 or higher AS_INSTALL=`dirname "$0"`/../glassfish AS_INSTALL_LIB="$AS_INSTALL/lib" . "${AS_INSTALL}/config/asenv.conf" JAVA=java #Depends upon Java from ../config/asenv.conf if [ ${AS_JAVA} ]; then JAVA=${AS_JAVA}/bin/java fi # OLD version #exec "$JAVA" -jar "$AS_INSTALL_LIB/client/appserver-cli.jar" "$@" exec /usr/libexec/java_home -v 1.8.0_191 --exec java -jar "$AS_INSTALL_LIB/client/appserver-cli.jar" "$@"
Then you will be able to stop/start the application server:
MacBook-de-Christophe:~ christophecerin$ glassfish5/glassfish/bin/asadmin start-domain Waiting for domain1 to start .... Successfully started the domain : domain1 domain Location: /Users/christophecerin/glassfish5/glassfish/domains/domain1 Log File: /Users/christophecerin/glassfish5/glassfish/domains/domain1/logs/server.log Admin Port: 4848 Command start-domain executed successfully. MacBook-de-Christophe:~ christophecerin$
You are now ready to open a browser with URL as http://localhost:8080/
Other files that can be usefull to read for installing all you need:
If you use a special JDK, don't forgot to set environment variable (here for a MacOS system):
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/
If you use the latest version of maven and that the pom.xml file specifies and oldest one, don't forget to modify the dependencies.
For instance, if you try to install some projects coming with glassfish distribution as samples, modify the pom.xml with the correct version of maven (here 3.6.1 and not 2.1.1 in the original distribution. We need a pom.mxl file as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>servlet-samples</artifactId> <groupId>org.glassfish-samples</groupId> <version>5.0-SNAPSHOT</version> </parent> <groupId>org.glassfish-samples</groupId> <artifactId>non-blocking-io-read-war</artifactId> <version>5.0-SNAPSHOT</version> <packaging>war</packaging> <name>non-blocking-io-read-war</name> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> </dependency> </dependencies> <build> <finalName>non-blocking-io-read-war</finalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <artifactId>maven-dependency-plugin</artifactId> <version>3.6</version> </plugin> </plugins> </build> </project>
If your observe the following message, either follow the indication (change the port number) or shutdown the server (a new one will be lanch):
$ mvn cargo:run [INFO] Scanning for projects... [INFO] [INFO] -----------< org.glassfish-samples:non-blocking-io-read-war >----------- [INFO] Building non-blocking-io-read-war 5.0-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- cargo-maven2-plugin:1.4.0:run (default-cli) @ non-blocking-io-read-war --- [INFO] [en2.ContainerRunMojo] Resolved container artifact org.codehaus.cargo:cargo-core-container-glassfish:jar:1.4.0 for container glassfish4x [INFO] [talledLocalContainer] Port for cargo-domain (4848) is in use. Try a different port number. [INFO] [talledLocalContainer] CLI130: Could not create domain, cargo-domain [INFO] [talledLocalContainer] Command create-domain failed. [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.215 s [INFO] Finished at: 2019-04-28T15:39:09+08:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.codehaus.cargo:cargo-maven2-plugin:1.4.0:run (default-cli) on project non-blocking-io-read-war: Execution default-cli of goal org.codehaus.cargo:cargo-maven2-plugin:1.4.0:run failed: Failed to create a GlassFish 4.x standalone configuration: GlassFish admin command failed: asadmin exited 1 -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
In my case, I decided to shutdown the server, then to install, then to run the application:
$ ~/glassfish5/bin/asadmin stop-domain $ mvn install $ mvn cargo:run [INFO] Scanning for projects... [INFO] [INFO] -----------< org.glassfish-samples:non-blocking-io-read-war >----------- [INFO] Building non-blocking-io-read-war 5.0-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- cargo-maven2-plugin:1.4.0:run (default-cli) @ non-blocking-io-read-war --- [INFO] [en2.ContainerRunMojo] Resolved container artifact org.codehaus.cargo:cargo-core-container-glassfish:jar:1.4.0 for container glassfish4x [INFO] [talledLocalContainer] Using port 4848 for Admin. [INFO] [talledLocalContainer] Using port 8080 for HTTP Instance. [INFO] [talledLocalContainer] Using port 7676 for JMS. [INFO] [talledLocalContainer] Using port 3700 for IIOP. [INFO] [talledLocalContainer] Using port 8181 for HTTP_SSL. [INFO] [talledLocalContainer] Using port 3820 for IIOP_SSL. [INFO] [talledLocalContainer] Using port 3920 for IIOP_MUTUALAUTH. [INFO] [talledLocalContainer] Using port 8686 for JMX_ADMIN. [INFO] [talledLocalContainer] Using port 6666 for OSGI_SHELL. [INFO] [talledLocalContainer] Using port 9009 for JAVA_DEBUGGER. [INFO] [talledLocalContainer] Distinguished Name of the self-signed X.509 Server Certificate is: [INFO] [talledLocalContainer] [CN=MacBook-de-Christophe.local,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US] [INFO] [talledLocalContainer] Distinguished Name of the self-signed X.509 Server Certificate is: [INFO] [talledLocalContainer] [CN=MacBook-de-Christophe.local-instance,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US] [INFO] [talledLocalContainer] Domain cargo-domain created. [INFO] [talledLocalContainer] Domain cargo-domain admin port is 4848. [INFO] [talledLocalContainer] Domain cargo-domain admin user is "admin". [INFO] [talledLocalContainer] Command create-domain executed successfully. [INFO] [talledLocalContainer] GlassFish 4.x starting... [INFO] [talledLocalContainer] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=48m; support was removed in 8.0 [INFO] [talledLocalContainer] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0 [INFO] [talledLocalContainer] Waiting for cargo-domain to start ...... [INFO] [talledLocalContainer] Successfully started the domain : cargo-domain [INFO] [talledLocalContainer] domain Location: /Users/christophecerin/glassfish5/samples/servlet/non-blocking-io-read-war/target/cargo/installs/glassfish/glassfish/domains/cargo-domain [INFO] [talledLocalContainer] Log File: /Users/christophecerin/glassfish5/samples/servlet/non-blocking-io-read-war/target/cargo/installs/glassfish/glassfish/domains/cargo-domain/logs/server.log [INFO] [talledLocalContainer] Admin Port: 4848 [INFO] [talledLocalContainer] Command start-domain executed successfully. [INFO] [talledLocalContainer] Application deployed with name non-blocking-io-read-war. [INFO] [talledLocalContainer] Command deploy executed successfully. [INFO] [talledLocalContainer] Application deployed with name cargocpc. [INFO] [talledLocalContainer] Command deploy executed successfully. [INFO] [talledLocalContainer] GlassFish 4.x started on port [8080] [INFO] Press Ctrl-C to stop the container...
Last but not least, the previous Web application is now available at URL http://localhost:8080/non-blocking-io-read-war. Enjoy!
Servlets are the Java programs that runs on the Java-enabled web
server or application server. They are used to handle the request
obtained from the web server, process the request, produce the
response, then send response back to the web server.
Properties of Servlets :
Execution of Servlets :
Execution of Servlets involves the six basic steps:
Servlet Architecture
The following diagram shows the servlet architecture:
Advantages of a Java Servlet
The Servlet Container
Servlet container, also known as Servlet engine is an integrated set of objects that provide run time environment for Java Servlet components.
In simple words, it is a system that manages Java Servlet components on top of the Web server to handle the Web client requests.
This sample application that simulates a Weather Station demonstrates (a) how to use the non-blocking I/O functionality in servlets (b) how to parse the arguments of the URL (c) how to do a curl query and how to parse and analyze the respond.
The code is available online HERE.
In this sample application, the client (ClientTest
)
sends two parts of data to the server (ServerTest
). The
server registers a ReadListener
, whose methods are called
when events related to the input stream occur. The implementation of
ReadListener
in this application collects data and echoes
it back in reverse order.
The server executes the reverse operation, in a non blocking manner. This means that the server is not blocked until the end of the conversation with the client.
Then, we proceed with the execution of a code that examine the parameters on the URL line. The client prints the name and values of the parameters. This is a concrete example demonstrating the analysis of parameters i.e. how to communicate from the external world with the client's program.
At last, the client executes the curl
query in order
to download the information about the city, given in a parameter (city
code), on the URL line.
Notice that all the useful work is accomplished in the client. We could also balance the work by making the server executing the Internet query. This is just a choice!
Client
In ClientTest
, the client initiates an HTTP connection
to the server and writes two parts of data with a two second pause
between them, which simulates blocking from the client side.
@WebServlet(name = "ClientTest", urlPatterns = {"/"}) public class ClientTest extends HttpServlet { OutputStream output = null; InputStream input = null; protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... String urlPath = "http://" + request.getServerName() + ":" + request.getLocalPort() //default http port is 8080 + request.getContextPath() + "/ServerTest"; URL url = new URL(urlPath); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); ... conn.connect(); ... output = conn.getOutputStream(); String firstPart = "Hello"; writeData(output, firstPart); Thread.sleep(2000); ... // Sending the second part input = conn.getInputStream(); printEchoData(out, input); } protected void writeData(OutputStream output, String data) throws IOException { if (data != null && !data.equals("") && output != null) { output.write(data.getBytes()); output.flush(); } } protected void printEchoData(PrintWriter out, InputStream input) throws IOException { while (input.available() > 0 && input != null && out != null) { out.print((char) input.read()); } out.println("</br>"); } }
The servlet uses OutputStream
and InputStream
to write and read data,
and Thread.sleep()
to pause the thread for two seconds to
simulate I/O blocking. You can send larger blocks of data instead of
"Hello World".
Then the servlet executes the code to analyse the URL parameters:
@WebServlet(name = "ClientTest", urlPatterns = {"/*"}) public class ClientTest extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); ... // Start parsing of parameters Enumeration paramNames = request.getParameterNames(); out.print("<h1> Parameter Analysis...</h1>"); out.println("<table border=1 cellpadding=5 cellspacing=5>"); //out.println("<table>"); out.println("<tr> <th>Parameter Name</th>" + "<th>Parameter Value</th></tr>"); while(paramNames.hasMoreElements()) { String paramName = (String)paramNames.nextElement(); out.print("<tr><td>" + paramName + "\n<td>"); String[] paramValues = request.getParameterValues(paramName); if (paramValues.length == 1) { String paramValue = paramValues[0]; if (paramValue.length() == 0) out.println("No Value"); else out.println(paramValue); } else { out.println("<ul>"); for(int i=0; i<paramValues.length; i++) { out.println("<li>" + paramValues[i] + "</li>"); } out.println("</ul>"); } } out.println("</table>"); out.flush(); ... ...
Then the client execute the querry to the Internet in order to get
the wheather of the city specified as an argument of the URL. In the
first implementation, We store the answer into a file in
the /tmp/your_user_name/Cities/
directory. The code is
as follows:
ProcessBuilder pb = new ProcessBuilder( "/usr/bin/curl", "-s", "http://worldweather.wmo.int/fr/json/" + city + "_fr.xml"); pb.directory(new File("/tmp/your_user_name/Cities")); pb.redirectErrorStream(true); Process p = pb.start(); InputStream is = p.getInputStream(); FileOutputStream outputStream = new FileOutputStream( "/tmp/your_user_name/Cities/" + city + "_fr.json"); BufferedInputStream bis = new BufferedInputStream(is); byte[] bytes = new byte[100]; int numberByteReaded; while ((numberByteReaded = bis.read(bytes, 0, 100)) != -1) { outputStream.write(bytes, 0, numberByteReaded); Arrays.fill(bytes, (byte) 0); } outputStream.flush(); outputStream.close();
In the second implementation, we transfer the answer of the Python
code which analyze the answer, to the code that transforms an
InputStream parameter to a String one. The code for transforming the
types is String theString = IOUtils.toString(iss,
"UTF-8");
. The final portion of code is:
ProcessBuilder pbb = new ProcessBuilder( "python", "/Users/christophecerin/glassfish5/samples/servlet/weather-station-war/src/main/java/org/glassfish/servlet/weather_station_war/parse.py", "/tmp/your_user_name/Cities/"+city+"_fr.json"); pbb.redirectErrorStream(true); Process pp = pbb.start(); InputStream iss = pp.getInputStream(); String theString = IOUtils.toString(iss, "UTF-8");
Server
In ServerTest
, the server receives the request, starts
the asynchronous processing of the request, and registers
a ReadListener
@WebServlet(name="ServerTest", urlPatterns={"/ServerTest"}, asyncSupported = true) public class ServerTest extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... final AsyncContext context = request.startAsync(); final ServletInputStream input = request.getInputStream(); final ServletOutputStream output = response.getOutputStream(); input.setReadListener(new ServerReadListenerImpl(input, output, context)); } ... }
@WebServlet(..., asyncSupported = true)
is an
annotation that specifies the servlet name, its URL, and enables
asynchronous processing. The setReadListener()
method
registers a read listener for the input stream.
Note: Non-blocking I/O only works with asynchronous request processing in servlets and filters.
Implementation of the Read Listener
ReadListenerImpl.java
is the implementation of the ReadListener
interface:
public class ReadListenerImpl implements ReadListener { public ServerReadListenerImpl() { ... } @Override public void onDataAvailable() { ... while (input.isReady() && !input.isFinished()) { sb.append((char) input.read()); // Use StringBuilder to append chars together } } @Override public void onAllDataRead() { ... output.print("Echo the reverse String from server: " + sb.reverse().toString() + "<br>"); output.flush(); ... } @Override public void onError(Throwable t) { ... System.out.println("--> onError"); }
The onDataAvailable()
method is invoked when data is
available to be read from the input request stream. The container
subsequently invokes the read()
method if and only
if isReady()
returns
true. The onAllDataRead()
method is invoked when all the
data from the request has been read. The onError(Throwable
t)
method is invoked if there is any error or exceptions occurs
while processing the request. The isReady()
method
returns true if the underlying data stream is not blocked. At this
point, the container invokes the onDataAvailable()
method.
You can customize the constructor to handle different
parameters. Usually, the parameters
are ServletInputStream
, ServletOutputStream
,
or AsyncContext
. This sample uses all of them to
implement the ReadListener
interface.
This sample application demonstrates the following key features:
ReadListener
interface to avoid waiting for data inside a loop.To compile and to start the execution, do the following sequence of actions:
MacBook-de-Christophe:weather-station-war christophecerin$ mvn clean [INFO] Scanning for projects... [INFO] [INFO] -------------< org.glassfish-samples:weather-station-war >-------------- [INFO] Building weather-station-war 5.0-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- maven-clean-plugin:2.3:clean (default-clean) @ weather-station-war --- [INFO] Deleting file set: /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target (included: [**], excluded: []) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.471 s [INFO] Finished at: 2019-05-02T09:18:27+08:00 [INFO] ------------------------------------------------------------------------ MacBook-de-Christophe:weather-station-war christophecerin$ mvn install [INFO] Scanning for projects... [INFO] [INFO] -------------< org.glassfish-samples:weather-station-war >-------------- [INFO] Building weather-station-war 5.0-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ weather-station-war --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 3 resources [INFO] [INFO] --- maven-compiler-plugin:3.6.1:compile (default-compile) @ weather-station-war --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target/classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ weather-station-war --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.6.1:testCompile (default-testCompile) @ weather-station-war --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ weather-station-war --- [INFO] No tests to run. [INFO] [INFO] --- maven-war-plugin:2.2:war (default-war) @ weather-station-war --- [INFO] Packaging webapp [INFO] Assembling webapp [weather-station-war] in [/Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target/weather-station-war] [INFO] Processing war project [INFO] Copying webapp resources [/Users/christophecerin/glassfish5/samples/servlet/weather-station-war/src/main/webapp] [INFO] Webapp assembled in [58 msecs] [INFO] Building war: /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target/weather-station-war.war [INFO] [INFO] --- maven-install-plugin:2.4:install (default-install) @ weather-station-war --- [INFO] Installing /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target/weather-station-war.war to /Users/christophecerin/.m2/repository/org/glassfish-samples/weather-station-war/5.0-SNAPSHOT/weather-station-war-5.0-SNAPSHOT.war [INFO] Installing /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/pom.xml to /Users/christophecerin/.m2/repository/org/glassfish-samples/weather-station-war/5.0-SNAPSHOT/weather-station-war-5.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.420 s [INFO] Finished at: 2019-05-02T09:18:34+08:00 [INFO] ------------------------------------------------------------------------ MacBook-de-Christophe:weather-station-war christophecerin$ mvn cargo:run [INFO] Scanning for projects... [INFO] [INFO] -------------< org.glassfish-samples:weather-station-war >-------------- [INFO] Building weather-station-war 5.0-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- cargo-maven2-plugin:1.4.0:run (default-cli) @ weather-station-war --- [INFO] [en2.ContainerRunMojo] Resolved container artifact org.codehaus.cargo:cargo-core-container-glassfish:jar:1.4.0 for container glassfish4x [INFO] [talledLocalContainer] Using port 4848 for Admin. [INFO] [talledLocalContainer] Using port 8080 for HTTP Instance. [INFO] [talledLocalContainer] Using port 7676 for JMS. [INFO] [talledLocalContainer] Using port 3700 for IIOP. [INFO] [talledLocalContainer] Using port 8181 for HTTP_SSL. [INFO] [talledLocalContainer] Using port 3820 for IIOP_SSL. [INFO] [talledLocalContainer] Using port 3920 for IIOP_MUTUALAUTH. [INFO] [talledLocalContainer] Using port 8686 for JMX_ADMIN. [INFO] [talledLocalContainer] Using port 6666 for OSGI_SHELL. [INFO] [talledLocalContainer] Using port 9009 for JAVA_DEBUGGER. [INFO] [talledLocalContainer] Distinguished Name of the self-signed X.509 Server Certificate is: [INFO] [talledLocalContainer] [CN=Campusnetwork.net.hziee.edu.cn,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US] [INFO] [talledLocalContainer] Distinguished Name of the self-signed X.509 Server Certificate is: [INFO] [talledLocalContainer] [CN=Campusnetwork.net.hziee.edu.cn-instance,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US] [INFO] [talledLocalContainer] Domain cargo-domain created. [INFO] [talledLocalContainer] Domain cargo-domain admin port is 4848. [INFO] [talledLocalContainer] Domain cargo-domain admin user is "admin". [INFO] [talledLocalContainer] Command create-domain executed successfully. [INFO] [talledLocalContainer] GlassFish 4.x starting... [INFO] [talledLocalContainer] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=48m; support was removed in 8.0 [INFO] [talledLocalContainer] Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0 [INFO] [talledLocalContainer] Waiting for cargo-domain to start ...... [INFO] [talledLocalContainer] Successfully started the domain : cargo-domain [INFO] [talledLocalContainer] domain Location: /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target/cargo/installs/glassfish/glassfish/domains/cargo-domain [INFO] [talledLocalContainer] Log File: /Users/christophecerin/glassfish5/samples/servlet/weather-station-war/target/cargo/installs/glassfish/glassfish/domains/cargo-domain/logs/server.log [INFO] [talledLocalContainer] Admin Port: 4848 [INFO] [talledLocalContainer] Command start-domain executed successfully. [INFO] [talledLocalContainer] Application deployed with name weather-station-war. [INFO] [talledLocalContainer] Command deploy executed successfully. [INFO] [talledLocalContainer] Application deployed with name cargocpc. [INFO] [talledLocalContainer] Command deploy executed successfully. [INFO] [talledLocalContainer] GlassFish 4.x started on port [8080] [INFO] Press Ctrl-C to stop the container...
It remains to explore the weather, here for Clermont-Ferrand in France, with a query like http://localhost:8080/weather-station-war/?City=1052.
A good introduction to Java mail is available online at this address or from the main repository.
You probably need to get the following two jar
files javax.mail.jar, activation.jar
in order to be able
to complie and to execute the samples. You can download the java mail
jar file as well as sample examples
from https://github.com/javaee/javamail/releases
and the activation jar file
from https://www.oracle.com/technetwork/java/jaf11-139815.html.
This is an example on how to compile and send a message with a sample coming with the java mail examples:
cerin@ankara:~/public_html/HDU$ javac -cp ./javax.mail.jar:./activation.jar:. msgsendsample.java cerin@ankara:~/public_html/HDU$ java -cp ./javax.mail.jar:./activation.jar:. msgsendsample christophe.cerin@orange.fr christophe.cerin@lipn.univ-paris13.fr mail.lipn.univ-paris13.fr true DEBUG: JavaMail version 1.6.2 DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map DEBUG: setDebug: JavaMail version 1.6.2 DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle] DEBUG SMTP: useEhlo true, useAuth false DEBUG SMTP: trying to connect to host "mail.lipn.univ-paris13.fr", port 25, isSSL false 220 mail.lipn.univ-paris13.fr ESMTP Postfix; DEBUG SMTP: connected to host "mail.lipn.univ-paris13.fr", port: 25 EHLO luxembourg.lipn.univ-paris13.fr 250-mail.lipn.univ-paris13.fr 250-PIPELINING 250-SIZE 50000000 250-ETRN 250-STARTTLS 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN DEBUG SMTP: Found extension "PIPELINING", arg "" DEBUG SMTP: Found extension "SIZE", arg "50000000" DEBUG SMTP: Found extension "ETRN", arg "" DEBUG SMTP: Found extension "STARTTLS", arg "" DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg "" DEBUG SMTP: Found extension "8BITMIME", arg "" DEBUG SMTP: Found extension "DSN", arg "" DEBUG SMTP: use8bit false MAIL FROM:250 2.1.0 Ok RCPT TO: 250 2.1.5 Ok DEBUG SMTP: Verified Addresses DEBUG SMTP: christophe.cerin@orange.fr DATA 354 End data with . Date: Fri, 3 May 2019 02:10:29 +0200 (CEST) From: christophe.cerin@lipn.univ-paris13.fr To: christophe.cerin@orange.fr Message-ID: <943968903.0.1556842229376@luxembourg.lipn.univ-paris13.fr> Subject: JavaMail APIs Test MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit This is a message body. Here's the second line. . 250 2.0.0 Ok: queued as 7813E261407 DEBUG SMTP: message successfully delivered to mail server QUIT 221 2.0.0 Bye cerin@ankara:~/public_html/HDU$ ps -aux | grep -i postfix root 2608 0.0 0.0 30536 3816 ? Ss avril08 0:06 /usr/lib/postfix/master postfix 2612 0.0 0.0 32540 3896 ? S avril08 0:02 qmgr -l -t unix -u postfix 19036 0.0 0.0 32388 3440 ? S 01:57 0:00 pickup -l -t unix -u -c cerin 19820 0.0 0.0 15484 2260 pts/7 S+ 02:11 0:00 grep -i postfix cerin@ankara:~/public_html/HDU$
Special note: the machine that executes the previous java command should be configured as a relay. You will probably need to run Postfix on your machine. To start a Postfix service on an Unix machine do as follows:
MacBook-de-Christophe:javamail-samples christophecerin$ sudo postfix start Password: postfix/postfix-script: starting the Postfix mail system MacBook-de-Christophe:javamail-samples christophecerin$ java -cp ../javax.mail.jar:../activation.jar:. msgsendsample christophe.cerin@orange.fr christophe.cerin@lipn.univ-paris13.fr mail.lipn.univ-paris13.fr true DEBUG: JavaMail version 1.6.2 DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map DEBUG: setDebug: JavaMail version 1.6.2 DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle] DEBUG SMTP: useEhlo true, useAuth false DEBUG SMTP: trying to connect to host "mail.lipn.univ-paris13.fr", port 25, isSSL false 220 mail.lipn.univ-paris13.fr ESMTP Postfix; DEBUG SMTP: connected to host "mail.lipn.univ-paris13.fr", port: 25 EHLO 192.168.125.115 250-mail.lipn.univ-paris13.fr 250-PIPELINING 250-SIZE 50000000 250-ETRN 250-STARTTLS 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN DEBUG SMTP: Found extension "PIPELINING", arg "" DEBUG SMTP: Found extension "SIZE", arg "50000000" DEBUG SMTP: Found extension "ETRN", arg "" DEBUG SMTP: Found extension "STARTTLS", arg "" DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg "" DEBUG SMTP: Found extension "8BITMIME", arg "" DEBUG SMTP: Found extension "DSN", arg "" DEBUG SMTP: use8bit false MAIL FROM:250 2.1.0 Ok RCPT TO: 404 4.5.2 <192.168.125.115>: Helo command rejected: need fully-qualified hostname DEBUG SMTP: Valid Unsent Addresses DEBUG SMTP: christophe.cerin@orange.fr DEBUG SMTP: Sending failed because of invalid destination addresses RSET 250 2.0.0 Ok DEBUG SMTP: MessagingException while sending, THROW: javax.mail.SendFailedException: Invalid Addresses; nested exception is: com.sun.mail.smtp.SMTPAddressFailedException: 404 4.5.2 <192.168.125.115>: Helo command rejected: need fully-qualified hostname at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:2079) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1301) at javax.mail.Transport.send0(Transport.java:255) at javax.mail.Transport.send(Transport.java:124) at msgsendsample.main(msgsendsample.java:86) Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 404 4.5.2 <192.168.125.115>: Helo command rejected: need fully-qualified hostname at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1979) ... 4 more QUIT 221 2.0.0 Bye --Exception handling in msgsendsample.java javax.mail.SendFailedException: Invalid Addresses; nested exception is: com.sun.mail.smtp.SMTPAddressFailedException: 404 4.5.2 <192.168.125.115>: Helo command rejected: need fully-qualified hostname at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:2079) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1301) at javax.mail.Transport.send0(Transport.java:255) at javax.mail.Transport.send(Transport.java:124) at msgsendsample.main(msgsendsample.java:86) Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 404 4.5.2 <192.168.125.115>: Helo command rejected: need fully-qualified hostname at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1979) ... 4 more ** Invalid Addresses ** ValidUnsent Addresses christophe.cerin@orange.fr MacBook-de-Christophe:javamail-samples christophecerin$ java -cp ../javax.mail.jar:../activation.jar:. msgsendsample christophe.cerin@orange.fr christophe.cerin@lipn.univ-paris13.fr localhost true DEBUG: JavaMail version 1.6.2 DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map DEBUG: setDebug: JavaMail version 1.6.2 DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle] DEBUG SMTP: useEhlo true, useAuth false DEBUG SMTP: trying to connect to host "localhost", port 25, isSSL false 220 MacBook-de-Christophe.local ESMTP Postfix DEBUG SMTP: connected to host "localhost", port: 25 EHLO 192.168.125.115 250-MacBook-de-Christophe.local 250-PIPELINING 250-SIZE 10485760 250-VRFY 250-ETRN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250-DSN 250 SMTPUTF8 DEBUG SMTP: Found extension "PIPELINING", arg "" DEBUG SMTP: Found extension "SIZE", arg "10485760" DEBUG SMTP: Found extension "VRFY", arg "" DEBUG SMTP: Found extension "ETRN", arg "" DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg "" DEBUG SMTP: Found extension "8BITMIME", arg "" DEBUG SMTP: Found extension "DSN", arg "" DEBUG SMTP: Found extension "SMTPUTF8", arg "" DEBUG SMTP: use8bit false MAIL FROM: 250 2.1.0 Ok RCPT TO: 250 2.1.5 Ok DEBUG SMTP: Verified Addresses DEBUG SMTP: christophe.cerin@orange.fr DATA 354 End data with . Date: Fri, 3 May 2019 08:39:58 +0800 (CST) From: christophe.cerin@lipn.univ-paris13.fr To: christophe.cerin@orange.fr Message-ID: <2075203460.0.1556843998941@[192.168.125.115]> Subject: JavaMail APIs Test MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit This is a message body. Here's the second line. . 250 2.0.0 Ok: queued as 19E2B2017F5F8F DEBUG SMTP: message successfully delivered to mail server QUIT 221 2.0.0 Bye MacBook-de-Christophe:javamail-samples christophecerin$
Special special note: to configure your Postfix sender (on a Mac) please have a look to https://benjaminrojas.net/configuring-postfix-to-send-mail-from-mac-os-x-mountain-lion/. In this tutorial the local postfix program will contact Google mail and, after an authentication with Gmail, with your Google login and password, will send your emails. In that way, people will received your emails according to your Google email address. This method does not act as a plain mail server. Google mail is used as a relay. Your local machine has not been configured as a mail server by itself!
Christophe Cérin
christophe.cerin@univ-paris13.fr
April-May, 2019