commit 55ba0f4483e7151e15d637f332d2e5ac72bf0eb7 Author: YuCheng Hu Date: Wed Nov 24 13:04:26 2021 -0500 USRE-84 Client API project CVS source commit diff --git a/build.properties b/build.properties new file mode 100644 index 0000000..e7c76ca --- /dev/null +++ b/build.properties @@ -0,0 +1,11 @@ +# +# Properties file for commons build.xml +# +src=src +etc=src/etc +libsrc=src/lib +javadoc=src/javadoc + +build=target +classes=target/classes +libdir=target/lib diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..17ae7fb --- /dev/null +++ b/build.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RETS 1.5]]> + + Copyright © 2002 National Association of Realtors - All Rights Reserved.]]> + + + + + + + + + + + + + diff --git a/project.properties b/project.properties new file mode 100644 index 0000000..6edcba6 --- /dev/null +++ b/project.properties @@ -0,0 +1,7 @@ +# JAR FILES +maven.jar.override=on +maven.jar.BZip2=${basedir}/src/lib/BZip2.jar +maven.jar.retscommons=${basedir}/src/lib/retsCommons.jar +maven.jar.log4j=${basedir}/src/lib/log4j-1.2.5.jar +maven.jar.commons-httpclient=${basedir}/src/lib/commons-httpclient-2.0-rc1-avantia.jar +maven.jar.regexp=${basedir}/src/lib/regexp.jar diff --git a/project.xml b/project.xml new file mode 100644 index 0000000..2c6a8c2 --- /dev/null +++ b/project.xml @@ -0,0 +1,115 @@ + + + ${maven.base.project} + + 3 + RETS API + retsapi + 1.0 + 2003 + org.realtor.rets + /images/logos/rets_suite.jpg + + + Java RETS API + + + + The RETS API is a Reference Implementation API for RETS Transactions + written in Java and can be used as an API by web-based and desktop + RETS Client Applications. + + + /sandbox/${pom.artifactId}/ + + + + release + 1.0 + RETSAPI + + + + + + log4j + log4j + 1.2.5 + http://www.ibiblio.org/maven/log4j + + + commons-httpclient + commons-httpclient + 2.0-rc1 + http://www.ibiblio.org/maven/commons-httpclient + + + regexp + regexp + 1.2 + http://www.ibiblio.org/maven/regexp + + + BZip2 + BZip2 + 1.0 + + + + retscommons + retscommons + 1.0 + + + + + + + + rets@avantia-inc.com + + + ${basedir}/src + + + + + + ${basedir}/lib + + *.jar + + + + ${basedir}/src/resources/misc + + *.xsd + + + + ${basedir}/config + + log4j.properties + + + + + + + + + + + + maven-changelog-plugin + maven-changes-plugin + maven-checkstyle-plugin + maven-developer-activity-plugin + maven-file-activity-plugin + maven-jdepend-plugin + maven-junit-report-plugin + maven-jxr-plugin + maven-pmd-plugin + maven-tasklist-plugin + + diff --git a/src/etc/dummy.dtd b/src/etc/dummy.dtd new file mode 100644 index 0000000..9a6a4ec --- /dev/null +++ b/src/etc/dummy.dtd @@ -0,0 +1,3 @@ +DO NOT REMOVE! +This file is search for as a resource. +All other DTDs should be in the same directory as this file. diff --git a/src/etc/log4j.properties b/src/etc/log4j.properties new file mode 100644 index 0000000..a4e6cd2 --- /dev/null +++ b/src/etc/log4j.properties @@ -0,0 +1,38 @@ +log4j.rootCategory=error, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n + + +# client API logfile +log4j.appender.R1=org.apache.log4j.RollingFileAppender +log4j.appender.R1.File=/tmp/retsClientAPI.log +log4j.appender.R1.MaxFileSize=100KB +log4j.appender.R1.MaxBackupIndex=3 +log4j.appender.R1.layout=org.apache.log4j.PatternLayout +log4j.appender.R1.layout.ConversionPattern=%p %t %c CLIENTAPI- %m%n + + +# server logfile +log4j.appender.R2=org.apache.log4j.RollingFileAppender +log4j.appender.R2.File=/tmp/retsServer.log +log4j.appender.R2.MaxFileSize=100KB +log4j.appender.R2.MaxBackupIndex=3 +log4j.appender.R2.layout=org.apache.log4j.PatternLayout +log4j.appender.R2.layout.ConversionPattern=%p %t %c SERVER - %m%n + +# setup RETS client API logging +log4j.category.org.realtor.rets.retsapi=debug, R1 +log4j.category.org.realtor.rets.util=debug, R1 + + +# setup RETS server logging +log4j.category.org.realtor.rets.server=debug, R2, stdout +log4j.category.org.realtor.rets.util=debug, R2 +log4j.category.org.realtor.rets.persistance=error, R2 + + +log4j.additivity.org.realtor.rets.server=false +log4j.additivity.org.realtor.rets.retsapi=false +log4j.additivity.org.realtor.rets.util=false diff --git a/src/lib/BZip2.jar b/src/lib/BZip2.jar new file mode 100644 index 0000000..43c3c49 Binary files /dev/null and b/src/lib/BZip2.jar differ diff --git a/src/lib/activation.jar b/src/lib/activation.jar new file mode 100644 index 0000000..eb69201 Binary files /dev/null and b/src/lib/activation.jar differ diff --git a/src/lib/commons-httpclient-2.0-rc1-avantia.jar b/src/lib/commons-httpclient-2.0-rc1-avantia.jar new file mode 100644 index 0000000..87a2722 Binary files /dev/null and b/src/lib/commons-httpclient-2.0-rc1-avantia.jar differ diff --git a/src/lib/commons-logging-api.jar b/src/lib/commons-logging-api.jar new file mode 100644 index 0000000..209bcdf Binary files /dev/null and b/src/lib/commons-logging-api.jar differ diff --git a/src/lib/commons-logging.jar b/src/lib/commons-logging.jar new file mode 100644 index 0000000..b99c937 Binary files /dev/null and b/src/lib/commons-logging.jar differ diff --git a/src/lib/commons-rets.jar b/src/lib/commons-rets.jar new file mode 100644 index 0000000..e91e48b Binary files /dev/null and b/src/lib/commons-rets.jar differ diff --git a/src/lib/log4j-1.2.5.jar b/src/lib/log4j-1.2.5.jar new file mode 100644 index 0000000..c7111d8 Binary files /dev/null and b/src/lib/log4j-1.2.5.jar differ diff --git a/src/lib/mail.jar b/src/lib/mail.jar new file mode 100644 index 0000000..eb2269d Binary files /dev/null and b/src/lib/mail.jar differ diff --git a/src/lib/regexp-1.2.jar b/src/lib/regexp-1.2.jar new file mode 100644 index 0000000..713441c Binary files /dev/null and b/src/lib/regexp-1.2.jar differ diff --git a/src/lib/regexp.jar b/src/lib/regexp.jar new file mode 100644 index 0000000..c6ea2b0 Binary files /dev/null and b/src/lib/regexp.jar differ diff --git a/src/lib/retsCommons.jar b/src/lib/retsCommons.jar new file mode 100644 index 0000000..8bb7815 Binary files /dev/null and b/src/lib/retsCommons.jar differ diff --git a/src/main/java/org/realtor/rets/retsapi/InputStreamDataSource.java b/src/main/java/org/realtor/rets/retsapi/InputStreamDataSource.java new file mode 100644 index 0000000..df492c9 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/InputStreamDataSource.java @@ -0,0 +1,126 @@ +package org.realtor.rets.retsapi; + +import javax.activation.DataSource; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +/** + * A class to provide a {@link javax.activation.DataSource} interface to + * an input stream of unknown characteristics. The DataSource + * interface requires that its implementor be able to repeatedly restart + * the read from the beginning. This isn't guaranteed by InputStream, so + * we encapsulate the InputStream with an object that will buffer the + * data coming from it. (We can't use mark/reset + * because the eventual data source consumer might use those methods, + * which would override use here. + */ +public class InputStreamDataSource implements DataSource +{ + private byte fStreamBytes[]; + private String fContentType; + + public InputStreamDataSource(InputStream baseStream, String contentType) throws IOException { + fContentType = contentType; + + // Read the content of the input stream into byte array blocks. Read + // to the end of file and save all the blocks. These will be consolidated + // after all are read. This uses twice as much storage as simply designing + // a new input stream, but I don't want to write that class right now, + // especially since I'm not completely clear on the blocking semantics. + // ByteArrayInputStream already knows them, so I'll just use that. + + Vector byteArrays = new Vector(); + int totalBytesRead = 0; + byte temporaryByteArray[]; + int readCount; + int quantum = 4096; + int bytesInCurrentBlock; + + do { + bytesInCurrentBlock = 0; + temporaryByteArray = new byte[quantum]; + do { + readCount = + baseStream.read(temporaryByteArray, bytesInCurrentBlock, quantum - bytesInCurrentBlock); + if (readCount > 0) bytesInCurrentBlock += readCount; + } while (readCount >= 0 && bytesInCurrentBlock < quantum); + + if (bytesInCurrentBlock > 0) + byteArrays.add(temporaryByteArray); + + totalBytesRead += bytesInCurrentBlock; + } while (readCount >= 0); + + // Copy all the blocks into one single mondo block. + fStreamBytes = new byte[totalBytesRead]; + + int numberOfBlocks = byteArrays.size(); + byte theBlock[]; + for (int blockIndex = 0; blockIndex < numberOfBlocks - 1; ++blockIndex) { + theBlock = (byte[]) byteArrays.get(blockIndex); + System.arraycopy(theBlock, 0, fStreamBytes, blockIndex * quantum, quantum); + } + + theBlock = (byte[]) byteArrays.get(numberOfBlocks - 1); + System.arraycopy(theBlock, 0, fStreamBytes, quantum * (numberOfBlocks - 1), bytesInCurrentBlock); + + } + + /** + * Returns the Content-Type header value for the encapsulated content. + */ + public String getContentType() { + return fContentType; + } + + /** + * Returns an input stream that may be used to access the content of this + * DataSource A new input stream, set at the beginning of the + * stream, is returned each time you call this method. + * + * @return An {@link InputStream} that will furnish the + * associated data. + */ + public InputStream getInputStream() { + return new ByteArrayInputStream(fStreamBytes); + } + + /** + * Returns the name of this data source. This class does not provide named data + * sources; the string "Untitled" is returned. + * + * @return The string "Untitled". + */ + public String getName() { + return "Untitled"; + } + + /** + * Conformance to javax.activation.DataSource Throws an + * {@link IOException} since this DataSource is read-only. + */ + public OutputStream getOutputStream() throws IOException { + throw new IOException("InputStreamDataSource is read-only."); + } + + /** + * Return the content of the input stream as a full byte array. + */ + public byte[] contentAsByteArray() { + return fStreamBytes; + } + + /** + * Returns the loaded data as a string. This is primarily for diagnostic + * purposes, as there are other ways of turning an InputStream into a String. + * + * @return A String containing the input data. + */ + public String bufferedDataAsString() { + return new String(fStreamBytes); + } +} + diff --git a/src/main/java/org/realtor/rets/retsapi/RETSActionTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSActionTransaction.java new file mode 100644 index 0000000..abb18ed --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSActionTransaction.java @@ -0,0 +1,34 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + + +/** + * RETSActionTransaction.java + * + * This class is used to build an action transaction + * + * @author jbrush + * @version 1.0 + */ +public class RETSActionTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSConnection.class); + + /** + * Constructor + */ + public RETSActionTransaction() { + super(); + setRequestType("Action"); + } + + /** + * Sets the reponse body. + * + *@param body text of the response + * + */ + public void setResponse(String body) { + super.setResponse(body); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSBasicResponseParser.java b/src/main/java/org/realtor/rets/retsapi/RETSBasicResponseParser.java new file mode 100644 index 0000000..f8d329b --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSBasicResponseParser.java @@ -0,0 +1,128 @@ +// +// RETSBasicResponseParser.java +// NARRETSClasses +// +// Created by Bruce Toback on 1/3/05. +// Copyright (c) 2005 __MyCompanyName__. All rights reserved. +// + +package org.realtor.rets.retsapi; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.InputStream; +import java.util.Vector; + +/** + * Do a basic parse of a RETS response, optionally failing fast upon determination + * as to whether this is actually a RETS response. + */ + +public class RETSBasicResponseParser extends org.xml.sax.helpers.DefaultHandler +{ + protected String fReplyText; + protected int fReplyCode; + protected Vector fExceptions; + protected StringBuffer fElementCharacters; + protected boolean fReplyValid; + protected boolean fFirstElementProcessed; // Set once the first element is processed + + public RETSBasicResponseParser(InputStream responseStream) { + fReplyValid = false; + + try { + SAXParserFactory aParserFactory = SAXParserFactory.newInstance(); + + aParserFactory.setValidating(false); + aParserFactory.setNamespaceAware(true); + + SAXParser aParser = aParserFactory.newSAXParser(); + + aParser.parse(responseStream, this); + } catch (SAXParseException saxParseException) { + addExceptionMessage(saxParseException.getMessage()); + } catch (SAXException saxException) { + addExceptionMessage(saxException.getMessage()); + } catch (ParserConfigurationException parserConfigurationException) { + addExceptionMessage(parserConfigurationException.getMessage()); + } catch (java.io.IOException ioException) { + addExceptionMessage(ioException.getMessage()); + } + } + + public boolean responseIsValid() { + return fReplyValid && (fExceptions == null || fExceptions.size() == 0); + } + + public Vector exceptionMessages() { + return fExceptions; + } + + public int replyCode() { + return fReplyCode; + } + + public String replyText() { + return fReplyText; + } + + protected void addExceptionMessage(String exceptionMessage) { + if (fExceptions == null) + fExceptions = new Vector(); + fExceptions.addElement(exceptionMessage); + } + + // Methods required to extend DefaultHandler + + public void error(SAXParseException e) { + addExceptionMessage(e.getMessage()); + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + int attributeCount = attributes.getLength(); + int attributeIndex; + String attributeName; + + if (fElementCharacters == null) + fElementCharacters = new StringBuffer(); + else + fElementCharacters.setLength(0); + + if (qName.equals("RETS") || + qName.equals("RETS-STATUS")) { + for (attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex) { + attributeName = attributes.getLocalName(attributeIndex); + + if (attributeName.equals("ReplyText")) + fReplyText = attributes.getValue(attributeIndex); + else if (attributeName.equals("ReplyCode")) { + String replyCode = attributes.getValue(attributeIndex); + try { + fReplyCode = Integer.parseInt(replyCode); + fReplyValid = true; + } catch (NumberFormatException e) { + fReplyCode = 0; + addExceptionMessage("RETS reply code invalid (\"" + replyCode + "\")"); + } + } + } + } else if (!fFirstElementProcessed) { + throw new SAXException("Not a RETS reply."); + } + + fFirstElementProcessed = true; + } + + public void endElement(String uri, String localName, String qName) { + } + + public void characters(char[] ch, int start, int length) { + fElementCharacters.append(ch, start, length); + } + +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSChangePasswordTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSChangePasswordTransaction.java new file mode 100644 index 0000000..eaac012 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSChangePasswordTransaction.java @@ -0,0 +1,168 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + +import org.realtor.rets.util.*; + +import java.security.*; + + +/** + * Send a Change Password transaction to the server. + * + * @author jbrush + * @version 1.0 + */ +public class RETSChangePasswordTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSChangePasswordTransaction.class); + private String oldPassword = null; + private String newPassword = null; + private String newPassword2 = null; + private String encrypted = null; + private String decrypted = null; + private String username = null; + + /** Create a new RETSChangePasswordTransaction */ + public RETSChangePasswordTransaction() { + super(); + setRequestType("ChangePassword"); + } + + /** + * Sets the username + *@param username name user signs in with + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Sets the old password + *@param passwd users password to be changed + */ + public void setOldPassword(String passwd) { + this.oldPassword = passwd; + } + + /** + * Sets the new password value + *@param passwd new password + */ + public void setNewPassword(String passwd) { + this.newPassword = passwd; + } + + /** + * Sets the new password confirm value + *@param passwd new password + */ + public void setNewPassword2(String passwd) { + this.newPassword2 = passwd; + } + + /** + * process the transaction + */ + public void preprocess() { + String errMsg = null; + + super.preprocess(); + + setUsername((String) transactionContext.get("username")); + + cat.debug("username=" + username); + cat.debug("oldPassword=" + oldPassword); + cat.debug("newPassword=" + newPassword); + + //cat.debug("newPassword2="+newPassword2); + + /*if (oldPassword == null + || !oldPassword.equals(transactionContext.get("password"))) { + errMsg = "Old Password does not match."; + } + else if ((newPassword1 == null || newPassword2 == null) + || (!newPassword1.equals(newPassword2))) { + errMsg = "New Passwords do not match."; + }*/ + + //else { + String pwd = encryptPWD(); + + //cat.debug("PWD:"+pwd); + setRequestVariable("PWD", pwd); + + //} + if (errMsg != null) { + cat.warn(errMsg); + + setResponseStatus("20513"); // Miscellaneous error + setResponseStatusText(errMsg); + setResponse(errMsg); + + errMsg = null; + } + } + + public void postprocess() { + transactionContext.put("password", newPassword); + } + + private String encryptPWD() { + byte[] key = makeKey(); + String source = newPassword + ":" + username; + + return DES(key, source); + } + + private String DES(byte[] keyBytes, String source) { + try { + // Create encrypter/decrypter class + DesCrypter crypter = new DesCrypter(keyBytes); + + // Encrypt + encrypted = crypter.encrypt(source); + + // Decrypt + decrypted = crypter.decrypt(encrypted); + + return encrypted; + } catch (Exception e) { + } + + return null; + } + + private byte[] makeKey() { + MessageDigest md = null; + + try { + md = MessageDigest.getInstance("MD5"); + } catch (java.security.NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + md.reset(); + + // trim to 8 bytes + byte[] key = new byte[8]; + System.arraycopy(md.digest((oldPassword + username).toUpperCase() + .getBytes()), 0, key, 0, 8); + + return key; + } + + ///////////////////////////////////////////////// + public static void main(String[] args) { + RETSChangePasswordTransaction t = new RETSChangePasswordTransaction(); + + t.setUsername(args[0]); + t.setOldPassword(args[1]); + t.setNewPassword(args[2]); + + //t.setNewPassword2(args[2]); + t.preprocess(); + + System.out.println("encrypted=" + t.encrypted); + System.out.println("decrypted=" + t.decrypted); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSConnection.java b/src/main/java/org/realtor/rets/retsapi/RETSConnection.java new file mode 100644 index 0000000..b2ff47c --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSConnection.java @@ -0,0 +1,633 @@ +/* + * RETSConnection.java + * + * Created on November 16, 2001, 1:33 PM + */ +package org.realtor.rets.retsapi; + +import com.aftexsw.util.bzip.CBZip2InputStream; +import org.apache.commons.httpclient.*; +import org.apache.commons.httpclient.cookie.CookiePolicy; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Category; +import org.realtor.rets.util.RETSConfigurator; + +import java.io.*; +import java.net.URLEncoder; +import java.util.*; +import java.util.zip.GZIPInputStream; + + +/** + * Provides a connection to a RETSServer. + * + * @author tweber + * @version 1.0 + */ +public class RETSConnection extends java.lang.Object { + // log4j category + static Category cat = Category.getInstance(RETSConnection.class); + + static { + RETSConfigurator.configure(); + } + + //Key value pairs for request header. + private HashMap headerHash = new HashMap(); + private HashMap responseHeaderMap = new HashMap(); + private String serverUrl = null; + private String errMsg = null; + private boolean isRetryingAuthorization = false; + private boolean gzipCompressed = false; + private boolean bzipCompressed = false; + private boolean STREAMRESPONSE = false; + private long lastTransactionTime = 0; + private String transactionLogDirectory = null; + private String imageAccept = "image/gif"; // default + private PrintWriter log = null; + private HttpClient client = new HttpClient(); + + HashMap transactionContext = new HashMap(); // holds data across transactions + private int connTimeoutSeconds = 60; // 60 seconds default + + /** + * Creates new RETSConnection and changes default connection timeout + * and sets the ServerURL. + */ + public RETSConnection(String url, int connTimeoutSeconds) { + this(url); + this.connTimeoutSeconds = connTimeoutSeconds; + } + + /** + * Creates new RETSConnection and changes default connection timeout + * and sets the ServerURL. + */ + public RETSConnection(int connTimeoutSeconds) { + this(); + this.connTimeoutSeconds = connTimeoutSeconds; + } + + /** + * Creates new RETSConnection and sets the ServerURL. + */ + public RETSConnection(String url) { + this(); + serverUrl = url; + } + + /** + * Create a new RETSConnection and setup some required Header fields. + */ + public RETSConnection() { + setRequestHeaderField("User-Agent", "Mozilla/4.0"); + setRequestHeaderField("RETS-Version", "RETS/1.0"); + } + + /** + * Executes a transaction + * + * @param RETSTransaction transaction to execute + */ + public void execute(RETSTransaction transaction) { + execute(transaction, false); + } + + /** + * Executes a transaction + * + * @param RETSTransaction transaction to execute + */ + public void executeStreamResponse(RETSTransaction transaction) { + execute(transaction, true); + } + + /** + * Executes a transaction + * + * @param RETSTransaction transaction to execute + */ + public void execute(RETSTransaction transaction, boolean asStream) { + java.util.Date dt1 = new Date(); + STREAMRESPONSE = asStream; + + if ( transaction instanceof RETSGetObjectTransaction ) { + setRequestHeaderField("Accept", getImageAccept()); + } else { + setRequestHeaderField("Accept", "*/*"); + } + + if ((transactionLogDirectory != null) && (transactionLogDirectory.length() > 1)) { + String transType = transaction.getClass().getName(); + int nameIdx = transType.lastIndexOf(".") + 1; + String name = transType.substring(nameIdx); + Date dt = new Date(); + String outFile = transactionLogDirectory + "/" + name + dt.getTime() + ".txt"; + + try { + log = new PrintWriter(new FileWriter(outFile)); + log.println(""); + } catch (Exception e) { + cat.error("could create output file :" + outFile); + } + } + + String compressFmt = transaction.getCompressionFormat(); + + if (compressFmt != null) { + if (compressFmt.equalsIgnoreCase("gzip")) { + setRequestHeaderField("Accept-Encoding", "application/gzip,gzip"); + } else if (compressFmt.equalsIgnoreCase("bzip")) { + setRequestHeaderField("Accept-Encoding", "application/bzip,bzip"); + } else if (compressFmt.equalsIgnoreCase("none")) { + removeRequestHeaderField("Accept-Encoding"); + } + } + + transaction.setContext(transactionContext); + + transaction.preprocess(); + + processRETSTransaction(transaction); + + transaction.postprocess(); + + Date dt2 = new Date(); + lastTransactionTime = dt2.getTime() - dt1.getTime(); + + if (log != null) { + try { + log.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + log = null; + } + + return; + } + + public long getLastTransactionTime() { + return lastTransactionTime; + } + + public void setTransactionLogDirectory(String tLogDir) { + this.transactionLogDirectory = tLogDir; + } + + public String getTransactionLogDirectory() { + return this.transactionLogDirectory; + } + + private void writeToTransactionLog(String msg) { + if (log != null) { + try { + this.log.println(msg); + } catch (Exception e) { + e.printStackTrace(); + } + } + + cat.debug(msg); + } + + private void writeMapToTransactionLog(Map map) { + if (map == null) { + return; + } + + Iterator itr = map.keySet().iterator(); + + while (itr.hasNext()) { + String key = (String) itr.next(); + String value = ""; + Object obj = map.get(key); + + if (obj instanceof String) { + value = (String) obj; + } else { + value = "{ "; + + Collection c = (Collection) obj; + Iterator i2 = c.iterator(); + + if (i2.hasNext()) { + value = (String) i2.next(); + + while (i2.hasNext()) { + value = value + ", " + (String) i2.next(); + } + } + + value = value + " }"; + } + + writeToTransactionLog(key + "=" + value); + } + } + + /** + * Returns the server's URL, this url as a base for all transactions + */ + public String getServerUrl() { + return serverUrl; + } + + /** + * Sets the url for the connection. + * + * @param url Server's address ex: http://www.realtor.org/RETSServer + */ + public void setServerUrl(String url) { + serverUrl = url; + } + + /** + * Key value pairs in the client request header + * + * @param key field name in the request header + * @param value value associated with the key + */ + public void setRequestHeaderField(String key, String value) { + headerHash.put(key, value); + } + + public void setUserAgent(String userAgent) { + headerHash.put("User-Agent", userAgent); + } + + public void setRetsVersion(String retsVersion) { + setRequestHeaderField("RETS-Version", retsVersion); + } + + /** + * Removes a key/value pair from the request header. + * + * @param key field to remove from the request header. + */ + public void removeRequestHeaderField(String key) { + headerHash.remove(key); + } + + public HashMap getResponseHeaderMap() { + return responseHeaderMap; + } + + /** + * gets the url content and returns an inputstream + * + * @param strURL + * @param requestMethod + * @param requestMap + */ + public InputStream getURLContent(String strURL, String requestMethod, Map requestMap) { + InputStream is = null; + gzipCompressed = false; + bzipCompressed = false; + + boolean needToAuth = false; + + HttpMethod method = null; + + cat.debug("getURLContent: URL=" + strURL); + + try { + if (requestMethod.equalsIgnoreCase("GET")) { + method = new GetMethod(strURL); + } else { + method = new PostMethod(strURL); + } + + client.getState().setCredentials(null, null, new UsernamePasswordCredentials(getUsername(), getPassword())); + client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY); + client.setConnectionTimeout(connTimeoutSeconds * 1000); + + method.setDoAuthentication(true); + method.setFollowRedirects(true); + + addHeaders(method, headerHash); + writeMapToTransactionLog(headerHash); + + // send the request parameters + if (requestMap != null) { + NameValuePair[] pairs = mapToNameValuePairs(requestMap); + + if (requestMethod.equalsIgnoreCase("POST")) { + // requestMethod is a post, so we can safely cast. + PostMethod post = (PostMethod) method; + post.setRequestBody(pairs); + } else { + GetMethod get = (GetMethod) method; + get.setQueryString(pairs); + } + } + + this.writeToTransactionLog(""); + + int responseCode = client.executeMethod(method); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ByteArrayInputStream bais = new ByteArrayInputStream(method.getResponseBody()); + copyResponseHeaders(method); + method.releaseConnection(); // from bruce + return bais; + } catch (IOException io) { + io.printStackTrace(); + errMsg = "RETSAPI: I/O exception while processing transaction: " + io.getMessage(); + return null; + } finally { + if (method != null) { + method.releaseConnection(); + } + } + } + + /** + * Changes a map into an array of name value pairs + * + * @param requestMap The map to change. + * @return An array of Name value pairs, representing keys and values from the map. + */ + private NameValuePair[] mapToNameValuePairs(Map requestMap) { + NameValuePair[] pairs = new NameValuePair[requestMap.size()]; + Iterator iter = requestMap.keySet().iterator(); + int i = 0; + + while (iter.hasNext()) { + String key = (String) iter.next(); + String value = (String) requestMap.get(key); + NameValuePair nvp = new NameValuePair(key, value); + pairs[i] = nvp; + i++; + } + + return pairs; + } + + /** + * Adds response headers to Http method + * + * @param responseHeaderMap + * @param method + */ + private void copyResponseHeaders(HttpMethod method) { + responseHeaderMap.clear(); + + Header[] headers = method.getResponseHeaders(); + + for (int i = 0; i < headers.length; i++) { + Header current = headers[i]; + List list = (List) responseHeaderMap.get(current.getName()); + + if (list == null) { + list = new ArrayList(); + } + + list.add(current.getValue()); + responseHeaderMap.put(current.getName(), list); + } + } + + private void addHeaders(HttpMethod method, HashMap headers) { + Iterator keys = headers.keySet().iterator(); + + while (keys.hasNext()) { + String key = (String) keys.next(); + Object value = headers.get(key); + + if (value instanceof String && isValidString((String) value)) { + method.addRequestHeader(key, (String) value); + } else if (value instanceof ArrayList) { + ArrayList list = (ArrayList) value; + StringBuffer valueList = new StringBuffer(); + + for (int i = 0; i < list.size(); i++) { + if (i > 0) { + valueList.append(";"); + } + + valueList.append(list.get(i)); + } + + method.addRequestHeader(key, valueList.toString()); + } + } + } + + /** + * Processes a transaction, sends rets request and gets + * the response stream from the server. Uncompresses the + * response stream if compression was used in the reply + * + * @param transaction rets transaction to process + */ + private void processRETSTransaction(RETSTransaction transaction) { + try { + serverUrl = transaction.getUrl(); + + cat.debug(transaction.getRequestType() + " URL : {" + serverUrl + "}"); + + if (serverUrl == null) { + cat.error(transaction.getRequestType() + " URL is null"); + transaction.setResponseStatus("20036"); + transaction.setResponseStatusText(transaction.getRequestType() + " URL is missing. Successful login is required."); + return; // throw exception here + } + + String method = "POST"; + + // Action transaction requires a GET according to RETS spec + if (transaction.getRequestType().equalsIgnoreCase("Action")) { + method = "GET"; + } + cat.debug("method: " + method); + + InputStream is = getURLContent(serverUrl, method, transaction.getRequestMap()); + + if (is == null) { + transaction.setResponseStatus("20513"); // Miscellaneous error + transaction.setResponseStatusText(errMsg); + transaction.setResponse(errMsg); + errMsg = null; + + return; + } else { + Iterator itr = responseHeaderMap.keySet().iterator(); + Object compressionFmt = responseHeaderMap.get("Content-Encoding"); + + if (compressionFmt != null) { + cat.debug("Header class : " + compressionFmt.getClass().getName()); + + if (compressionFmt.toString().equalsIgnoreCase("[gzip]")) { + gzipCompressed = true; + } else if (compressionFmt.toString().equalsIgnoreCase("[bzip]")) { + bzipCompressed = true; + } + } + + if (gzipCompressed) { + is = new GZIPInputStream(is); + } else if (bzipCompressed) { + is = new CBZip2InputStream(is); + } + } + this.writeToTransactionLog(""); + + transaction.setResponseHeaderMap(this.responseHeaderMap); + + if ((transaction instanceof RETSGetObjectTransaction && (! transaction.getResponseHeader("Content-Type").startsWith("text/xml"))) || STREAMRESPONSE) { + transaction.setResponseStream(is); + } else { + String contents = null; + contents = streamToString(is); + writeToTransactionLog(contents); + + /*catch( IOException e) { + errMsg = "Error reading response stream: " + contents; + cat.error(errMsg, e); + transaction.setResponseStatus("20513"); // Miscellaneous error + transaction.setResponseStatusText(errMsg); + errMsg = null; + }*/ + if (contents.length() == 0) { + transaction.setResponseStatus("20513"); // Miscellaneous error + transaction.setResponseStatusText("Empty Body"); + } + + transaction.setResponse(contents); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + String getUsername() { + String username = null; //(String)requestMap.get("username"); + + if (username == null) { + username = (String) transactionContext.get("username"); + } + + return username; + } + + String getPassword() { + String password = null; //(String)requestMap.get("password"); + + if (password == null) { + password = (String) transactionContext.get("password"); + } + + return password; + } + + /** + * Removes the quotes on a string. + * + * @param quotedString string that might contain quotes + */ + private static String removeQuotes(String quotedString) { + if ((quotedString != null) && (quotedString.length() > 2)) { + return quotedString.substring(1, quotedString.length() - 1); + } else { + return ""; // empty string + } + } + + /** + * Checks to make sure the string passed in is a valid string parameter (not null and not zero length). + * + * @param value string to be validated + */ + private boolean isValidString(String value) { + return ((value != null) && (value.length() > 0)); + } + + private String streamToString(InputStream is) throws IOException { + if (is != null) { + StringBuffer sb = new StringBuffer(); + int numread = 0; + byte[] buffer = new byte[1024 * 8]; //initialize an 8k buffer + + while ((numread = is.read(buffer)) >= 0) { + String s = new String(buffer, 0, numread); + sb.append(s); + } + + return sb.toString(); + } + + return null; + } + + /** + * Main method for testing only! + * + * @param args the command line arguments + */ + public static void main(String[] args) { + BasicConfigurator.configure(); + + RETSConnection rc = new RETSConnection(); + RETSLoginTransaction trans = new RETSLoginTransaction(); + + try { + Properties props = new Properties(); + props.load(new FileInputStream("/tmp/client.properties")); + + // Add the optional request parameters if they exist, are non-null and non-zero-length + // rc.setRequestHeaderField("Authorization", (String)props.get("login.AUTHORIZATION")); + rc.setServerUrl((String) props.getProperty("SERVER_URL")); + trans.setUrl((String) props.getProperty("SERVER_URL")); + trans.setUsername((String) props.getProperty("USERNAME")); + trans.setPassword((String) props.getProperty("PASSWORD")); + } catch (Exception e) { + e.printStackTrace(); + } + + rc.execute(trans); + } + + /** + * Build the queryString from the request map + * + * @param requestMap the list of request parameters + */ + private String buildQueryString(Map requestMap) { + /*if (((String)(requestMap.get("requestType"))).equalsIgnoreCase("Search")) { + return "SearchType=Property&Class=RESI&Query=(Listing_Price%3D100000%2B)&QueryType=DMQL"; + }*/ + StringBuffer sb = new StringBuffer(); + Iterator it = requestMap.keySet().iterator(); + + // build query string + while (it.hasNext()) { + String key = (String) it.next(); + + if (key.equals("requestType")) { + //commenting out requestType because it is not a standard req parameter and may break RETS servers + continue; + } + + String reqStr = key + "=" + URLEncoder.encode((String) requestMap.get(key)); + cat.debug(reqStr); + sb.append(reqStr); + + if (it.hasNext()) { + sb.append("&"); + } + } + + return sb.toString(); + } + + public String getImageAccept() { + return imageAccept; + } + + public void setImageAccept(String imageAccept) { + this.imageAccept = imageAccept; + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSGetMetadataTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSGetMetadataTransaction.java new file mode 100644 index 0000000..ae03c9a --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSGetMetadataTransaction.java @@ -0,0 +1,52 @@ +/** + * RETSGetMetadataTransaction.java + * + * @author jbrush + * @version + */ +package org.realtor.rets.retsapi; + + +//import java.util.*; +import org.apache.log4j.*; + + +/////////////////////////////////////////////////////////////////////// +public class RETSGetMetadataTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSGetMetadataTransaction.class); + String version = null; + + /** + * constructor + */ + public RETSGetMetadataTransaction() { + super(); + setRequestType("GetMetadata"); + } + + public void setType(String str) { + setRequestVariable("Type", str); + } + + public void setId(String str) { + setRequestVariable("ID", str); + } + + public void setFormat(String str) { + setRequestVariable("Format", str); + } + + // public void setResource(String str) { + // setRequestVariable("resource", str); + // } + // public void setResourceClass(String str) { + // setRequestVariable("resourceClass", str); + // } + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSGetObjectTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSGetObjectTransaction.java new file mode 100644 index 0000000..590b041 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSGetObjectTransaction.java @@ -0,0 +1,350 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.Category; + +import java.io.IOException; +import java.io.InputStream; + +import java.util.Iterator; +import java.util.Collection; +import java.util.ArrayList; + +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.InternetHeaders; +import javax.mail.internet.MimeBodyPart; +import javax.mail.MessagingException; + +/** + * RETSGetObjectTransaction.java + * + * @version 1.0 + * @author jbrush + */ +public class RETSGetObjectTransaction extends RETSTransaction +{ + /** + * Encapsulates the RETS GetObject transaction, and provides services for + * interpreting the result. As with all {@link RETSTransaction} classes, + * your code should create an instance of this class when it wants to + * perform a GetObject transaction. The transaction requires three + * parameters: the resource name (see {@link #setResource}), the type + * of object requested (e.g., "Photo"; see {@link setType}), + * and the ID of the object -- that is, its associated key such as the + * MLS number or agent number. You may also request that the server + * send back only the location of the resource by calling {@link #setLocation}. + */ + + static Category cat = Category.getInstance(RETSGetObjectTransaction.class); + // collection of body parts resulting from the collision of the server response with the MIME parsing code. +// protected Collection fBodyParts; + protected ArrayList parts; + + /** + * create new RETSGetObjectTransaction and set RequestType + */ + public RETSGetObjectTransaction() { + super(); + setRequestType("GetObject"); + } + + /** + * Sets the response body for the transaction. + * + * @param body body of the transaction + */ + public void setResponse(String body) { + super.setResponse(body); + setKeyValuePairs(body); + } + + /** + * Sets the resource attribute to the string passed in. + * + * @param str resource value + */ + public void setResource(String str) { + cat.debug("set Resource=" + str); + setRequestVariable("Resource", str); + } + + /** + * Sets the type attribute to the string passed in. + * + * @param type type attribute value + */ + public void setType(String str) { + cat.debug("set Type=" + str); + setRequestVariable("Type", str); + } + + /** + * Sets the ID attribute to the string passed in. + * + * @param str ID of the object + */ + public void setID(String str) { + if ( str != null ) { + cat.debug("set ID=" + str.trim()); + setRequestVariable("ID", str.trim()); + } else { + cat.debug("set ID=" + str); + } + } + + /** + * Sets the location attribute to the string passed in. + * + * @param str location attribute value + */ + public void setLocation(String str) { + cat.debug("set Location=" + str); + setRequestVariable("Location", str); + } + + /** + * Sets the response stream. This triggers various actions depending on the + * content-type of the response stream: + + * If the content-type is text/plain or text/xml, then assume we have a + * RETS response and parse it accordingly. Note that we'll not have + * anything in the RETS response except error information. (We might + * have no error and nothing else, in which case the only possibility + * is that we made a request with Location=1 and the server has + * responded in kind.) We still make this available, in case there *is* + * something else in the response. + + * A content-type of multipart, with any subtype, is interpreted as a + * multipart response. This is parsed to create a list of MIME parts. + * Any other content type is simply made available as a single MIME part. + + * This method is called by {@link RETSConnection} to provide access to + * the response from a transaction. You don't normally call this method + * yourself. + * + * @param responseStream The response stream to associate with this transaction. + * @call Rarely. + * @override Rarely. You can override this method if you want to provide + * special processing of a GetObject response stream, but + * it will usually be more convenient to override one + * of the methods that handles particular MIME types. + */ + public void setResponseStream(InputStream responseStream) { + + String mimeType; +// String contentType = responseHeaderNamed("content-type"); + String contentType = super.getResponseHeader("Content-Type"); + cat.debug("====[RETSGetObjectTx] --> " + contentType); + int contentTypeSemicolonIndex = + contentType == null ? -1 : contentType.indexOf(";"); + + // If there was no Content-Type header, we can't do anything here. Punt to the default handler. + if (contentType == null) { + cat.debug("====[RETSGetObjectTx] : NO CONTENT TYPE"); + super.setResponseStream(responseStream); + return; + } + + // If the content-type string had parameters, trim them to get just the MIME type. + if (contentTypeSemicolonIndex >= 0) + mimeType = contentType.substring(0, contentTypeSemicolonIndex).trim(); + else + mimeType = contentType.trim(); + + cat.debug("====[RETSGetObjectTx] : mime-type -> " + mimeType); + + // If the type is text/xml, then this is probably an error response. + // We need to parse the input stream nondestructively to find out. + if (mimeType.equals("text/xml")) { + handleXMLStream(responseStream, mimeType, contentType); + } else if (mimeType.startsWith("multipart/")) { + // If it's multipart, take it apart and set up appropriate data structures. + cat.debug("====[RETSGetObjectTx] : RECIEVED MULTIPART"); + handleMultipartStream(responseStream, mimeType, contentType); + } else { + // Otherwise, since we do have a MIME type, assume that the response *is* object value. + handleStreamWithType(responseStream, mimeType, contentType); + } + } + + /** + * Handle an input stream whose advertised MIME type is text/xml. + * This may be a RETS error response or something else; we need to figure + * out exactly what it is. If it is a RETS response, parse it and + * deal with it. If not, handle as for an unknown response. + * + * @param responseStream The response stream containing the XML data. + * @call Only from subclasses. + * @ @override Override to provide your own handling of XML data + * streams. + */ + protected void handleXMLStream(InputStream responseStream, String mimeType, String contentType) { + try { + InputStreamDataSource responseStreamDataSource = + new InputStreamDataSource(responseStream, contentType); + RETSBasicResponseParser basicResponseParser = + new RETSBasicResponseParser(responseStreamDataSource.getInputStream()); + if (basicResponseParser.responseIsValid()) { + setResponseStatus(Integer.toString(basicResponseParser.replyCode())); + setResponseStatusText(basicResponseParser.replyText()); + } else { + makeSinglePartFromByteStream(responseStreamDataSource.contentAsByteArray(), contentType); + setResponseStatus("0"); // The response is valid in this case, since we got a body that we can provide + } + } catch (Exception e) { + // We really need something better for this. + setResponseStatus("20513"); + setResponseStatusText("RETSAPI: Could not create a MIME body part from XML stream: " + e.getMessage()); + } + } + + /** + * Handle an input stream whose advertised MIME type is multipart. + * This involves breaking up the stream into its constituent parts + * for callers to retrieve. + * + * @param responseStream The stream to parse. + * @param mimeType The MIME type and subtype associated with the stream. + * @param contentType The Content-Type header, including the MIME type and its parameters, if any. + * @call Only from subclasses. + * @override Override to provide your own handling of MIME multipart + *

