Good evening,
I've created a patch to allow to OpenEMM 2013 the support of a proxy server set by JAVA_OPTS or CATALINA_OPTS.
I've replaced library "commons-httpclient-3.1.jar" with new HttpClient 4.2 ("httpclient-4.2.5.jar" and "httpcore-4.2.4.jar"), and I've modified source files "LinkcheckWorker.java", "MailingComponentImpl.java" and "VersionControlDaoImpl.java" as follows (I've also introduced further checks for links).
LinkcheckWorker.java
package org.agnitas.service;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.net.UnknownHostException;
import java.net.URISyntaxException;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.net.InetAddress;
import org.apache.commons.validator.UrlValidator;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.SystemDefaultHttpClient;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.log4j.Logger;
public class LinkcheckWorker implements Runnable {
private static final transient Logger logger = Logger.getLogger( LinkcheckWorker.class);
int timeout = 0; // default, no timeout.
// contains a list with links. if the given URL is valid and reachable,
// the given link will be removed from the link-list. Vector because its thread-save.
Vector<String> links;
String linkToCheck = null;
/**
* @param timeout
* @param links
* @param linkToCheck
*
*/
public LinkcheckWorker(int timeout, Vector<String> links, String linkToCheck) {
this.timeout = timeout;
this.links = links;
this.linkToCheck = linkToCheck;
}
@Override
public void run() {
boolean failure = false;
boolean dynamic = false;
// check if the link has dynamic content.
dynamic = dynamicLinkCheck();
if (dynamic) {
if( logger.isInfoEnabled()) {
logger.info( "Link is dynamic - no checking for: " + linkToCheck);
}
failure = false;
} else {
failure = netBasedTest();
}
// remove working link from failure-list
if (!failure) {
if( logger.isInfoEnabled()) {
logger.info( "Link is working: " + linkToCheck);
}
links.remove(linkToCheck);
}
}
/**
* this method checks, if the given link contains dynamic content like ##AGNUID##
* if thats the case, we wont check the link anymore.
* If failure = true, the link is dynamic and has to be removed. "failure" here means dynamic.
* @return
*/
private boolean dynamicLinkCheck() {
boolean dynamic = false;
Pattern pattern = Pattern.compile ("##([^#]+)##");
Matcher aMatch = pattern.matcher(linkToCheck);
if (aMatch.find() ) {
// found dynamic content
return true;
}
return dynamic;
}
/**
* this method checks, if the given link works. It gets a real connection
* to the given server and tries to fetch some answers.
* @param failure
* @return
*/
private boolean netBasedTest() {
boolean failure = false;
URL url;
URI uri;
HttpGet get = null;
try {
if( logger.isInfoEnabled()) {
logger.info( "Checking link: " + linkToCheck);
}
String[] schemes = {"http","https"};
UrlValidator urlValidator = new UrlValidator(schemes);
if (!urlValidator.isValid(linkToCheck)) {
failure = true;
}
url = new URL(linkToCheck); // just for checking, we could use the plain String...
uri = url.toURI();
//Hostname verify
HttpHost validatedhost = URIUtils.extractHost(uri);
if( validatedhost == null ) {
failure = true;
} else {
InetAddress.getByName(validatedhost.getHostName());
}
HttpClient client = new SystemDefaultHttpClient();
// create get-method.
get = new HttpGet(url.toString());
get.getParams().setParameter("http.socket.timeout", new Integer(timeout));
get.getParams().setParameter("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 (.NET CLR 3.5.30729)");
// lets start working...
HttpResponse response = client.execute(get);
// check response code
int statuscode = response.getStatusLine().getStatusCode();
if (statuscode != HttpURLConnection.HTTP_OK && statuscode != HttpURLConnection.HTTP_ACCEPTED && statuscode != HttpURLConnection.HTTP_CREATED && statuscode != HttpURLConnection.HTTP_MOVED_PERM && statuscode != HttpURLConnection.HTTP_MOVED_TEMP) {
failure = true;
}
}
catch (MalformedURLException e) {
if( logger.isInfoEnabled()) {
logger.info( "Link URL malformed: " + linkToCheck); // This is no "real error", this is a test result for the link. So we can log this at INFO leven
}
failure = true;
}
catch (URISyntaxException e) {
if( logger.isInfoEnabled()) {
logger.info( "Link URL malformed: " + linkToCheck); // This is no "real error", this is a test result for the link. So we can log this at INFO leven
}
failure = true;
}
catch (UnknownHostException e) {
if( logger.isInfoEnabled()) {
logger.info( "Unknown host: " + linkToCheck); // This is no "real error", this is a test result for the link. So we can log this at INFO leven
}
failure = true;
}
catch (IOException e1) {
logger.warn( "I/O error testing URL: " + linkToCheck, e1); // This is no "real error", this is a test result for the link. Since this could be any IO problem, let us report this at WARN level
// some other connection problem, but link was found, so don't add it to invalid links.
// invalidlinks.add(fullUrl);
}
finally
{
get.releaseConnection();
}
return failure;
}
}
MailingComponentImpl.java
/*********************************************************************************
* The contents of this file are subject to the Common Public Attribution
* License Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
http://www.openemm.org/cpal1.html. The License is based on the Mozilla
* Public License Version 1.1 but Sections 14 and 15 have been added to cover
* use of software over a computer network and provide for limited attribution
* for the Original Developer. In addition, Exhibit A has been modified to be
* consistent with Exhibit B.
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is OpenEMM.
* The Original Developer is the Initial Developer.
* The Initial Developer of the Original Code is AGNITAS AG. All portions of
* the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights
* Reserved.
*
* Contributor(s): AGNITAS AG.
********************************************************************************/
package org.agnitas.beans.impl;
/**
*
* @author mhe
* @version
*/
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.util.Date;
import javax.mail.internet.MimeUtility;
import org.agnitas.beans.MailingComponent;
import org.agnitas.util.AgnUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.impl.client.SystemDefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
public class MailingComponentImpl implements MailingComponent {
private static final long serialVersionUID = 6149094755767488656L;
private static final transient Logger logger = Logger.getLogger( MailingComponentImpl.class);
protected int id;
protected String mimeType;
protected String componentName;
protected String description;
protected int mailingID;
protected int companyID;
protected int type;
protected StringBuffer emmBlock;
protected byte[] binaryBlock;
protected Date timestamp;
protected String link;
protected int urlID;
protected int targetID;
protected Date startDate;
protected Date endDate;
public static final int TYPE_FONT = 6;
public MailingComponentImpl() {
id = 0;
componentName = null;
mimeType = " ";
mailingID = 0;
companyID = 0;
type = TYPE_IMAGE;
emmBlock = null;
targetID = 0;
}
public void setComponentName(String cid) {
if (cid != null) {
componentName = cid;
} else {
componentName = "";
}
}
public void setType(int cid) {
type = cid;
if ((type != TYPE_IMAGE) && (type != TYPE_TEMPLATE)
&& (type != TYPE_ATTACHMENT)
&& (type != TYPE_PERSONALIZED_ATTACHMENT)
&& (type != TYPE_HOSTED_IMAGE) && (type != TYPE_FONT)
&& (type != TYPE_THUMBMAIL_IMAGE)
&& (type != TYPE_PREC_ATTACHMENT)) {
type = TYPE_IMAGE;
}
}
public void setMimeType(String cid) {
if (cid != null) {
mimeType = cid;
} else {
mimeType = "";
}
}
public void setId(int cid) {
id = cid;
}
public String getComponentName() {
if (componentName != null) {
return componentName;
}
return "";
}
public void setMailingID(int cid) {
mailingID = cid;
}
public void setCompanyID(int cid) {
companyID = cid;
}
public void setEmmBlock(String cid) {
if (cid != null) {
emmBlock = new StringBuffer(cid);
} else {
emmBlock = new StringBuffer();
}
}
public void setBinaryBlock(byte[] cid) {
binaryBlock = cid;
}
public boolean loadContentFromURL() {
boolean returnValue = true;
// return false;
if ((type != TYPE_IMAGE) && (type != TYPE_ATTACHMENT)) {
return false;
}
HttpClient httpClient = new SystemDefaultHttpClient();
HttpParams clientParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(clientParams, 5000);
String encodedURI = encodeURI(componentName);
HttpGet get = new HttpGet(encodedURI);
HttpParams httpParams = get.getParams();
httpParams.setParameter(ClientPNames.HANDLE_REDIRECTS, Boolean.TRUE);
get.setParams(httpParams);
try {
HttpResponse response = httpClient.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
response.getAllHeaders();
HttpEntity entity = response.getEntity();
if (entity != null) {
this.binaryBlock = EntityUtils.toByteArray(entity);
}
setEmmBlock(makeEMMBlock());
mimeType = response.getFirstHeader("Content-Type").getValue();
}
} catch (Exception e) {
logger.error("loadContentFromURL", e);
returnValue = false;
}
// lets clear connection
finally {
get.releaseConnection();
}
if( logger.isInfoEnabled())
logger.info("loadContentFromURL: loaded " + componentName);
return returnValue;
}
public String makeEMMBlock() {
ByteArrayOutputStream baos = null;
if (type == TYPE_TEMPLATE) {
try {
return new String(binaryBlock, "UTF8");
} catch (Exception e) {
logger.error("makeEMMBlock: encoding error", e);
return " ";
}
} else {
try {
baos = new ByteArrayOutputStream();
OutputStream dos = MimeUtility.encode(
new DataOutputStream(baos), "base64");
dos.write(binaryBlock);
dos.flush();
return baos.toString();
} catch (Exception e) {
return null;
}
}
}
public String getEmmBlock() {
return new String(emmBlock);
}
public int getId() {
return id;
}
public String getMimeType() {
return mimeType;
}
/**
* Getter for property targetID.
*
* @return Value of property targetID.
*/
public int getTargetID() {
return this.targetID;
}
/**
* Setter for property targetID.
*
* @param targetID
* New value of property targetID.
*/
public void setTargetID(int targetID) {
this.targetID = targetID;
}
/**
* Getter for property type.
*
* @return Value of property type.
*/
public int getType() {
return this.type;
}
/** Don't invoke this. Used by Hibernate only. */
public void setBinaryBlob(Blob imageBlob) {
this.binaryBlock = AgnUtils.BlobToByte(imageBlob);
}
/** Don't invoke this. Used by Hibernate only. */
public Blob getBinaryBlob() {
return Hibernate.createBlob(this.binaryBlock);
}
/**
* Getter for property binaryBlock.
*
* @return Value of property binaryBlock.
*
*/
public byte[] getBinaryBlock() {
return this.binaryBlock;
}
/**
* Getter for property mailingID.
*
* @return Value of property mailingID.
*/
public int getMailingID() {
return this.mailingID;
}
/**
* Getter for property companyID.
*
* @return Value of property companyID.
*/
public int getCompanyID() {
return this.companyID;
}
/**
* Getter for property timestamp.
*
* @return Value of property timestamp.
*/
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public int getUrlID() {
return urlID;
}
public void setUrlID(int urlID) {
this.urlID = urlID;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
/**
* This method encodes some parts of a URI. If in the given URI a "[", "]", "{" or "}" are found, they
* will be replaced by appropriate HEX-Identifiers.
* See here for more information:
*
http://www.ietf.org/rfc/rfc3986.txt
*
http://stackoverflow.com/questions/4056 ... ts-in-urls
*
http://www.blooberry.com/indexdot/html/ ... coding.htm
*
* @return "cleaned" URI
*/
private String encodeURI(String uri) {
// TODO Replace this version with a more generic approach. Now only one special
// case is fixed. This method should convert ALL special characters.
/*
* Note: Using a generic method to URL-encode a String may lead to another problem:
* The URLs found in the mailing may already be URL encoded (irrespective to some
* of the dirty things, that got common practice, like "{" or "}" in URLs), so we
* URL-encode an URL-encoded URI.
*
* This method should simply "clean" the given URL/URI and do no further encoding.
* TODO: In this case, renaming of the method would be a great deal!
*/
uri = uri.replace("[", "%5B");
uri = uri.replace("]", "%5D");
uri = uri.replace("{", "%7B");
uri = uri.replace("}", "%7D");
return uri;
}
}
VersionControlDaoImpl.java
/*********************************************************************************
* The contents of this file are subject to the Common Public Attribution
* License Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
http://www.openemm.org/cpal1.html. The License is based on the Mozilla
* Public License Version 1.1 but Sections 14 and 15 have been added to cover
* use of software over a computer network and provide for limited attribution
* for the Original Developer. In addition, Exhibit A has been modified to be
* consistent with Exhibit B.
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is OpenEMM.
* The Original Developer is the Initial Developer.
* The Initial Developer of the Original Code is AGNITAS AG. All portions of
* the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights
* Reserved.
*
* Contributor(s): AGNITAS AG.
********************************************************************************/
package org.agnitas.dao.impl;
import java.util.ArrayList;
import java.util.List;
import java.net.URI;
import java.net.URISyntaxException;
import org.agnitas.beans.VersionObject;
import org.agnitas.beans.impl.VersionObjectImpl;
import org.agnitas.dao.VersionControlDao;
import org.agnitas.util.AgnUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.HttpClient;
//import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.SystemDefaultHttpClient;
import org.apache.log4j.Logger;
//import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
//import org.apache.http.client.utils.URLEncodedUtils;
//import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
public class VersionControlDaoImpl implements VersionControlDao {
private static final transient Logger logger = Logger.getLogger(VersionControlDaoImpl.class);
private static final int CONNECTION_TIMEOUT = 5000;
private static final String URL = AgnUtils.getDefaultValue( "system.updateserver" ) + "/version/" + AgnUtils.getCurrentVersion() + "/current_version.html";
private static final String VERSION_KEY = "currentVersion";
private static final long MAX_AGE = 60 * 60 * 1000L; // one hour cache time
private static VersionObject versionObject = null;
private long lastRefresh = 0L;
/* (non-Javadoc)
* @see org.agnitas.dao.VersionControlDao#getServerVersion()
*/
@Override
public VersionObject getServerVersion(String currentVersion, String referrer ) {
checkRefresh( currentVersion, referrer );
return versionObject;
}
private void checkRefresh(String currentVersion, String referrer) {
if ( versionObject == null || System.currentTimeMillis() - lastRefresh > MAX_AGE ) {
String serverVersion = fetchServerVersion(currentVersion, referrer);
versionObject = new VersionObjectImpl( currentVersion, serverVersion );
lastRefresh = System.currentTimeMillis();
}
}
private String fetchServerVersion(String currentVersion, String referrer) {
HttpClient client = new SystemDefaultHttpClient();
HttpParams clientParams = client.getParams();
HttpConnectionParams.setConnectionTimeout(clientParams, CONNECTION_TIMEOUT);
String responseBody = null;
HttpGet get = null;
try{
URI uri = new URI(URL);
URIBuilder ub = new URIBuilder(uri.toASCIIString());
ub.addParameter(VERSION_KEY, currentVersion);
URI finaluri = ub.build();
//List <NameValuePair> queryparams = new ArrayList <NameValuePair>();
//queryparams.add(new BasicNameValuePair(VERSION_KEY, currentVersion));
//String completeurl = URL + URLEncodedUtils.format(queryparams, "UTF-8");
get = new HttpGet(finaluri);
get.setHeader( "referer", referrer );
HttpParams httpParams = get.getParams();
httpParams.setParameter(ClientPNames.HANDLE_REDIRECTS, Boolean.TRUE);
get.setParams(httpParams);
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
if (entity != null) {
responseBody = EntityUtils.toString(entity);
}
} catch (URISyntaxException he) {
logger.error( "Cannot build update URL '" + URL + "'", he);
} catch (Exception he) {
logger.error( "HTTP error connecting to '" + URL + "'", he);
}
//clean up the connection resources
get.releaseConnection();
return responseBody;
}
}
After recompiling, these actions are needed to install new files:
1. Stop OpenEMM
2. Remove the file "commons-httpclient-3.1.jar" in folders "/home/openemm/webapps/openemm/WEB-INF/lib/" and "/home/openemm-ws/webapps/openemm/WEB-INF/lib/"
3. Copy files "httpclient-4.2.5.jar" and "httpcore-4.2.4.jar" in folders "/home/openemm/webapps/openemm/WEB-INF/lib/" and "/home/openemm-ws/webapps/openemm-ws/WEB-INF/lib/"
4. Copy the file "LinkcheckWorker.class" in folders "/home/openemm/webapps/openemm/WEB-INF/classes/org/agnitas/service/" and "/home/openemm/webapps/openemm-ws/WEB-INF/classes/org/agnitas/service/"
5. Copy the file "MailingComponentImpl.class" in folders "/home/openemm//webapps/openemm/WEB-INF/classes/org/agnitas/beans/impl/" and "/home/openemm/webapps/openemm-ws/WEB-INF/classes/org/agnitas/beans/impl/"
6. Copy the file "VersionControlDaoImpl.class" in folders "/home/openemm/webapps/openemm/WEB-INF/classes/org/agnitas/dao/impl/" and "/home/openemm/webapps/openemm-ws/WEB-INF/classes/org/agnitas/dao/impl/"
7. Start OpenEMM
I hope that this informations may be useful: I've also the compiled classes, if needed.
Regards.
Carlo D'Ambrosio