A servlet can find out much about the server in which it is executing. It can learn the hostname, listening port, and server software, among other things. A servlet can display this information to a client, use it to customize its behavior based on a particular server package, or even use it to explicitly restrict the machines on which the servlet will run.
There are four methods that a servlet can use to learn about its
server: two that are called using the
ServletRequest
object passed to the servlet and
two that are called from the ServletContext
object
in which the servlet is executing. A servlet can get the name of the
server and the
port number for a particular request
with getServerName()
and
getServerPort()
, respectively:
public String ServletRequest.getServerName() public int ServletRequest.getServerPort()
These methods are attributes of ServletRequest
because the values can change for different requests if the server
has more than one name (a technique called
virtual
hosting). The returned name might be something like
"www.servlets.com"
while the returned port might
be something like "8080"
.
The
getServerInfo()
and
getAttribute()
methods of ServletContext
provide information
about the server software and its attributes:
public String ServletContext.getServerInfo() public Object ServletContext.getAttribute(String name)
getServerInfo()
returns the name and version of
the server software, separated by a slash. The string returned might
be something like "JavaWebServer/1.1.1"
.
getAttribute()
returns the value of the named
server attribute as an Object
or
null
if the attribute does not exist. The
attributes are server-dependent. You can think of this method as a
back door through which a servlet can get extra information about its
server. Attribute names should follow the same
convention as package names. The
package names
java.*
and javax.*
are reserved for use by the Java Software division of Sun
Microsystems (formerly known as JavaSoft), and
com.sun.*
is reserved for use by Sun Microsystems. See your server’s
documentation for a list of its attributes. Because these methods are
attributes of ServletContext
in which the servlet
is executing, you have to call them through that object:
String serverInfo = getServletContext().getServerInfo();
The most straightforward use of information about the server is an “About This Server” servlet, as shown in Example 4.3.
Example 4-3. Snooping the server
import java.io.*; import java.util.*; import javax.servlet.*; public class ServerSnoop extends GenericServlet { public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); PrintWriter out = res.getWriter(); out.println("req.getServerName(): " + req.getServerName()); out.println("req.getServerPort(): " + req.getServerPort()); out.println("getServletContext().getServerInfo(): " + getServletContext().getServerInfo()); out.println("getServerInfo() name: " + getServerInfoName(getServletContext().getServerInfo())); out.println("getServerInfo() version: " + getServerInfoVersion(getServletContext().getServerInfo())); out.println("getServletContext().getAttribute(\"attribute\"): " + getServletContext().getAttribute("attribute")); } private String getServerInfoName(String serverInfo) { int slash = serverInfo.indexOf('/'); if (slash == -1) return serverInfo; else return serverInfo.substring(0, slash); } private String getServerInfoVersion(String serverInfo) { int slash = serverInfo.indexOf('/'); if (slash == -1) return null; else return serverInfo.substring(slash + 1); } }
This servlet also directly subclasses
GenericServlet
, demonstrating that all the
information about a server is available to servlets of any type. The
servlet outputs simple raw text. When accessed, this servlet prints
something like:
req.getServerName(): localhost req.getServerPort(): 8080 getServletContext().getServerInfo(): JavaWebServer/1.1.1 getServerInfo() name: JavaWebServer getServerInfo() version: 1.1.1 getServletContext().getAttribute("attribute"): null
Unfortunately, there is no server-independent way to determine the
server’s root directory,
referred to in this book as server_root
.
However, some servers—including the Java Web Server—save
the server’s root directory name in the
server.root
system property, where it can be
retrieved using System. getProperty("server.root")
.
This server information can be put to more productive uses. Let’s assume you’ve written a servlet and you don’t want it running just anywhere. Perhaps you want to sell it and, to limit the chance of unauthorized copying, you want to lock the servlet to your customer’s machine with a software license. Or, alternatively, you’ve written a license generator as a servlet and want to make sure it works only behind your firewall. This can be done relatively easily because a servlet has instant access to the information about its server.
Example 4.4 shows a servlet that locks itself to a particular server IP address and port number. It requires an init parameter key that is appropriate for its server IP address and port before it unlocks itself and handles a request. If it does not receive the appropriate key, it refuses to continue. The algorithm used to map the key to the IP address and port (and vice-versa) must be secure.
Example 4-4. A servlet locked to a server
import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; public class KeyedServerLock extends GenericServlet { // This servlet has no class or instance variables // associated with the locking, so as to simplify // synchronization issues. public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); PrintWriter out = res.getWriter(); // The piracy check shouldn't be done in init // because name/port are part of request. String key = getInitParameter("key"); String host = req.getServerName(); int port = req.getServerPort(); // Check if the init parameter "key" unlocks this server. if (! keyFitsServer(key, host, port)) { // Explain, condemn, threaten, etc. out.println("Pirated!"); } else { // Give 'em the goods out.println("valid"); // etc... } } // This method contains the algorithm used to match a key with // a server host and port. This example implementation is extremely // weak and should not be used by commercial sites. // private boolean keyFitsServer(String key, String host, int port) { if (key == null) return false; long numericKey = 0; try { numericKey = Long.parseLong(key); } catch (NumberFormatException e) { return false; } // The key must be a 64-bit number equal to the logical not (~) // of the 32-bit IP address concatenated with the 32-bit port number. byte hostIP[]; try { hostIP = InetAddress.getByName(host).getAddress(); } catch (UnknownHostException e) { return false; } // Get the 32-bit IP address long servercode = 0; for (int i = 0; i < 4; i++) { servercode <<= 8; servercode |= (hostIP[i] & 255); } // Concatentate the 32-bit port number servercode <<= 32; servercode |= port; // Logical not long accesscode = ~numericKey; // The moment of truth: Does the key match? return (servercode == accesscode); } }
This servlet refuses to perform unless given the correct key. To
really make it secure, however, the simple
keyFitsServer()
logic should be replaced with a
strong algorithm and the whole servlet should be run through an
obfuscator to
prevent decompiling. Example 4.8 later in this
chapter provides the code used to generate keys. If you try this
servlet yourself, it’s best if you access the server with its
actual name, rather than localhost, so the
servlet can determine the web server’s true name and IP
address.
Get Java Servlet Programming now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.