+ * data. + */ + protected void handleMultipartStream(InputStream responseStream, String mimeType, String contentType) { + + InputStreamDataSource responseStreamDataSource = null; + try { + responseStreamDataSource = new InputStreamDataSource(responseStream, contentType); + MimeMultipart multipartResponse = new MimeMultipart(responseStreamDataSource); +// multipartResponse.writeTo(System.err); + + parts = new ArrayList(); + int partCount = multipartResponse.getCount(); + for (int i = 0; i < partCount; ++i) { + parts.add(multipartResponse.getBodyPart(i)); + + } + + setResponseStatus("0"); + } catch (MessagingException messagingException) { + if (responseStreamDataSource != null) + cat.debug(responseStreamDataSource.bufferedDataAsString()); +// System.out.println(responseStreamDataSource.bufferedDataAsString()); + + messagingException.printStackTrace(); + setResponseStatus("20513"); + setResponseStatusText("RETSAPI: Could not create a multipart stream from response: " + messagingException.getMessage()); + } catch (IOException ioException) { + ioException.printStackTrace(); + setResponseStatus("20513"); + setResponseStatusText("RETSAPI: I/O exception while creating multipart stream from response: " + ioException.getMessage()); + } finally { + // We always want at least an empty body part list. + if ( parts == null ) parts = new ArrayList(); + } + } + + /** + * Helper for making the response into a single body part. This takes an + * byte array which may have been created during an earlier + * phase of processing, rather than taking an InputStream. + * + * @param inputBytes A byte array containing the response data. + * @param contentType The content-type header. + * @call Rarely. + * @override Rarely. + */ + protected void makeSinglePartFromByteStream(byte[] inputBytes, String contentType) { + // First, we need to gather the response headers into an InternetHeader object + InternetHeaders originalHeaders = new InternetHeaders(); + Iterator headerIterator = getResponseHeaderMap().keySet().iterator(); + + Object headerContent = null; + String headerName = null; + while (headerIterator.hasNext()) { + headerName = (String) headerIterator.next(); +// String headerContent = (String) getResponseHeaderMap().get(headerName); + headerContent = getResponseHeaderMap().get(headerName); + if ( headerContent != null ) + originalHeaders.addHeader(headerName, headerContent.toString()); + } + parts = new ArrayList(1); // We may not have *any*, but we won't have more than 1. + try { + parts.add(new MimeBodyPart(originalHeaders, inputBytes)); + setResponseStatus("0"); + } catch (Exception e) { + e.printStackTrace(); + setResponseStatus("20513"); + setResponseStatusText("RETSAPI: Could not create a MIME body part from response: " + e.getMessage()); + } + } + + /** + * Handle an input stream whose advertised MIME type isn't either + * multipart or XML. This packages up the stream as its own MIME part + * in order to offer it through the normal multipart interface. + * + * @param responseStream The stream to parse. + * @param mimeType The MIME type and subtype associated with the stream. + * @param contentType The Content-Type header, including the MIME type and its parameters, if any. + * @call Only from subclasses. + * @override Override to provide your own handling of data with special + *

+ * MIME types. + */ + protected void handleStreamWithType(InputStream responseStream, String mimeType, String contentType) { + try { + makeSinglePartFromByteStream( + (new InputStreamDataSource(responseStream, contentType)).contentAsByteArray(), contentType); + } catch (IOException e) { + e.printStackTrace(); + setResponseStatus("20513"); + setResponseStatusText("RETSAPI: Could not create a data source from response: " + e.getMessage()); + } + } + + /** + * Returns the count of retrieved objects. + */ + public int getObjectCount() { + if ( parts == null ) return 0; + return parts.size(); + } + + /** + * Returns a vector of all objects found. These are stored as MimeBodyPart objects. + * The returned vector may be empty. + */ + public Collection allReturnedObjects() { + return parts; + } + + /** + * Returns the object with the given index as a MIME body part. Returns null + * if no object with the given index exists. + * + * @param objectIndex The index of the object to retrieve. + */ + public MimeBodyPart partAtIndex(int objectIndex) { + if ( parts == null || objectIndex < 0 || objectIndex >= parts.size() ) + return null; + return (MimeBodyPart) parts.get(objectIndex); + } + + public InputStream getPartAsStream(int objectIndex) { + InputStream inputStream = null; + MimeBodyPart part = this.partAtIndex(objectIndex); + try { + if ( part != null ) { + Object content = part.getContent(); + inputStream = (InputStream) content; + cat.debug("--- MimeBodyPart Content--> " + content); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Kablewie!"); + } + return inputStream; + } + +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSLoginTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSLoginTransaction.java new file mode 100644 index 0000000..3227c71 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSLoginTransaction.java @@ -0,0 +1,179 @@ +package org.realtor.rets.retsapi; + + +//import org.apache.regexp.*; +import org.apache.log4j.*; + +import java.net.*; + +import java.util.*; + + +/** + * RETSLoginTransaction.java + * + * + * @author jbrush + * @version 1.0 + */ +public class RETSLoginTransaction extends RETSTransaction { + /** log4j Object */ + static Category cat = Category.getInstance(RETSLoginTransaction.class); + + /** + * capablitiy list + */ + private static final String[] capList = { + "Login", // Login first because it might reset URL root + "Action", "ChangePassword", "GetObject", "LoginComplete", "Logout", + "Search", "GetMetadata", "Update", "ServerInformation" + }; + String url = null; + String version = null; + + public RETSLoginTransaction() { + super(); + setRequestType("Login"); + } + + /** Sets the response body. This method is called from RETSConnection.execute() + * @param body body of the response + */ + public void setResponse(String body) { + super.setResponse(body); + + setKeyValuePairs(body); + + setCapabilityUrls(); + } + + /** Sets the username for this transaction + * @param username the user's login name + */ + public void setUsername(String username) { + setRequestVariable("username", username); + } + + /** sets the User's password for this transaction. + * @param password password string + */ + public void setPassword(String password) { + setRequestVariable("password", password); + } + + /** sets the URL for this transaction. + * @param url url string + */ + public void setUrl(String url) { + this.url = url; + } + + /** gets the URL for this transaction. + * @param url url string + */ + public String getUrl() { + return url; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } + + /** + * + */ + public void preprocess() { + super.preprocess(); + + // save the username and password + transactionContext.put("username", getRequestVariable("username")); + transactionContext.put("password", getRequestVariable("password")); + } + + /** Extracts the capabilitiesUrls out of the response body. + * + */ + void setCapabilityUrls() { + Map respMap = getResponseMap(); + Set respKeySet = respMap.keySet(); + Iterator iter = null; + String key = null; + + int capLength = capList.length; + + String urlRoot = getUrlRoot(url); // set root to current url root + String capUrl = null; + String qualifiedUrl = null; + + /* jump thru hoop because key might not be in proper mixed-case so we need to map it */ + for (int i = 0; i < capLength; i++) { + iter = respKeySet.iterator(); + + while (iter.hasNext()) { + key = (String) iter.next(); + + if (capList[i].equalsIgnoreCase(key)) { + capUrl = getResponseVariable(key); + qualifiedUrl = qualifyUrl(capUrl, urlRoot); + + cat.debug(capList[i] + "=[" + qualifiedUrl + "]"); + putCapabilityUrl(capList[i], qualifiedUrl); + + if (capList[i].equalsIgnoreCase("Login")) // login may reset rootUrl + { + urlRoot = getUrlRoot(qualifiedUrl); + } + + break; + } + } + } + } + + /** + * Makes sure url is fully qualified. + */ + private String qualifyUrl(String url, String defaultRoot) { + String root = getUrlRoot(url); + String sep = ""; + + if (root == null) { + if (url.charAt(0) != '/') { + sep = "/"; + } + + return defaultRoot + sep + url; + } else { + return url; + } + } + + String getUrlRoot(String myUrl) { + try { + URL url = new URL(myUrl); + + String protocol = url.getProtocol(); + String host = url.getHost(); + int port = url.getPort(); + + //String path = url.getPath(); + //String file = url.getFile(); + cat.debug("protocol = [" + protocol + "]"); + cat.debug("host = [" + host + "]"); + cat.debug("port = [" + port + "]"); + + //cat.debug("path = ["+path+"]"); + //cat.debug("file = ["+file+"]"); + return protocol + "://" + host + ((port > 0) ? (":" + port) : ""); + } catch (MalformedURLException e) { + cat.warn("getUrlRoot:MalformedURLException myUrl=\"" + myUrl + + "\""); + } + + return null; + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSLogoutTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSLogoutTransaction.java new file mode 100644 index 0000000..f9f9cb4 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSLogoutTransaction.java @@ -0,0 +1,31 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + + +/** + * Send a logout transaction to the server. + * + * @author jbrush + * @version 1.0 + */ +public class RETSLogoutTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSLogoutTransaction.class); + + /** Create a new RETSLogoutTransaction */ + public RETSLogoutTransaction() { + super(); + setRequestType("Logout"); + } + + /** Sets the response body. Called from RETSConnection.execute() + * after the logout transaction is executed. + * + * @param body Body of the response to the RETSLogoutTransaction. + */ + public void setResponse(String body) { + super.setResponse(body); + + setKeyValuePairs(body); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSSearchAgentTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSSearchAgentTransaction.java new file mode 100644 index 0000000..2509e93 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSSearchAgentTransaction.java @@ -0,0 +1,31 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + + +/** + * RETSSearchAgentTransaction.java + * Search for agents + * + * @author jbrush + * @version 1.0 + */ +public class RETSSearchAgentTransaction extends RETSSearchTransaction { + static Category cat = Category.getInstance(RETSSearchAgentTransaction.class); + + /**create a new RETSSearchAgentTransaction*/ + public RETSSearchAgentTransaction() { + super(); + setSearchType("Agent"); + setSearchClass("Agent"); + } + + /** Search by last name, pass in the lastname of a user + * as the "query" argument. + * @param searchByLastName lastname of the user to search. + */ + public void setSearchByLastname(String searchByLastname) { + // convert to DMQL + setSearchQuery("(LastName=" + searchByLastname + ")"); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSSearchOfficeTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSSearchOfficeTransaction.java new file mode 100644 index 0000000..8f13f51 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSSearchOfficeTransaction.java @@ -0,0 +1,26 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + + +/** + * RETSSearchOfficeTransaction.java + * Performs a getOffice Transaction + * + * @author jbrush + * @version 1.0 + */ +public class RETSSearchOfficeTransaction extends RETSSearchTransaction { + static Category cat = Category.getInstance(RETSSearchOfficeTransaction.class); + + /** Creates new a RETSSearchOfficeTransaction + * + */ + public RETSSearchOfficeTransaction() { + super(); + setSearchType("Office"); + + setSearchClass("Office"); +// setSearchClass("OFF"); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSSearchPropertyBatchTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSSearchPropertyBatchTransaction.java new file mode 100644 index 0000000..f29f0c2 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSSearchPropertyBatchTransaction.java @@ -0,0 +1,25 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + + +/** + * RETSSearchPropertyBatchTransaction.java + * + * @author jbrush + * @version 1.0 + */ +public class RETSSearchPropertyBatchTransaction extends RETSSearchTransaction { + static Category cat = Category.getInstance(RETSSearchPropertyBatchTransaction.class); + + public RETSSearchPropertyBatchTransaction() { + super(); + setSearchType("Property"); + setSearchClass("RES"); + } + + public void setSearchByListingAgent(String agent) { + // convert to DMQL + setSearchQuery("(AgentID=" + agent + ")"); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSSearchPropertyTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSSearchPropertyTransaction.java new file mode 100644 index 0000000..79f9503 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSSearchPropertyTransaction.java @@ -0,0 +1,19 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + + +/** + * RETSSearchPropertyBatchTransaction.java + * + * @author jbrush + * @version 1.0 + */ +public class RETSSearchPropertyTransaction extends RETSSearchTransaction { + static Category cat = Category.getInstance(RETSSearchPropertyTransaction.class); + + public RETSSearchPropertyTransaction() { + super(); + setSearchType("Property"); + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSSearchTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSSearchTransaction.java new file mode 100644 index 0000000..3f6ab88 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSSearchTransaction.java @@ -0,0 +1,326 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.Category; + +import org.realtor.rets.util.AttributeExtracter; +import org.realtor.rets.util.ResourceLocator; + +import org.xml.sax.helpers.DefaultHandler; + +import java.io.ByteArrayInputStream; + +import java.util.HashMap; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + + +/** + * RETSSearchTransaction.java + * + * @author jbrush + * @version 1.0 + */ +public class RETSSearchTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSSearchTransaction.class); + + //Required Arguments + protected static final String SEARCHTYPE = "SearchType"; + protected static final String SEARCHCLASS = "Class"; + protected static final String SEARCHQUERY = "Query"; + protected static final String SEARCHQUERYTYPE = "QueryType"; + + // Optional Arguments + protected static final String SEARCHCOUNT = "Count"; + protected static final String SEARCHFORMAT = "Format"; + protected static final String SEARCHLIMIT = "Limit"; + protected static final String SEARCHOFFSET = "Offset"; + protected static final String SEARCHSELECT = "Select"; + protected static final String SEARCHDELIMITER = "DELIMITER"; + protected static final String SEARCHRESTRICTEDINDICATOR = "RestrictedIndicator"; + protected static final String SEARCHSTANDARDNAMES = "StandardNames"; + private String version = null; + + public RETSSearchTransaction() { + super(); + setRequestType("Search"); + setSearchQueryType("DMQL"); + } + + public void setResponse(String body) { + super.setResponse(body); + + HashMap hm = this.getAttributeHash(body); + processXML(hm); + } + + /////////////////////////////////////////////////////////////////////// + + /* void processCompact(String body) { + processCountTag(body); + processDelimiterTag(body); + processColumnTag(body); + processCompactData(body); + processMaxRowTag(body); + } */ + void processXML(HashMap hash) { + if (hash == null) { + return; + } + + processCountTag((HashMap) hash.get("COUNT")); + processXMLData((HashMap) hash.get("BODY")); + processMaxRowTag((HashMap) hash.get("MAXROWS")); + processDelimiterTag((HashMap) hash.get("DELIMITER")); + } + + private HashMap getAttributeHash(String body) { + AttributeExtracter ae = new AttributeExtracter(); + DefaultHandler h = ae; + + try { + SAXParserFactory spf = SAXParserFactory.newInstance(); + SAXParser p = spf.newSAXParser(); + ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes()); + + p.parse(bais, h, "file:/" + ResourceLocator.locate("dummy.dtd")); + } catch (Exception e) { + cat.warn(e, e); + + return null; + } + + return ae.getHash(); + } + + void processCountTag(HashMap hash) { + if (hash == null) { + return; + } + + String records = (String) hash.get("Records"); + if ( records == null) { + records = (String) hash.get("records"); + } + if ( records == null) { + records = (String) hash.get("RECORDS"); + } + setCount( records); + } + + void processDelimiterTag(HashMap hash) { + if (hash == null) { + return; + } + + String delim = (String) hash.get("value"); + + if (delim == null) { + delim = (String) hash.get("VALUE"); + } + + if (delim == null) { + delim = (String) hash.get("Value"); + } + + setSearchDelimiter(delim); + } + + void processColumnTag(HashMap hash) { + } + + void processCompactData(HashMap hash) { + } + + void processXMLData(HashMap hash) { + } + + void processMaxRowTag(HashMap hash) { + if (hash == null) { + setResponseVariable("MAXROWS", "true"); + } + + // else + // setResponseVariable("MAXROWS", "false"); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchType(String searchType) { + setRequestVariable(SEARCHTYPE, searchType); + } + + public String getSearchType() { + return getRequestVariable(SEARCHTYPE); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchClass(String searchClass) { + setRequestVariable(SEARCHCLASS, searchClass); + } + + public String getSearchClass() { + return getRequestVariable(SEARCHCLASS); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchQuery(String searchQuery) { + setRequestVariable(SEARCHQUERY, searchQuery); + } + + public String getSearchQuery() { + return getRequestVariable(SEARCHQUERY); + } + + public void setQuery(String searchQuery) { + setRequestVariable(SEARCHQUERY, searchQuery); + } + + public String getQuery() { + return getRequestVariable(SEARCHQUERY); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchQueryType(String searchQueryType) { + setRequestVariable(SEARCHQUERYTYPE, searchQueryType); + } + + public String getSearchQueryType() { + return getRequestVariable(SEARCHQUERYTYPE); + } + + public void setQueryType(String searchQueryType) { + setRequestVariable(SEARCHQUERYTYPE, searchQueryType); + } + + public String getQueryType() { + return getRequestVariable(SEARCHQUERYTYPE); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchCount(String value) { + setRequestVariable(SEARCHCOUNT, value); + } + + public String getSearchCount() { + return getRequestVariable(SEARCHCOUNT); + } + + public void setCount(String value) { + setRequestVariable(SEARCHCOUNT, value); + } + + public String getCount() { + return getRequestVariable(SEARCHCOUNT); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchFormat(String value) { + setRequestVariable(SEARCHFORMAT, value); + } + + public String getSearchFormat() { + return getRequestVariable(SEARCHFORMAT); + } + + public void setFormat(String value) { + setRequestVariable(SEARCHFORMAT, value); + } + + public String getFormat() { + return getRequestVariable(SEARCHFORMAT); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchLimit(String value) { + setRequestVariable(SEARCHLIMIT, value); + } + + public String getSearchLimit() { + return getRequestVariable(SEARCHLIMIT); + } + + public void setLimit(String value) { + setRequestVariable(SEARCHLIMIT, value); + } + + public String getLimit() { + return getRequestVariable(SEARCHLIMIT); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchOffset(String value) { + setRequestVariable(SEARCHOFFSET, value); + } + + public String getSearchOffset() { + return getRequestVariable(SEARCHOFFSET); + } + + public void setOffset(String value) { + setRequestVariable(SEARCHOFFSET, value); + } + + public String getOffset() { + return getRequestVariable(SEARCHOFFSET); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchSelect(String value) { + setRequestVariable(SEARCHSELECT, value); + } + + public String getSearchSelect() { + return getRequestVariable(SEARCHSELECT); + } + + public void setSelect(String value) { + setRequestVariable(SEARCHSELECT, value); + } + + public String getSelect() { + return getRequestVariable(SEARCHSELECT); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchDelimiter(String value) { + setRequestVariable(SEARCHDELIMITER, value); + } + + public String getSearchDelimiter() { + return getRequestVariable(SEARCHDELIMITER); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchRestrictedIndicator(String value) { + setRequestVariable(SEARCHRESTRICTEDINDICATOR, value); + } + + public String getSearchRestrictedIndicator() { + return getRequestVariable(SEARCHRESTRICTEDINDICATOR); + } + + /////////////////////////////////////////////////////////////////////// + public void setSearchStandardNames(String value) { + setRequestVariable(SEARCHSTANDARDNAMES, value); + } + + public String getSearchStandardNames() { + return getRequestVariable(SEARCHSTANDARDNAMES); + } + + public void setStandardNames(String value) { + setRequestVariable(SEARCHSTANDARDNAMES, value); + } + + public String getStandardNames() { + return getRequestVariable(SEARCHSTANDARDNAMES); + } + + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSServerInformationTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSServerInformationTransaction.java new file mode 100644 index 0000000..f3505fb --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSServerInformationTransaction.java @@ -0,0 +1,48 @@ +/** + * RETSServerInformationTransaction.java + * + * @author pobrien + * @version + */ +package org.realtor.rets.retsapi; + + +//import java.util.*; +import org.apache.log4j.*; + + +/////////////////////////////////////////////////////////////////////// +public class RETSServerInformationTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSServerInformationTransaction.class); + String version = null; + + /** + * constructor + */ + public RETSServerInformationTransaction() { + super(); + setRequestType("ServerInformation"); + } + + public void setResource(String str) { + setRequestVariable("Resource", str); + } + + public void setInfoClass(String str) { + setRequestVariable("Class", str); + } + + public void setStandardNames(String str) { + setRequestVariable("StandardNames", str); + } + + + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } + +} diff --git a/src/main/java/org/realtor/rets/retsapi/RETSTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSTransaction.java new file mode 100644 index 0000000..a2876d4 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSTransaction.java @@ -0,0 +1,274 @@ +/** + * RETSTransaction.java + * + * @author jbrush + * @version + */ +package org.realtor.rets.retsapi; + +import org.apache.log4j.*; + +import org.apache.regexp.*; + +import org.realtor.rets.util.*; + +import java.io.*; + +import java.util.*; + + +/////////////////////////////////////////////////////////////////////// + +public class RETSTransaction extends RETSRequestResponse { + static Category cat = Category.getInstance(RETSTransaction.class); + private static final String STATUS = "status"; + private static final String STATUSTEXT = "statusText"; + private static final String BODY = "body"; + private static final String REQUESTTYPE = "requestType"; + protected static final String RESOURCE = "Resource"; + protected static final String CLASS_NAME = "ClassName"; + protected HashMap transactionContext = null; + private HashMap capabilityUrls = null; + protected HashMap responseHeaderMap = null; + InputStream responseStream = null; + RE firstStatusRE = null; + RE secondStatusRE = null; + + /** + * Holds value of property compressionFormat. + */ + private String compressionFormat = null; + + public RETSTransaction() { + super(); + + try { + // TODO: RE's should be precompiled + firstStatusRE = new RE("= 0) { + setResponseVariable(line.substring(0, equalSign).trim(), + line.substring(equalSign + 1).trim()); + } + } + } + } + + /////////////////////////////////////////////////////////////////////// + void putCapabilityUrl(String key, String value) { + if (capabilityUrls == null) { + capabilityUrls = new HashMap(); + } + + capabilityUrls.put(key, value); + } + + public String getCapabilityUrl(String key) { + return (String) capabilityUrls.get(key); + } + + /////////////////////////////////////////////////////////////////////// + public void preprocess() { + // by default does nothing + //subclasses can override + } + + public void postprocess() { + // by default does nothing + //subclasses can override + } + + void setContext(HashMap transactionContext) { + if (transactionContext != null) { + this.transactionContext = transactionContext; + + capabilityUrls = (HashMap) transactionContext.get("capabilityUrls"); + + if (capabilityUrls == null) { + capabilityUrls = new HashMap(); + transactionContext.put("capabilityUrls", capabilityUrls); + } + } + } + + public HashMap getTransactionContext() { + return transactionContext; + } + + public HashMap getResponseHeaderMap() { + return responseHeaderMap; + } + + public void setResponseHeaderMap(HashMap responseHeaders) { + responseHeaderMap = responseHeaders; + } + + /** + * Returns the value of the response header with the specified name, or null + * if the header was not returned. + * + * @param headerName The name of the header to be retrieved. + */ + public String getResponseHeader(String headerName) { + String responseString = null; + // If we have no header map, we obviously have no headers. Also, if + // there is no list for the header name, we don't have the + // requested header. + if ( headerName != null && headerName.equals("content-type") ) { + headerName = "Content-Type"; + } + if (responseHeaderMap != null) { + cat.debug("RESPONSEHEADERMAP ==> " + responseHeaderMap.toString()); +// responseString = (String) responseHeaderMap.get(headerName.toLowerCase()); + cat.debug("ContentType Class is ... " + responseHeaderMap.get(headerName).getClass().getName()); + Object object = responseHeaderMap.get(headerName); + if ( object == null ) + return null; + if ( object instanceof ArrayList ) { + responseString = (String) ((ArrayList)object).get(0); + } else + responseString = object.toString(); + } else { + cat.debug("RESPONSEHEADERMAP ==> " + responseHeaderMap); + } + return responseString; + } + + /** + * Getter for property compressionFormat. + * + * @return Value of property compressionFormat. + */ + public String getCompressionFormat() { + return this.compressionFormat; + } + + /** + * Setter for property compressionFormat. + * + * @param compressionFormat New value of property compressionFormat. + */ + public void setCompressionFormat(String compressionFormat) { + this.compressionFormat = compressionFormat; + } + + static public void log(String logMessage) { + cat.debug(logMessage); + } +} + + diff --git a/src/main/java/org/realtor/rets/retsapi/RETSUpdateTransaction.java b/src/main/java/org/realtor/rets/retsapi/RETSUpdateTransaction.java new file mode 100644 index 0000000..7a50290 --- /dev/null +++ b/src/main/java/org/realtor/rets/retsapi/RETSUpdateTransaction.java @@ -0,0 +1,166 @@ +package org.realtor.rets.retsapi; + +import org.apache.log4j.Category; + +import java.util.Iterator; +import java.util.Map; +import org.apache.xpath.XPathAPI; +import org.realtor.rets.util.XMLUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import java.util.Collections; +import java.util.Vector; +import javax.xml.transform.TransformerException; + + +/** + * RETSUpdateTransaction.java + * + * @author pobrien + * @version 1.0 + */ +public class RETSUpdateTransaction extends RETSTransaction { + static Category cat = Category.getInstance(RETSUpdateTransaction.class); + + /** + * + */ + public RETSUpdateTransaction() { + super(); + setRequestType("Update"); + setDelimiter("09");//default is ascii ht + } + + /** + * Sets the response body for the transaction. + * + * @param body body of the transaction + */ + public void setResponse(String body) { + super.setResponse(body); + System.out.println("Setting response as " + body); + setKeyValuePairs(body); + } + + /** + * Sets the type attribute to the string passed in. + * + * @param type type attribute value + */ + public void setType(String str) { + cat.debug("set Type=" + str); + setRequestVariable("Type", str); + } + + /** + * Sets the ID attribute to the string passed in. + * + * @param str ID of the object + */ + public void setValidate(String str) { + cat.debug("set Validate=" + str); + setRequestVariable("Validate", str); + } + + /** + * Sets the location attribute to the string passed in. + * + * @param str location attribute value + */ + public void setDelimiter(String str) { + cat.debug("set Delimiter=" + str); + setRequestVariable("Delimiter", str); + } + + public String getDelimiter() { + return getRequestVariable("Delimiter"); + } + + public void setRecord(String str) { + cat.debug("set Record=" + str); + setRequestVariable("Record", str); + } + + public void setWarningResponse(String str) { + cat.debug("set WarningResponse=" + str); + setRequestVariable("WarningResponse", str); + } + + public void setNewValues(Map m) { + // convert to a string and feed to setRecord().... + StringBuffer record = new StringBuffer(); + Iterator iter = m.keySet().iterator(); + // delimiter is a 2 digit HEX value + char delim = (char) Integer.parseInt(getDelimiter().trim(), 16); + + while (iter.hasNext()) { + String name = (String) iter.next(); + Object val = m.get(name); + String value = ""; + + if (val instanceof String) { + value = (String) val; + } else { + String[] arr = (String[]) val; + value = arr[0]; + } + + record.append(name); + record.append("="); + record.append(value); + + if (iter.hasNext()) { + + record.append(delim); + } + } + + setRecord(record.toString()); + } + + + public void setWarningResponseValues(Map m) { + // convert to a string and feed to setWarningResponse().... + StringBuffer warning = new StringBuffer("("); + Iterator iter = m.keySet().iterator(); + // delimiter is a 2 digit HEX value + char delim = (char) Integer.parseInt(getDelimiter().trim(), 16); + + while (iter.hasNext()) { + String name = (String) iter.next(); + Object val = m.get(name); + String value = ""; + + if (val instanceof String) { + value = (String) val; + } else { + String[] arr = (String[]) val; + value = arr[0]; + } + + warning.append(name); + warning.append("="); + warning.append(value); + + if (iter.hasNext()) { + + warning.append(delim); + } + } + + warning.append(")"); + setWarningResponse(warning.toString()); + } + + public void setUID(String id) { + System.out.println("UID is " + id); + setRequestVariable("OriginalUid", id); + } + + + + + +}