What would be a better design to resolve host/port lookups
static image (e.g. car.png)
<table><tr><td><img src="http://<somehost>:<someport>/images/car.png" /></td></tr></table>
(e.g. lookup by id=123456 and fetched via a servlet from the database)
<table><tr><td><img src="http://<somehost>:<someport>/doc?id=123456"/></td></tr></table>
We generate snippets of HTML code (as me开发者_运维百科ntioned above) and store these in the database which is used to re-construct a user specific page in a dynamic fashion.
The problem in the above scenario is that somehost / someport is statically bound and stored in the database which I would like to avoid since If I have to upgrade to a different machine with a different IP all of the above calls will fail.
How to solve this in a generic fashion, so that I can bind at a later stage as for as host/port is concerned.
First of all, storing HTML in a database isn't a good idea. But ala.
As to the concrete problem, you could just define a HTML <base>
tag which would make all relative URLs in the document become relative of it.
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
...
<head>
<c:set var="r" value="${pageContext.request}" />
<base href="${fn:replace(r.requestURL, r.requestURI, '')}${r.contextPath}/" />
</head>
This way you can just use
<table><tr><td><img src="images/car.png" /></td></tr></table>
<table><tr><td><img src="doc?id=123456"/></td></tr></table>
Without the base you'll be dependent on the context path.
If you really want to parameterize them, then I would use java.text.MessageFormat
. You can use {0}
, {1}
, {2}
, etc as placeholders for the first, second, third, etc parameters.
<table><tr><td><img src="{0}/images/car.png" /></td></tr></table>
<table><tr><td><img src="{0}/doc?id=123456"/></td></tr></table>
You can grab the current host/port (and context!) from the HttpServletRequest
as follows:
HttpServletRequest r = getItSomehow();
String base = r.getRequestURL().toString().replace(r.getRequestURI(), "") + r.getContextPath();
You can format the HTML from DB as follows:
String html = getItSomehow();
String formatted = MessageFormat.format(html, base);
And then display that in the JSP. You could even wrap this in a custom EL function. Even more, some MVC frameworks like JSF have also tags which uses MessageFormat
under the covers. E.g.
<h:outputFormat value="#{bean.html}" escape="false">
<f:param value="#{bean.base}" />
</h:outputFormat>
Why don't you use this way? This should work
static image (e.g. car.png)
<table><tr><td><img src="images/car.png" /></td></tr></table>
(e.g. lookup by id=123456 and fetched via a servlet from the database)
<table><tr><td><img src="/doc?id=123456"/></td></tr></table>
You can try
<img src="http://<%=request.getServerName()%>:request.getServerPort()/images/car.png" />
Check out the Java docs
Alternatively, you can try
<% @page import="java.net.InetAddress" %>
<%
InetAddress ia = InetAddress.getLocalHost();
String hostName = ia.getHostName();
%>
The basic Problem is that you have specific text in your database and you need to be able to replace certain parts of it at some point in time. That leaves you with no other option other than placeholders.
You can do two things now:
a) Store your html in the db with placeholders:
<table><tr><td><img src="http://%1:%2/images/car.png" /></td></tr></table>
and replace the placeholders using
String.format(strHtmlFromDatabase, strHost, strPort);
Cons to this is that you replace the string every time (at every request). So you should cache it in memory, or:
b) do the same as a) and use a second database table or even files to store the "final" (fully replaced) version of your html strings. Then write a small generator script that takes the spaceholder-version from a), fills in the values and then stores it in the table/file from b). Your app then needs to use the final strings from this table/file. You only need to run this script everytime host and port change.
You will need to refactor your optimization to obtain your goal without cheap hacks.
You should store the HTML generator inputs in the database instead of the outputs, it is hardly more efficient to transfer text from a database than generating it on the application server side. In fact, all database access sucks and should be avoided.
Rather create a proper cache on the application server side. For maximum performance on page composition, reduce your common text elements into byte arrays (hack an XML serializer if you want) and use a per-thread writer to apply the 'somehost', 'someport' and 'id'.
This will give you several hundred hits per second if no database access is necessary. If you manage to construct and populate your caches well (divide and conquer), by hashing your input requests, you probably will get more than 2000 hits per seconds, if caches are hit, on a normal server.
Optimize further by using caches like Varnish.
There's a couple of ways of achieving this, and it kind of depends on what kind of HTML these URIs need to be extracted from. If you are only dealing with images, links, stylesheets, and other HTML specific imports then you could try an approach based on DNS redirect. I'm going to assume that you have access to some extra resources, specifically a web server or servlet container, and have the ability to control the DNS to that server.
So first off parse your document on creation to extract all referenced host names (or if it is just specific hostnames, look for them). Then use those hostnames (and ports if specified) as a key to a database lookup. Converting your hostname into a unique code. If you don't find an an entry for that hostname, create one and add it. Basically convert your hostname into a representative code. Now for some DNS trickery. take your unqiuely generated code and append it to some DNS that has been set up to point to your aforementioned web server. To do this you'll need to set up DNS wildcards. So to take an example. Say if your source content looks like:
<p>Why is it that all my baking ends up on <a href="http://cakewrecks.blogspot.com/p/faq.html">the internet</a></p>
You extract the hostname cakewrecks.blogspot.com
. Look it up in your conversion table and convert it into a representative code, say 11ag3
. This then gets appended to the web server hostname e.g. 11ag3.content.mycompany.com
Now you take your new hostname and insert that back into your content.
<p>Why is it that all my baking ends up on <a href="http://11ag3.content.mycompany.com/p/faq.html">the internet</a></p>
Next you'll need to write a servlet (or potentially some other dynamic code). What this should do is intercept any incoming HTTP request (all your requests from your content should now end up at this servlet rather than where they originally went). So the servlet receives a request for
http://11ag3.content.mycompany.com/p/faq.html
It extracts the hostname 11ag3.content.mycompany.com
, and from that the unique code 11ag3
. It now does the inverse of what we did at creation of the document and looks up the original hostname, and puts it back into the request, hence reconstructing http://cakewrecks.blogspot.com/p/faq.html
. It now responds to the request with an HTTP 300 state code (probably 307, temporary redirect) along with the reconstructed URL.
The big advantage here is that you now have a database table that contains all your hostnames. If you want to change where some content is served from you can just update the appropriate entry with the new hostname. Additionally you can avoid the overhead of templating every page each time you serve it.
The main problem with this approach may be flash, which has a complicated security model. You might be able to get it working by delving into the complex world of cross domain policies though.
This solution is practically what everyone else has commented on, but this is a more elegant way of doing replacements, and its what I used in an application.
Pretty much you have parameters stored in your database that are dynamic. For example I could see [somehost] changing even when you are in a development mode vs a production mode. What I created to resolve this is a class that mass replaces variables for me, I call this class Replacer (code below).
I suggest you store [somehost] and all your [...] parameters in your database as parameters such as: [x], [y], [z]. If you can place x, y z, in an enum and save their ordinal number in the database, such as [1], [2], [3] in your database, then you will saving lots of space on your db drives.
Later create a Properties object that statically creates an populates a static Replacer, and run the replacer on all the data that comes out of your database.
public class Replacer {
private final Map<String, Object> replacements = new HashMap<String, Object>();
public Replacer () {
replacements.put("''", "\"");
}
public void addReplacement (String replaceWhat, Object replaceWith) {
replacements.put(replaceWhat, replaceWith);
}
public String replace ( Object contentToReplace ) {
String output = contentToReplace.toString();
for (String replacement : replacements.keySet() ) {
output = output.replace(replacement, replacements.get(replacement).toString() );
}
return output;
}
public static void main (String[] args) throws Exception {
testReplaceTwoSingleQuote();
}
public static void testReplaceTwoSingleQuote () throws Exception {
Replacer rep = new Replacer();
assert rep.replace( "And Mary said, ''Hello Bob''. ").equals("And Mary said, \"Hello Bob\".");
}
}
精彩评论