This article describe web application technologies from the first 10 years of web applications.
The content of this article is not intended to be used for active development.
The content of this article describe obsolete technologies. Reasons to read about obsolete technologies include:
The very first web applications were similar to traditional console applications writing text (or text with ANSI escape sequences) to a terminal. They just wrote HTML out.
Example:
print("<html>") ... print("</html>")
CGI (Common Gateway Interface) is a standard for communication between a web server and an external script/program.
It was defined in 1993.
In theory any language can be used for CGI scripts. But by far the most common used language was Perl. When native code was needed then C was often used.
The CGI standard specifies:
Besides the general problem of low-productivity due to explicit outputting HTML then CGI also comes with a performance problem as the CGI script is run in an external process (that is fine for low volume but does not work for high volume).
Some Perl CGI scripts are still used, but most CGI usage was replaced with ASP/PHP/JSP when they came out in the late 90’s.
I will show a C example as I am really bad at Perl.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql.h>
static char *parse(char *line, char *marker)
{
char *p = strstr(line, marker);
if(p == NULL)
{
return NULL;
}
else
{
p = p + strlen(marker);
char *res = malloc(1024);
char *p2 = p;
while(*p2 && *p2 != '&') p2++;
memcpy(res, p, p2 - p);
res[p2 - p] = 0;
for(int i = 0; i < strlen(res); i++) if(res[i] == '+') res[i] = ' ';
return res;
}
}
int main(int argc, char *argv[])
{
char *f1 = NULL;
char *f2 = NULL;
char *f3 = NULL;
char line[1024];
if(fgets(line, sizeof(line), stdin))
{
f1 = parse(line, "F1=");
f2 = parse(line, "F2=");
f3 = parse(line, "F3=");
}
printf("Status: 200 OK\n");
printf("Content-Type: text/html\n");
printf("\n");
printf("<html>\n");
printf("<head>\n");
printf("<title>CGI - C</title>\n");
printf("</head>\n");
printf("<body>\n");
printf("<h1>CGI - C</h1>\n");
printf("<h2>Show data:</h2>\n");
printf("<table border=\"1\">\n");
printf(" <tr>\n");
printf(" <th>F1</th>\n");
printf(" <th>F2</th>\n");
printf(" <th>F3</th>\n");
printf(" </tr>\n");
int stat;
MYSQL *con = mysql_init(NULL);
con = mysql_real_connect(con, "localhost", "root", "hemmeligt", "Test", 3306, NULL, 0);
if(strcmp(getenv("REQUEST_METHOD"), "POST") == 0 && f1 != NULL && strlen(f1) > 0)
{
MYSQL_STMT *stmt = mysql_stmt_init(con);
char *sqlstr = "INSERT INTO t1demo VALUES(?,?,?)";
stat = mysql_stmt_prepare(stmt, sqlstr, strlen(sqlstr));
int xf1 = atoi(f1);
MYSQL_BIND in[3];
memset(in, 0, sizeof(in));
in[0].buffer_type = MYSQL_TYPE_LONG;
in[0].buffer = &xf1;
in[0].buffer_length = sizeof(long);
in[0].is_null= 0;
in[1].buffer_type = MYSQL_TYPE_STRING;
in[1].buffer = f2;
in[1].buffer_length = strlen(f2);
in[2].buffer_type = MYSQL_TYPE_STRING;
in[2].buffer = f3;
in[2].buffer_length = strlen(f3);
stat = mysql_stmt_bind_param(stmt, in);
stat = mysql_stmt_execute(stmt);
}
stat = mysql_query(con, "SELECT f1,f2,f3 FROM t1demo");
MYSQL_RES *result = mysql_store_result(con);
MYSQL_ROW row;
while(row = mysql_fetch_row(result))
{
printf(" <tr>\n");
printf(" <td>%s</td>\n", row[0]);
printf(" <td>%s</td>\n", row[1]);
printf(" <td>%s</td>\n", row[2]);
printf(" </tr>\n");
}
mysql_free_result(result);
mysql_close(con);
printf("</table>\n");
printf("<h2>Add data:</h2>\n");
printf("<form method=\"POST\" name=\"AddForm\">\n");
printf("F1: <input type=\"text\" name=\"F1\">\n");
printf("<br>\n");
printf("F2: <input type=\"text\" name=\"F2\">\n");
printf("<br>\n");
printf("F3: <select name=\"F3\">\n");
printf(" <option></option>\n");
printf(" <option>Verified</option>\n");
printf(" <option>Not verified</option>\n");
printf(" </select>\n");
printf("<br>\n");
printf("<input type=\"submit\" name=\"Save\" value=\"Add\">\n");
printf("</form>\n");
printf("</body>\n");
printf("</html>\n");
return 0;
}
The result looks like:
Java could also be used for CGI scripts, but Sun came up with a new technology of Java Servlets running in a Java Servlet Engine (or Java Servlet Container). In recent years it has been renamed to of Jakarta Servlets running in a Jakarta Servlet Engine (or Jakarta Servlet Container).
First version came out in 1996.
Servlets were more efficient than CGI, because all request was processed by the same JVM process. But productivity was bad compared to Microsoft ASP.
So in 1999 SUN came up with JSP and original servlet usage died out.
Servlet are still widely used in the Java web world as controllers (processing request). And even though HTML output is done via JSP, then JSP actually get translated to servlets!
package demo;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class IndexServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.println("<html>");
pw.println("<head>");
pw.println("<title>Servlet</title>");
pw.println("</head>");
pw.println("<body>");
pw.println("<h1>Servlet</h1>");
pw.println("<h2>Show data:</h2>");
pw.println("<table border=\"1\">");
pw.println(" <tr>");
pw.println(" <th>F1</th>");
pw.println(" <th>F2</th>");
pw.println(" <th>F3</th>");
pw.println(" </tr>");
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/Test", "root", "hemmeligt");
if(req.getMethod().equals("POST") && req.getParameter("F1") != null && !req.getParameter("F1").equals("")) {
PreparedStatement pstmt = con.prepareStatement("INSERT INTO t1demo VALUES(?,?,?)");
pstmt.setInt(1, Integer.parseInt(req.getParameter("F1")));
pstmt.setString(2, req.getParameter("F2"));
pstmt.setString(3, req.getParameter("F3"));
pstmt.executeUpdate();
}
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT f1,f2,f3 FROM t1demo");
while(rs.next()) {
pw.println(" <tr>");
pw.println(" <td>" + rs.getInt(1) + "</td>");
pw.println(" <td>" + rs.getString(2) + "</td>");
pw.println(" <td>" + rs.getString(3) + "</td>");
pw.println(" </tr>");
}
rs.close();
stmt.close();
con.close();
} catch(Exception ex) {
throw new ServletException(ex);
}
pw.println("</table>");
pw.println("<h2>Add data:</h2>");
pw.println("<form method=\"POST\" name=\"AddForm\">");
pw.println("F1: <input type=\"text\" name=\"F1\">");
pw.println("<br>");
pw.println("F2: <input type=\"text\" name=\"F2\">");
pw.println("<br>");
pw.println("F3: <select name=\"F3\">");
pw.println(" <option></option>");
pw.println(" <option>Verified</option>");
pw.println(" <option>Not verified</option>");
pw.println(" </select>");
pw.println("<br>");
pw.println("<input type=\"submit\" name=\"Save\" value=\"Add\">");
pw.println("</form>");
pw.println("</body>");
pw.println("</html>");
pw.close();
}
}
The result looks like:
Having UI design embedded in print statements in code is not productive, so the web world started looking for alternative approaches.
Most actually ended up with a relatve similar approach. Mixing blocks of HTML with blocks of code. Typical:
HTML <% code %> HTML <% code %> HTML
ASP (Active Server Pages) from Microsoft:
Microsoft supported VBScript and JScript (Microsoft JavaScript) as scripting languages, but third parties supported Perl, Python etc..
Microsoft supported ASP on IIS/Windows, but third parties supported ASP on Apache/*nix.
ASP could use COM components (like ADODB for database access). For special needs one could write a custom COM component in C++ or VB6.
ASP was a huge success. Everybody from large companies to the hobbyist web developer started using ASP.
Often the ASP was not pretty though. ASP was often done spaghetti style.
In 2002 Microsoft replaced ASP with ASP.NET and despite the name that gives associations to ASP on .NET, then ASP.NET was a totally different technology.
Companies migrated from ASP to ASP.NET and were probably happy with the change – ASP.NET encouraged a way more structured approach.
The hobbyists found ASP.NET too difficult (and web hosting too expensive) and switched from ASP to PHP.
During the 00’s ASP dropped from the dominant web technology to a rare and obsolete technology.
Note though that recent IIS and Windows still support ASP. It is not enabled by default, but it can be enabled.
<html>
<head>
<title>ASP Classic</title>
</head>
<body>
<h1>ASP Classic</h1>
<h2>Show data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<%
adParamInput = 1
adInteger = 3
adVarChar = 200
Set con = Server.CreateObject("ADODB.Connection")
con.Open "Driver={MySQL ODBC 8.0 ANSI Driver};Server=localhost;Database=Test;User Id=root;Password=hemmeligt;"
If (Request.ServerVariables("REQUEST_METHOD") = "POST") And Not(Request.Form("F1") Is Nothing) And (Request.Form("F1") <> "") Then
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO t1demo VALUES(?,?,?)"
cmd.Parameters.Append(cmd.CreateParameter("@f1", adInteger, adParamInput))
cmd.Parameters.Append(cmd.CreateParameter("@f2", adVarChar, adParamInput, 32))
cmd.Parameters.Append(cmd.CreateParameter("@f3", adVarChar, adParamInput, 32))
cmd.Parameters("@f1") = Request.Form("F1")
cmd.Parameters("@f2") = Request.Form("F2")
cmd.Parameters("@f3") = Request.Form("F3")
cmd.Execute
Set cmd = Nothing
End If
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open "SELECT f1,f2,f3 FROM t1demo", con
Do While Not rs.EOF
%>
<tr>
<td><%= rs("F1") %></td>
<td><%= rs("F2") %></td>
<td><%= rs("F3") %></td>
</tr>
<%
rs.MoveNext
Loop
Set rs = Nothing
Set con = Nothing
%>
</table>
<h2>Add data:</h2>
<form method="POST" name="AddForm">
F1: <input type="text" name="F1">
<br>
F2: <input type="text" name="F2">
<br>
F3: <select name="F3">
<option></option>
<option>Verified</option>
<option>Not verified</option>
</select>
<br>
<input type="submit" name="Save" value="Add">
</form>
</body>
</html>
The result looks like:
PHP was invented by Rasmus Lerdorf back in 1995.
The first years PHP was a niche technology compared to Perl CGI, ASP, ColdFusion etc., but during the 00’s it evolved from a niche language to the dominant web application language.
Today PHP is used by many companies from small startups to FaceBook and by millions of hobbyist web developers.
Original PHP web applications was often rather unstructured. But mid 00’s the professional side of PHP (companies) started switching I MVC frameworks (ZF, Symphony, CodeIgniter, CakePHP, Lareval etc.). The hobbyist web developers to large extent continues with the unstructured approach.
PHP can be run in different ways (different execution model):
Obviously high volume requires something else than CGI.
<html>
<head>
<title>PHP Old Style</title>
</head>
<body>
<h1>PHP Old Style</h1>
<h2>Show data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<?php
$con = mysqli_connect('localhost', 'root', 'hemmeligt', 'Test');
if(($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['F1']) && $_POST['F1'] <> '') {
$stmt = mysqli_prepare($con, 'INSERT INTO t1demo VALUES(?,?,?)');
mysqli_stmt_bind_param($stmt, 'iss', $_POST['F1'], $_POST['F2'], $_POST['F3']);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
}
$rs = mysqli_query($con, 'SELECT f1,f2,f3 FROM t1demo');
while($row = mysqli_fetch_array($rs, MYSQLI_ASSOC)) {
?>
<tr>
<td><?php echo $row['f1']; ?></td>
<td><?php echo $row['f2']; ?></td>
<td><?php echo $row['f3']; ?></td>
</tr>
<?php
}
mysqli_close($con);
?>
</table>
<h2>Add data:</h2>
<form method="POST" name="AddForm">
F1: <input type="text" name="F1">
<br>
F2: <input type="text" name="F2">
<br>
F3: <select name="F3">
<option></option>
<option>Verified</option>
<option>Not verified</option>
</select>
<br>
<input type="submit" name="Save" value="Add">
</form>
</body>
</html>
The result looks like:
JSP (Java Server Pages) was invented by Sun in 1999. In recent years it has been renamed to Jakarta Server pages.
It was created to match ASP. And it did.
But it was only used similar to ASP for a few years.
Around 2000-2001 MVC became the way for Java web applications. The Struts 1.x framework was created in 2000 and WebWork (later Struts 2.x) was created in 2002.
And JSP got delegated to be view technology in MVC frameworks (Struts 1.x, Struts 2.x, Stripes, Spring MVC etc.) and JSF.
And later it even got replaced as view technology by Facelets and Thymeleaf.
For more information about JSP see here.
<%@ page import="java.sql.*" %>
<html>
<head>
<title>JSP Old Style</title>
</head>
<body>
<h1>JSP Old Style</h1>
<h2>Show data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<%
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/Test", "root", "hemmeligt");
if(request.getMethod().equals("POST") && request.getParameter("F1") != null && !request.getParameter("F1").equals("")) {
PreparedStatement pstmt = con.prepareStatement("INSERT INTO t1demo VALUES(?,?,?)");
pstmt.setInt(1, Integer.parseInt(request.getParameter("F1")));
pstmt.setString(2, request.getParameter("F2"));
pstmt.setString(3, request.getParameter("F3"));
pstmt.executeUpdate();
}
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT f1,f2,f3 FROM t1demo");
while(rs.next()) {
%>
<tr>
<td><%= rs.getInt(1) %></td>
<td><%= rs.getString(2) %></td>
<td><%= rs.getString(3) %></td>
</tr>
<%
}
rs.close();
stmt.close();
con.close();
%>
</table>
<h2>Add data:</h2>
<form method="POST" name="AddForm">
F1: <input type="text" name="F1">
<br>
F2: <input type="text" name="F2">
<br>
F3: <select name="F3">
<option></option>
<option>Verified</option>
<option>Not verified</option>
</select>
<br>
<input type="submit" name="Save" value="Add">
</form>
</body>
</html>
The result looks like:
The web world soon realized that mixing HTML and code lend itself towards spagetti code.
Long term the solution for that problem ended up being separating the UI in view and controller/backing.
But for a few years they tried replacing code blocks with special tags that sort of extended the static HTML with dynamic behavior.
Instead of:
<% for(int i = 0; i < 10; i++) { if(i % 2 == 0) { %> <%=i%> <% } } %>
then (fictive syntax):
<for var="i" start="0" end="9"> <if cond="i % 2 == 0")> <%=i%> </if> </for>
ColdFusion (CF) was created in 1995 by Allaire. Macromedia acquired Allaire in 2001. Adobe acquired Macromedia in 2005.
In many ways CF was far ahead of its time and in the late 90’s and early 00’s it was widely used. Among the big companies using it was MyFaces.
But it faded away during the 00's. Microsoft pushed ASP.NET. Sun/BEA/Oracle/IBM/SAP pushed Java EE technologies. PHP became very popular. These competing technologies caugth up and CF lost its advantage. And the owner Adobe pushed Flash/Flex for client side more than CF for server side.
Today it is rare. Adobe is still maintaining it, but it has become niche.
Technically one need to distinguish between:
Distinguishing is important because there are actually other server implementations than Allaire/Macromedia/Adobe.
The servers can be based on different platforms. But Java EE platform is probably the most widely used platform.
For this demo I have been using Open BlueDragon. On Java EE (Tomcat).
Disclaimer: I have no experience with CF so the demo may not be reflective of CF practice today or CF practice in the late 90’s.
<html>
<head>
<title>ColdFusion</title>
</head>
<body>
<h1>ColdFusion</h1>
<h2>Show data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<cfif (#cgi.request_method# == "POST") and isdefined("Form.F1") and (#Form.F1# != "")>
<cfquery name="InsertOne" datasource="mysql">
INSERT INTO t1demo VALUES(<cfqueryparam value="#Form.F1#" cfsqltype="CF_SQL_INTEGER">,
<cfqueryparam value="#Form.F2#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#Form.F3#" cfsqltype="CF_SQL_VARCHAR">)
</cfquery>
</cfif>
<cfquery name="SelectAll" datasource="mysql" debug="Yes">
SELECT f1,f2,f3 FROM t1demo
</cfquery>
<cfoutput query="SelectAll">
<tr>
<td>#f1#</td>
<td>#f2#</td>
<td>#f3#</td>
</tr>
</cfoutput>
</table>
<h2>Add data:</h2>
<form method="POST" name="AddForm">
F1: <input type="text" name="F1">
<br>
F2: <input type="text" name="F2">
<br>
F3: <select name="F3">
<option></option>
<option>Verified</option>
<option>Not verified</option>
</select>
<br>
<input type="submit" name="Save" value="Add">
</form>
</body>
</html>
The result looks like:
JSTL (Java Standard Tag Library) was created in 2002 to provide a standard set of tag libraries for JSP. In recent years it has been renamed to Jakarta Standard Tag Library.
It is perfectly possible to create a web application using only JSP and JSTL, but nobody does that. When JSTL arrived the Java web world had already switched to MVC frameworks (and later JSF).
So as core of web application JSTL is irrelevant. But JSTL is widely used in JSP views (Struts 1.x, Stripes,\ Spring MVC, JSF etc.).
For more information about JSTL see here.
Here is an example of using JSP with JSTL in "ColdFusion style".
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
<html>
<head>
<title>JSTL Weird</title>
<%
// hack
request.setAttribute("method", request.getMethod());
%>
</head>
<body>
<h1>JSTL Weird</h1>
<h2>Show data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<sql:setDataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/Test" user="root" password="hemmeligt" var="con"/>
<c:if test="${requestScope.method == 'POST' && param.F1 != null && param.F1 != ''}">
<sql:update dataSource="${con}" sql="INSERT INTO t1demo VALUES(?,?,?)">
<sql:param value="${param.F1}"/>
<sql:param value="${param.F2}"/>
<sql:param value="${param.F3}"/>
</sql:update>
</c:if>
<sql:query dataSource="${con}" sql="SELECT f1,f2,f3 FROM t1demo" var="rs"/>
<c:forEach items="${rs.rows}" var="row">
<tr>
<td><c:out value="${row.f1}"/></td>
<td><c:out value="${row.f2}"/></td>
<td><c:out value="${row.f3}"/></td>
</tr>
</c:forEach>
</table>
<h2>Add data:</h2>
<form method="POST" name="AddForm">
F1: <input type="text" name="F1">
<br>
F2: <input type="text" name="F2">
<br>
F3: <select name="F3">
<option></option>
<option>Verified</option>
<option>Not verified</option>
</select>
<br>
<input type="submit" name="Save" value="Add">
</form>
</body>
</html>
The result looks like:
The world moved on and created web application frameworks to allow web applications to be done in a structured way. Specifically with a clear separation between UI layout and code logic.
See:
Version | Date | Description |
---|---|---|
1.0 | December 20th 2023 | Initial version |
See list of all articles here
Please send comments to Arne Vajhøj