From 4d8d8fbee7a7b3408ea28626ed3a98312e62582e Mon Sep 17 00:00:00 2001 From: YuCheng Hu Date: Tue, 30 Nov 2021 13:26:05 -0500 Subject: [PATCH] USRE-88 Change project to POM module and easier to debug source code --- .idea/compiler.xml | 5 +- .idea/encodings.xml | 4 + .idea/thriftCompiler.xml | 6 - .idea/vcs.xml | 2 +- pom.xml | 40 +- .../client/BrokerCodeRequredException.java | 30 - .../ossez/usreio/client/CapabilityUrls.java | 161 ---- .../usreio/client/ChangePasswordRequest.java | 40 - .../usreio/client/ChangePasswordResponse.java | 36 - .../CollectionOfCollectionsIterator.java | 40 - .../usreio/client/CommonsHttpClient.java | 180 ----- .../client/CommonsHttpClientResponse.java | 110 --- .../ossez/usreio/client/CompactRowPolicy.java | 56 -- .../usreio/client/GenericHttpRequest.java | 36 - .../usreio/client/GetMetadataRequest.java | 73 -- .../usreio/client/GetMetadataResponse.java | 80 -- .../usreio/client/GetObjectIterator.java | 12 - .../ossez/usreio/client/GetObjectRequest.java | 101 --- .../usreio/client/GetObjectResponse.java | 188 ----- .../client/GetObjectResponseIterator.java | 140 ---- .../client/InvalidArgumentException.java | 7 - .../client/InvalidHttpStatusException.java | 12 - .../client/InvalidReplyCodeException.java | 44 -- .../client/InvalidReplyCodeHandler.java | 17 - .../ossez/usreio/client/KeyValueResponse.java | 140 ---- .../com/ossez/usreio/client/LoginRequest.java | 23 - .../ossez/usreio/client/LoginResponse.java | 200 ----- .../ossez/usreio/client/LogoutRequest.java | 9 - .../ossez/usreio/client/LogoutResponse.java | 41 - ...rsionInsensitiveRequest.java => Main.java} | 4 +- .../usreio/client/MetaCollectorAdapter.java | 61 -- .../usreio/client/MetaCollectorImpl.java | 22 - .../usreio/client/NetworkEventMonitor.java | 23 - .../client/NullNetworkEventMonitor.java | 13 - .../com/ossez/usreio/client/ReplyCode.java | 100 --- .../ossez/usreio/client/ReplyCodeHandler.java | 23 - .../ossez/usreio/client/RetsException.java | 22 - .../ossez/usreio/client/RetsHttpClient.java | 36 - .../ossez/usreio/client/RetsHttpRequest.java | 81 -- .../ossez/usreio/client/RetsHttpResponse.java | 26 - .../com/ossez/usreio/client/RetsSession.java | 403 ---------- .../ossez/usreio/client/RetsTransport.java | 334 --------- .../com/ossez/usreio/client/RetsUtil.java | 48 -- .../com/ossez/usreio/client/RetsVersion.java | 106 --- .../ossez/usreio/client/SearchRequest.java | 122 --- .../com/ossez/usreio/client/SearchResult.java | 23 - .../usreio/client/SearchResultCollector.java | 18 - .../usreio/client/SearchResultHandler.java | 280 ------- .../ossez/usreio/client/SearchResultImpl.java | 87 --- .../ossez/usreio/client/SearchResultInfo.java | 25 - .../usreio/client/SearchResultProcessor.java | 13 - .../ossez/usreio/client/SearchResultSet.java | 15 - .../usreio/client/SingleObjectResponse.java | 53 -- .../usreio/client/SinglePartInputStream.java | 64 -- .../StreamingSearchResultProcessor.java | 324 -------- .../client/retsapi/InputStreamDataSource.java | 126 ---- .../client/retsapi/RETSActionTransaction.java | 35 - .../retsapi/RETSBasicResponseParser.java | 128 ---- .../RETSChangePasswordTransaction.java | 176 ----- .../usreio/client/retsapi/RETSConnection.java | 635 ---------------- .../retsapi/RETSGetMetadataTransaction.java | 51 -- .../retsapi/RETSGetObjectTransaction.java | 339 --------- .../client/retsapi/RETSLoginTransaction.java | 184 ----- .../client/retsapi/RETSLogoutTransaction.java | 32 - .../retsapi/RETSSearchAgentTransaction.java | 32 - .../retsapi/RETSSearchOfficeTransaction.java | 27 - .../RETSSearchPropertyBatchTransaction.java | 26 - .../RETSSearchPropertyTransaction.java | 20 - .../client/retsapi/RETSSearchTransaction.java | 321 -------- .../RETSServerInformationTransaction.java | 50 -- .../client/retsapi/RETSTransaction.java | 277 ------- .../client/retsapi/RETSUpdateTransaction.java | 156 ---- .../tests/common/metadata/AttrType.java | 16 - .../common/metadata/JDomCompactBuilder.java | 702 ------------------ .../common/metadata/JDomStandardBuilder.java | 628 ---------------- .../tests/common/metadata/MetaCollector.java | 20 - .../tests/common/metadata/MetaObject.java | 366 --------- .../common/metadata/MetaParseException.java | 26 - .../tests/common/metadata/Metadata.java | 154 ---- .../common/metadata/MetadataBuilder.java | 203 ----- .../common/metadata/MetadataElement.java | 31 - .../common/metadata/MetadataException.java | 27 - .../tests/common/metadata/MetadataType.java | 30 - .../metadata/attrib/AttrAbstractText.java | 49 -- .../common/metadata/attrib/AttrAlphanum.java | 31 - .../common/metadata/attrib/AttrBoolean.java | 54 -- .../common/metadata/attrib/AttrDate.java | 71 -- .../common/metadata/attrib/AttrEnum.java | 31 - .../metadata/attrib/AttrGenericText.java | 31 - .../common/metadata/attrib/AttrNumeric.java | 31 - .../metadata/attrib/AttrNumericPositive.java | 36 - .../common/metadata/attrib/AttrPlaintext.java | 28 - .../common/metadata/attrib/AttrText.java | 28 - .../common/metadata/attrib/AttrVersion.java | 66 -- .../tests/common/metadata/types/MClass.java | 126 ---- .../common/metadata/types/MEditMask.java | 50 -- .../common/metadata/types/MForeignKey.java | 87 --- .../tests/common/metadata/types/MLookup.java | 86 --- .../common/metadata/types/MLookupType.java | 56 -- .../tests/common/metadata/types/MObject.java | 79 -- .../common/metadata/types/MResource.java | 270 ------- .../common/metadata/types/MSearchHelp.java | 50 -- .../tests/common/metadata/types/MSystem.java | 89 --- .../tests/common/metadata/types/MTable.java | 242 ------ .../tests/common/metadata/types/MUpdate.java | 91 --- .../common/metadata/types/MUpdateHelp.java | 50 -- .../common/metadata/types/MUpdateType.java | 103 --- .../metadata/types/MValidationExpression.java | 61 -- .../metadata/types/MValidationExternal.java | 76 -- .../types/MValidationExternalType.java | 57 -- .../metadata/types/MValidationLookup.java | 76 -- .../metadata/types/MValidationLookupType.java | 56 -- .../com/ossez/usreio/util/SessionUtils.java | 62 -- .../usreio/tests/client/ConnectionTest.java | 52 -- .../tests/client/GetMetadataRequestTest.java | 58 -- .../tests/client/GetMetadataResponseTest.java | 156 ---- .../client/GetObjectResponseIteratorTest.java | 194 ----- .../usreio/tests/client/IOFailReader.java | 35 - .../usreio/tests/client/LoginRequestTest.java | 24 - .../tests/client/LoginResponseTest.java | 133 ---- .../tests/client/LogoutResponseTest.java | 57 -- .../tests/client/RetsGetObjectExample.java | 122 --- .../tests/client/RetsGetObjectURLExample.java | 94 --- .../usreio/tests/client/RetsMetadataTest.java | 96 --- .../tests/client/RetsSearchExample.java | 84 --- .../usreio/tests/client/RetsSessionTest.java | 69 -- .../usreio/tests/client/RetsTestCase.java | 87 --- .../usreio/tests/client/RetsVersionTest.java | 31 - .../tests/client/SearchResultHandlerTest.java | 153 ---- .../tests/client/SearchResultImplTest.java | 58 -- .../client/SingleObjectResponseTest.java | 37 - .../StreamingSearchResultProcessorTest.java | 200 ----- .../client/TestInvalidReplyCodeHandler.java | 20 - .../tests/client/objects-missing.multipart | Bin 105875 -> 0 bytes .../tests/common/metadata/MetaObjectTest.java | 44 -- .../common/metadata/MetadataTestCase.java | 14 - .../tests/common/metadata/TestMetaObject.java | 48 -- .../metadata/attrib/AttrAbstractTextTest.java | 38 - .../metadata/attrib/AttrAlphanumTest.java | 45 -- .../metadata/attrib/AttrBooleanTest.java | 46 -- .../common/metadata/attrib/AttrDateTest.java | 27 - .../common/metadata/attrib/AttrEnumTest.java | 17 - .../metadata/attrib/AttrGenericTextTest.java | 17 - .../metadata/attrib/AttrNumericTest.java | 21 - .../metadata/attrib/AttrPlaintextTest.java | 27 - .../common/metadata/attrib/AttrTextTest.java | 23 - .../common/metadata/attrib/AttrTypeTest.java | 28 - .../metadata/attrib/AttrVersionTest.java | 32 - .../util/CaseInsensitiveTreeMapTest.java | 54 -- src/test/resources/2237858_0.jpg | Bin 76861 -> 0 bytes .../getMetadataResponse_lookupZero.xml | 18 - .../getMetadataResponse_noRecords.xml | 1 - .../resources/getMetadataResponse_system.xml | 6 - .../getMetadataResponse_updateType.xml | 7 - src/test/resources/log4j2.xml | 97 --- src/test/resources/login_lower_case.xml | 17 - .../resources/login_response_valid_1.0.xml | 16 - .../resources/login_response_valid_1.5.xml | 18 - .../resources/login_response_valid_1.7.xml | 17 - .../resources/login_response_valid_1.8.xml | 17 - .../resources/login_response_valid_1.9.xml | 17 - src/test/resources/login_valid10.xml | 16 - src/test/resources/login_valid15.xml | 17 - src/test/resources/logout_lower_case.xml | 6 - src/test/resources/logout_no_equals.xml | 4 - src/test/resources/logout_valid10.xml | 5 - src/test/resources/logout_valid15.xml | 6 - src/test/resources/objects-missing.multipart | Bin 105875 -> 0 bytes src/test/resources/rets.properties | 4 - 169 files changed, 15 insertions(+), 13715 deletions(-) delete mode 100644 .idea/thriftCompiler.xml delete mode 100644 src/main/java/com/ossez/usreio/client/BrokerCodeRequredException.java delete mode 100644 src/main/java/com/ossez/usreio/client/CapabilityUrls.java delete mode 100644 src/main/java/com/ossez/usreio/client/ChangePasswordRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/ChangePasswordResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/CollectionOfCollectionsIterator.java delete mode 100644 src/main/java/com/ossez/usreio/client/CommonsHttpClient.java delete mode 100644 src/main/java/com/ossez/usreio/client/CommonsHttpClientResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/CompactRowPolicy.java delete mode 100644 src/main/java/com/ossez/usreio/client/GenericHttpRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/GetMetadataRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/GetMetadataResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/GetObjectIterator.java delete mode 100644 src/main/java/com/ossez/usreio/client/GetObjectRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/GetObjectResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/GetObjectResponseIterator.java delete mode 100644 src/main/java/com/ossez/usreio/client/InvalidArgumentException.java delete mode 100644 src/main/java/com/ossez/usreio/client/InvalidHttpStatusException.java delete mode 100644 src/main/java/com/ossez/usreio/client/InvalidReplyCodeException.java delete mode 100644 src/main/java/com/ossez/usreio/client/InvalidReplyCodeHandler.java delete mode 100644 src/main/java/com/ossez/usreio/client/KeyValueResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/LoginRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/LoginResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/LogoutRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/LogoutResponse.java rename src/main/java/com/ossez/usreio/client/{VersionInsensitiveRequest.java => Main.java} (70%) delete mode 100644 src/main/java/com/ossez/usreio/client/MetaCollectorAdapter.java delete mode 100644 src/main/java/com/ossez/usreio/client/MetaCollectorImpl.java delete mode 100644 src/main/java/com/ossez/usreio/client/NetworkEventMonitor.java delete mode 100644 src/main/java/com/ossez/usreio/client/NullNetworkEventMonitor.java delete mode 100644 src/main/java/com/ossez/usreio/client/ReplyCode.java delete mode 100644 src/main/java/com/ossez/usreio/client/ReplyCodeHandler.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsException.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsHttpClient.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsHttpRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsHttpResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsSession.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsTransport.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsUtil.java delete mode 100644 src/main/java/com/ossez/usreio/client/RetsVersion.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchRequest.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResult.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResultCollector.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResultHandler.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResultImpl.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResultInfo.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResultProcessor.java delete mode 100644 src/main/java/com/ossez/usreio/client/SearchResultSet.java delete mode 100644 src/main/java/com/ossez/usreio/client/SingleObjectResponse.java delete mode 100644 src/main/java/com/ossez/usreio/client/SinglePartInputStream.java delete mode 100644 src/main/java/com/ossez/usreio/client/StreamingSearchResultProcessor.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/InputStreamDataSource.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSActionTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSBasicResponseParser.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSChangePasswordTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSConnection.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSGetMetadataTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSGetObjectTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSLoginTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSLogoutTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSSearchAgentTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSSearchOfficeTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyBatchTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSSearchTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSServerInformationTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/client/retsapi/RETSUpdateTransaction.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/AttrType.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/JDomCompactBuilder.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/JDomStandardBuilder.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetaCollector.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetaObject.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetaParseException.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/Metadata.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetadataBuilder.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetadataElement.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetadataException.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/MetadataType.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAbstractText.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAlphanum.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrBoolean.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrDate.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrEnum.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrGenericText.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumeric.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumericPositive.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrPlaintext.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrText.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrVersion.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MClass.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MEditMask.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MForeignKey.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookup.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookupType.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MObject.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MResource.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MSearchHelp.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MSystem.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MTable.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdate.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateHelp.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateType.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExpression.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternal.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternalType.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookup.java delete mode 100644 src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookupType.java delete mode 100644 src/main/java/com/ossez/usreio/util/SessionUtils.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/ConnectionTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/GetMetadataRequestTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/GetMetadataResponseTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/GetObjectResponseIteratorTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/IOFailReader.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/LoginRequestTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/LoginResponseTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/LogoutResponseTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsGetObjectExample.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsGetObjectURLExample.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsMetadataTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsSearchExample.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsSessionTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsTestCase.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/RetsVersionTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/SearchResultHandlerTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/SearchResultImplTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/SingleObjectResponseTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/StreamingSearchResultProcessorTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/TestInvalidReplyCodeHandler.java delete mode 100644 src/test/java/com/ossez/usreio/tests/client/objects-missing.multipart delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/MetaObjectTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/MetadataTestCase.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/TestMetaObject.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAbstractTextTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAlphanumTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrBooleanTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrDateTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrEnumTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrGenericTextTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumericTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrPlaintextTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrTextTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrTypeTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/metadata/attrib/AttrVersionTest.java delete mode 100644 src/test/java/com/ossez/usreio/tests/common/util/CaseInsensitiveTreeMapTest.java delete mode 100644 src/test/resources/2237858_0.jpg delete mode 100644 src/test/resources/getMetadataResponse_lookupZero.xml delete mode 100644 src/test/resources/getMetadataResponse_noRecords.xml delete mode 100644 src/test/resources/getMetadataResponse_system.xml delete mode 100644 src/test/resources/getMetadataResponse_updateType.xml delete mode 100644 src/test/resources/log4j2.xml delete mode 100644 src/test/resources/login_lower_case.xml delete mode 100644 src/test/resources/login_response_valid_1.0.xml delete mode 100644 src/test/resources/login_response_valid_1.5.xml delete mode 100644 src/test/resources/login_response_valid_1.7.xml delete mode 100644 src/test/resources/login_response_valid_1.8.xml delete mode 100644 src/test/resources/login_response_valid_1.9.xml delete mode 100644 src/test/resources/login_valid10.xml delete mode 100644 src/test/resources/login_valid15.xml delete mode 100644 src/test/resources/logout_lower_case.xml delete mode 100644 src/test/resources/logout_no_equals.xml delete mode 100644 src/test/resources/logout_valid10.xml delete mode 100644 src/test/resources/logout_valid15.xml delete mode 100644 src/test/resources/objects-missing.multipart delete mode 100644 src/test/resources/rets.properties diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 0b86ec4..a3390ec 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -6,12 +6,9 @@ + - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index aa00ffa..29f1d39 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,6 +1,10 @@ + + + + diff --git a/.idea/thriftCompiler.xml b/.idea/thriftCompiler.xml deleted file mode 100644 index 7bc123c..0000000 --- a/.idea/thriftCompiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5905cf0..c0a19e8 100644 --- a/pom.xml +++ b/pom.xml @@ -2,11 +2,11 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.ossez.usreio - rets-io-client + rets-io 0.0.1-SNAPSHOT - jar + pom - Rets-Io-Client + Rets-Io 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. @@ -25,28 +25,12 @@ - - - local - - local - /tmp/files - - - true - - - + + rets-io-common + rets-io-client + - - - com.ossez.usreio - rets-io-common - 0.0.1-SNAPSHOT - - - ch.qos.logback @@ -118,7 +102,6 @@ - commons-codec commons-codec @@ -148,15 +131,6 @@ - - maven-compiler-plugin - 3.8.1 - - ${java.version} - ${java.version} - - - maven-assembly-plugin diff --git a/src/main/java/com/ossez/usreio/client/BrokerCodeRequredException.java b/src/main/java/com/ossez/usreio/client/BrokerCodeRequredException.java deleted file mode 100644 index 510af62..0000000 --- a/src/main/java/com/ossez/usreio/client/BrokerCodeRequredException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.client; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * dbt is lame and hasn't overridden the default - * javadoc string. - */ -public class BrokerCodeRequredException extends RetsException { - private final List mCodeList; - - public BrokerCodeRequredException(Collection codes) { - this.mCodeList = Collections.unmodifiableList(new ArrayList(codes)); - } - - public List getCodeList(){ - return this.mCodeList; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/CapabilityUrls.java b/src/main/java/com/ossez/usreio/client/CapabilityUrls.java deleted file mode 100644 index 913f60e..0000000 --- a/src/main/java/com/ossez/usreio/client/CapabilityUrls.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.ossez.usreio.client; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; - -/** - * - */ -public class CapabilityUrls { - - private final static Logger logger = LoggerFactory.getLogger(CapabilityUrls.class); - - public static final String ACTION_URL = "Action"; - public static final String CHANGE_PASSWORD_URL = "ChangePassword"; - public static final String GET_OBJECT_URL = "GetObject"; - public static final String LOGIN_URL = "Login"; - public static final String LOGIN_COMPLETE_URL = "LoginComplete"; - public static final String LOGOUT_URL = "Logout"; - public static final String SEARCH_URL = "Search"; - public static final String GET_METADATA_URL = "GetMetadata"; - public static final String UPDATE_URL = "Update"; - public static final String SERVER_INFO_URL = "ServerInformation";// for rets 1.7 - - private final Map mCapabilityUrls; - private URL mUrl; - - public CapabilityUrls() { - this(null); - } - - public CapabilityUrls(URL baseurl) { - this.mUrl = baseurl; - this.mCapabilityUrls = new HashMap(); - } - - public void setCapabilityUrl(String capability, String url) { - if (this.mUrl != null) { - try { - String newurl = new URL(this.mUrl, url).toString(); - if (!newurl.equals(url)) { - logger.info("qualified " + capability + " URL different: " - + url + " -> " + newurl); - url = newurl; - } - - } catch (MalformedURLException e) { - logger.warn("Couldn't normalize URL", e); - } - } - this.mCapabilityUrls.put(capability, url); - - } - - public String getCapabilityUrl(String capability) { - return (String) this.mCapabilityUrls.get(capability); - } - - public void setActionUrl(String url) { - setCapabilityUrl(ACTION_URL, url); - } - - public String getActionUrl() { - return getCapabilityUrl(ACTION_URL); - } - - public void setChangePasswordUrl(String url) { - setCapabilityUrl(CHANGE_PASSWORD_URL, url); - } - - public String getChangePasswordUrl() { - return getCapabilityUrl(CHANGE_PASSWORD_URL); - } - - public void setGetObjectUrl(String url) { - setCapabilityUrl(GET_OBJECT_URL, url); - } - - public String getGetObjectUrl() { - return getCapabilityUrl(GET_OBJECT_URL); - } - - /** - * - * @param url - */ - public void setLoginUrl(String url) { - if (this.mUrl == null) { - try { - this.mUrl = new URL(url); - } catch (MalformedURLException e) { - logger.debug("java.net.URL can't parse login url: " + url); - this.mUrl = null; - } - } - setCapabilityUrl(LOGIN_URL, url); - } - - public String getLoginUrl() { - return getCapabilityUrl(LOGIN_URL); - } - - public void setLoginCompleteUrl(String url) { - setCapabilityUrl(LOGIN_COMPLETE_URL, url); - } - - public String getLoginCompleteUrl() { - return getCapabilityUrl(LOGIN_COMPLETE_URL); - } - - public void setLogoutUrl(String url) { - setCapabilityUrl(LOGOUT_URL, url); - } - - public String getLogoutUrl() { - return getCapabilityUrl(LOGOUT_URL); - } - - public void setSearchUrl(String url) { - setCapabilityUrl(SEARCH_URL, url); - } - - public String getSearchUrl() { - return getCapabilityUrl(SEARCH_URL); - } - - public void setGetMetadataUrl(String url) { - setCapabilityUrl(GET_METADATA_URL, url); - } - - public String getGetMetadataUrl() { - return getCapabilityUrl(GET_METADATA_URL); - } - - public void setUpdateUrl(String url) { - setCapabilityUrl(UPDATE_URL, url); - } - - public String getUpdateUrl() { - return getCapabilityUrl(UPDATE_URL); - } - /** - * This is for RETS 1.7 and later and will return an empty string if it is not implemented. - * @param url - */ - public void setServerInfo(String url) { - setCapabilityUrl(SERVER_INFO_URL, url); - } - /** - * This is for RETS 1.7 and later and will return an empty string if it is not implemented. - * @return - */ - public String getServerInfo() { - return getCapabilityUrl(SERVER_INFO_URL); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/ChangePasswordRequest.java b/src/main/java/com/ossez/usreio/client/ChangePasswordRequest.java deleted file mode 100644 index af7ac26..0000000 --- a/src/main/java/com/ossez/usreio/client/ChangePasswordRequest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.ossez.usreio.client; - -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESKeySpec; - -import org.apache.commons.codec.binary.Base64; - -public class ChangePasswordRequest extends VersionInsensitiveRequest { - public ChangePasswordRequest(String username, String oldPassword, String newPassword) throws RetsException { - try { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - md5.update(username.toUpperCase().getBytes()); - md5.update(oldPassword.toUpperCase().getBytes()); - byte[] digest = md5.digest(); - DESKeySpec keyspec = new DESKeySpec(digest); - SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(keyspec); - Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, key); - cipher.update(newPassword.getBytes()); - cipher.update(":".getBytes()); - cipher.update(username.getBytes()); - md5.reset(); - md5.update(cipher.doFinal()); - byte[] output = md5.digest(); - byte[] param = Base64.encodeBase64(output); - setQueryParameter("PWD", new String(param)); - } catch (GeneralSecurityException e) { - throw new RetsException(e); - } - } - - @Override - public void setUrl(CapabilityUrls urls) { - this.setUrl(urls.getChangePasswordUrl()); - } -} diff --git a/src/main/java/com/ossez/usreio/client/ChangePasswordResponse.java b/src/main/java/com/ossez/usreio/client/ChangePasswordResponse.java deleted file mode 100644 index 9370c2e..0000000 --- a/src/main/java/com/ossez/usreio/client/ChangePasswordResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ossez.usreio.client; - -import org.dom4j.Document; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; - -import java.io.InputStream; - - -/** - * dbt is lame and hasn't overridden the default - * javadoc string. - */ -public class ChangePasswordResponse { - public ChangePasswordResponse(InputStream stream) throws RetsException { - SAXReader builder = new SAXReader(); - Document document = null; - try { - document = builder.read(stream); - } catch (Exception e) { - throw new RetsException(e); - } - Element rets = document.getRootElement(); - if (!rets.getName().equals("RETS")) { - throw new RetsException("Invalid Change Password Response"); - } - - int replyCode = Integer.parseInt(rets.attributeValue("ReplyCode")); - if (replyCode != 0) { - InvalidReplyCodeException exception; - exception = new InvalidReplyCodeException(replyCode); - exception.setRemoteMessage(rets.attributeValue("ReplyText")); - throw exception; - } - } -} diff --git a/src/main/java/com/ossez/usreio/client/CollectionOfCollectionsIterator.java b/src/main/java/com/ossez/usreio/client/CollectionOfCollectionsIterator.java deleted file mode 100644 index da398f5..0000000 --- a/src/main/java/com/ossez/usreio/client/CollectionOfCollectionsIterator.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.ossez.usreio.client; - -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; - -public class CollectionOfCollectionsIterator implements Iterator { - private Iterator mOuter; - private Iterator mInner; - - public CollectionOfCollectionsIterator(Collection c) { - this.mOuter = c.iterator(); - hasNext(); - } - - public boolean hasNext() { - if( this.mInner != null && this.mInner.hasNext() ) { - return true; - } - while( this.mOuter.hasNext() ){ - this.mInner = ((Collection) this.mOuter.next()).iterator(); - if( this.mInner.hasNext() ){ - return true; - } - } - return false; - } - - public Object next() { - if ( this.hasNext() ) - return this.mInner.next(); - - throw new NoSuchElementException(); - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/CommonsHttpClient.java b/src/main/java/com/ossez/usreio/client/CommonsHttpClient.java deleted file mode 100644 index 84a14ff..0000000 --- a/src/main/java/com/ossez/usreio/client/CommonsHttpClient.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.UnsupportedEncodingException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.ossez.usreio.common.util.CaseInsensitiveTreeMap; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.params.CookiePolicy; -import org.apache.http.cookie.Cookie; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; - -public class CommonsHttpClient extends RetsHttpClient { - private static final int DEFAULT_TIMEOUT = 300000; - private static final String RETS_VERSION = "RETS-Version"; - private static final String RETS_SESSION_ID = "RETS-Session-ID"; - private static final String RETS_REQUEST_ID = "RETS-Request-ID"; - private static final String USER_AGENT = "User-Agent"; - private static final String RETS_UA_AUTH_HEADER = "RETS-UA-Authorization"; - private static final String ACCEPT_ENCODING = "Accept-Encoding"; - public static final String CONTENT_ENCODING = "Content-Encoding"; - public static final String DEFLATE_ENCODINGS = "gzip,deflate"; - public static final String CONTENT_TYPE = "Content-Type"; - - public static BasicHttpParams defaultParams(int timeout) { - BasicHttpParams httpClientParams = new BasicHttpParams(); - // connection to server timeouts - HttpConnectionParams.setConnectionTimeout(httpClientParams, timeout); - HttpConnectionParams.setSoTimeout(httpClientParams, timeout); - // set to rfc 2109 as it puts the ASP (IIS) cookie _FIRST_, this is critical for interealty - httpClientParams.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109); - return httpClientParams; - } - public static ThreadSafeClientConnManager defaultConnectionManager(int maxConnectionsPerRoute, int maxConnectionsTotal) { - // allows for multi threaded requests from a single client - ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(); - connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); - connectionManager.setMaxTotal(maxConnectionsTotal); - return connectionManager; - } - - private final ConcurrentHashMap defaultHeaders; - private final DefaultHttpClient httpClient; - - // method choice improvement - private final String userAgentPassword; - - public CommonsHttpClient() { - this(new DefaultHttpClient(defaultConnectionManager(Integer.MAX_VALUE, Integer.MAX_VALUE), defaultParams(DEFAULT_TIMEOUT)), null, true); - } - - public CommonsHttpClient(int timeout, String userAgentPassword, boolean gzip) { - this(new DefaultHttpClient(defaultConnectionManager(Integer.MAX_VALUE, Integer.MAX_VALUE), defaultParams(timeout)), userAgentPassword, gzip); - } - - public CommonsHttpClient(DefaultHttpClient client, String userAgentPassword, boolean gzip) { - this.defaultHeaders = new ConcurrentHashMap(); - this.userAgentPassword = userAgentPassword; - - this.httpClient = client; - // ask the server if we can use gzip - if( gzip ) this.addDefaultHeader(ACCEPT_ENCODING, DEFLATE_ENCODINGS); - } - - public DefaultHttpClient getHttpClient(){ - return this.httpClient; - } - - //----------------------method implementations - @Override - public void setUserCredentials(String userName, String password) { - UsernamePasswordCredentials creds = new UsernamePasswordCredentials(userName, password); - this.httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, creds); - } - @Override - public RetsHttpResponse doRequest(String httpMethod, RetsHttpRequest request) throws RetsException { - return "GET".equals(StringUtils.upperCase(httpMethod)) ? this.doGet(request) : this.doPost(request); - } - - //----------------------method implementations - public RetsHttpResponse doGet(RetsHttpRequest request) throws RetsException { - String url = request.getUrl(); - String args = request.getHttpParameters(); - if (args != null) { - url = url + "?" + args; - } - HttpGet method = new HttpGet(url); - return execute(method, request.getHeaders()); - } - - public RetsHttpResponse doPost(RetsHttpRequest request) throws RetsException { - String url = request.getUrl(); - String body = request.getHttpParameters(); - if (body == null) body = ""; // commons-httpclient 3.0 refuses to accept null entity (body) - HttpPost method = new HttpPost(url); - try { - method.setEntity(new StringEntity(body, null, null)); - } catch (UnsupportedEncodingException e) { - throw new RetsException(e); - } - //updated Content-Type, application/x-www-url-encoded no longer supported - method.setHeader("Content-Type", "application/x-www-form-urlencoded"); - return execute(method, request.getHeaders()); - } - - protected RetsHttpResponse execute(final HttpRequestBase method, Map headers) throws RetsException { - try { - // add the default headers - if (this.defaultHeaders != null) { - for (Map.Entry entry : this.defaultHeaders.entrySet()) { - method.setHeader(entry.getKey(), entry.getValue()); - } - } - // add our request headers from rets - if (headers != null) { - for (Map.Entry entry : headers.entrySet()) { - method.setHeader(entry.getKey(), entry.getValue()); - } - } - // optional ua-auth stuff here - if( this.userAgentPassword != null ){ - method.setHeader(RETS_UA_AUTH_HEADER, calculateUaAuthHeader(method,getCookies())); - } - // try to execute the request - HttpResponse response = this.httpClient.execute(method); - StatusLine status = response.getStatusLine(); - if (status.getStatusCode() != HttpStatus.SC_OK) { - throw new InvalidHttpStatusException(status); - } - return new CommonsHttpClientResponse(response, getCookies()); - } catch (Exception e) { - throw new RetsException(e); - } - } - - @Override - public synchronized void addDefaultHeader(String key, String value) { - this.defaultHeaders.put(key, value); - if( value == null ) this.defaultHeaders.remove(key); - } - - protected Map getCookies() { - Map cookieMap = new CaseInsensitiveTreeMap(); - for (Cookie cookie : this.httpClient.getCookieStore().getCookies()) { - cookieMap.put(cookie.getName(), cookie.getValue()); - } - return cookieMap; - } - - protected String calculateUaAuthHeader(HttpRequestBase method, Map cookies ) { - final String userAgent = this.getHeaderValue(method, USER_AGENT); - final String requestId = this.getHeaderValue(method, RETS_REQUEST_ID); - final String sessionId = cookies.get(RETS_SESSION_ID); - final String retsVersion = this.getHeaderValue(method, RETS_VERSION); - String secretHash = DigestUtils.md5Hex(String.format("%s:%s",userAgent,this.userAgentPassword)); - String pieces = String.format("%s:%s:%s:%s",secretHash,StringUtils.trimToEmpty(requestId),StringUtils.trimToEmpty(sessionId),retsVersion); - return String.format("Digest %s", DigestUtils.md5Hex(pieces)); - } - - protected String getHeaderValue(HttpRequestBase method, String key){ - Header requestHeader = method.getFirstHeader(key); - if( requestHeader == null ) return null; - return requestHeader.getValue(); - } -} diff --git a/src/main/java/com/ossez/usreio/client/CommonsHttpClientResponse.java b/src/main/java/com/ossez/usreio/client/CommonsHttpClientResponse.java deleted file mode 100644 index 4236b63..0000000 --- a/src/main/java/com/ossez/usreio/client/CommonsHttpClientResponse.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import java.util.zip.GZIPInputStream; - -import com.ossez.usreio.common.util.CaseInsensitiveTreeMap; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import com.google.common.io.Closeables; - -public class CommonsHttpClientResponse implements RetsHttpResponse { - private HttpResponse response; - private Map headers; - private Map cookies; - - public CommonsHttpClientResponse(HttpResponse response, Map cookies) { - this.response = response; - this.cookies = new CaseInsensitiveTreeMap(cookies); - - this.headers = new CaseInsensitiveTreeMap(); - for (Header header : this.response.getAllHeaders()) { - this.headers.put(header.getName(), header.getValue()); - } - } - - public int getResponseCode() { - return this.response.getStatusLine().getStatusCode(); - } - - public Map getHeaders() { - return this.headers; - } - - public String getHeader(String header) { - return this.headers.get(header); - } - - - public Map getCookies() throws RetsException { - return this.cookies; - } - - - public String getCookie(String cookie) throws RetsException { - return this.cookies.get(cookie); - } - - - public String getCharset() { - String contentType = StringUtils.trimToEmpty(this.getHeader(CommonsHttpClient.CONTENT_TYPE)).toLowerCase(); - String[] split = StringUtils.split(contentType, ";"); - if (split == null) return null; - - for (String s : split) { - String sLower = s.toLowerCase().trim(); - boolean b = sLower.startsWith("charset="); - if (b){ - return StringUtils.substringAfter(s, "charset="); - } - } - return null; - } - - /** using this mess to provide logging, gzipping and httpmethod closing */ - - public InputStream getInputStream() throws RetsException { - try { - // get our underlying stream - InputStream inputStream = this.response.getEntity().getContent(); - // gzipped aware checks - String contentEncoding = StringUtils.trimToEmpty(this.getHeader(CommonsHttpClient.CONTENT_ENCODING)).toLowerCase(); - boolean gzipped = ArrayUtils.contains(CommonsHttpClient.DEFLATE_ENCODINGS.split(","),contentEncoding); - if( gzipped ) inputStream = new GZIPInputStream(inputStream); - - final InputStream in = inputStream; - // the http method close wrapper (necessary) - return new InputStream(){ - - public int read() throws IOException { - return in.read(); - } - - public int read(byte[] b) throws IOException { - return in.read(b); - } - - public int read(byte[] b, int off, int len) throws IOException { - return in.read(b, off, len); - } - - public void close() throws IOException { - // connection release _AFTER_ the input stream has been read - try { - Closeables.close(in, true); - } catch (IOException e) { - // ignore - } - } - }; - } catch (IOException e) { - throw new RetsException(e); - } - } - -} diff --git a/src/main/java/com/ossez/usreio/client/CompactRowPolicy.java b/src/main/java/com/ossez/usreio/client/CompactRowPolicy.java deleted file mode 100644 index 3e6e316..0000000 --- a/src/main/java/com/ossez/usreio/client/CompactRowPolicy.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.logging.LogFactory; - - -public interface CompactRowPolicy { - - /** fail fast and furiously */ - public static final CompactRowPolicy STRICT = new CompactRowPolicy(){ - - public boolean apply(int row, String[] columns, String[] values) { - if( values.length != columns.length ) - throw new IllegalArgumentException(String.format("Invalid number of result columns: got %s, expected %s",values.length, columns.length)); - return true; - }}; - - /** drop everything thats suspect */ - public static final CompactRowPolicy DROP = new CompactRowPolicy(){ - - public boolean apply(int row, String[] columns, String[] values) { - if (values.length != columns.length) { - LogFactory.getLog(CompactRowPolicy.class).warn(String.format("Row %s: Invalid number of result columns: got %s, expected ",row, values.length, columns.length)); - return false; - } - return true; - }}; - - /** fail fast on long rows */ - public static final CompactRowPolicy DEFAULT = new CompactRowPolicy(){ - - public boolean apply(int row, String[] columns, String[] values) { - if (values.length > columns.length) { - throw new IllegalArgumentException(String.format("Invalid number of result columns: got %s, expected %s",values.length, columns.length)); - } - if (values.length < columns.length) { - LogFactory.getLog(CompactRowPolicy.class).warn(String.format("Row %s: Invalid number of result columns: got %s, expected ",row, values.length, columns.length)); - } - return true; - }}; - - /** drop and log long rows, try to keep short rows */ - public static final CompactRowPolicy DROP_LONG = new CompactRowPolicy(){ - - public boolean apply(int row, String[] columns, String[] values) { - if (values.length > columns.length) { - LogFactory.getLog(CompactRowPolicy.class).warn(String.format("Row %s: Invalid number of result columns: got %s, expected ",row, values.length, columns.length)); - return false; - } - if (values.length < columns.length) { - LogFactory.getLog(CompactRowPolicy.class).warn(String.format("Row %s: Invalid number of result columns: got %s, expected ",row, values.length, columns.length)); - } - return true; - }}; - - public boolean apply(int row, String[] columns, String[] values); -} diff --git a/src/main/java/com/ossez/usreio/client/GenericHttpRequest.java b/src/main/java/com/ossez/usreio/client/GenericHttpRequest.java deleted file mode 100644 index 9c02ac6..0000000 --- a/src/main/java/com/ossez/usreio/client/GenericHttpRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ossez.usreio.client; - -/** - * on the off chance you need an ad hoc request object... - */ -public class GenericHttpRequest extends VersionInsensitiveRequest { - - public GenericHttpRequest(){ - // noop - } - - public GenericHttpRequest(String url){ - this.mUrl = url; - } - - /** - * throws an exception. GenericHttpRequest can't have a - * CapabilityUrl - * @param urls the CapabilityUrls object that has nothing we can use - */ - @Override - public void setUrl(CapabilityUrls urls) { - // do nothing - return; - } - - /** - * expose the queryParameter interface to build query arguments. - * @param name the parameter name - * @param value the parameter value - */ - @Override - public void setQueryParameter(String name, String value) { - super.setQueryParameter(name, value); - } -} diff --git a/src/main/java/com/ossez/usreio/client/GetMetadataRequest.java b/src/main/java/com/ossez/usreio/client/GetMetadataRequest.java deleted file mode 100644 index 8330f21..0000000 --- a/src/main/java/com/ossez/usreio/client/GetMetadataRequest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.lang3.StringUtils; - -public class GetMetadataRequest extends VersionInsensitiveRequest { - private static final int COMPACT_FORMAT = 0; - private static final int STANDARD_XML_FORMAT = 1; - public static final String KEY_TYPE = "Type"; - public static final String KEY_ID = "ID"; - public static final String KEY_FORMAT = "Format"; - public static final String FORMAT_STANDARD = "STANDARD-XML"; - public static final String FORMAT_STANDARD_PREFIX = "STANDARD-XML:"; - public static final String FORMAT_COMPACT = "COMPACT"; - - private int format; - private String standardXmlVersion; - - public GetMetadataRequest(String type, String id) throws RetsException { - this(type, new String[] { id }); - } - - public GetMetadataRequest(String type, String[] ids) throws RetsException { - assertValidIds(ids); - type = "METADATA-" + type; - if (type.equals("METADATA-SYSTEM") || type.equals("METADATA-RESOURCE")) { - assertIdZeroOrStar(ids); - } - - setQueryParameter(KEY_TYPE, type); - setQueryParameter(KEY_ID, StringUtils.join(ids, ":")); - setQueryParameter(KEY_FORMAT, FORMAT_STANDARD); - this.format = STANDARD_XML_FORMAT; - } - - @Override - public void setUrl(CapabilityUrls urls) { - setUrl(urls.getGetMetadataUrl()); - } - - private void assertValidIds(String[] ids) throws InvalidArgumentException { - if (ids.length == 0) { - throw new InvalidArgumentException("Expecting at least one ID"); - } - } - - private void assertIdZeroOrStar(String[] ids) throws InvalidArgumentException { - if (ids.length != 1) { - throw new InvalidArgumentException("Expecting 1 ID, but found, " + ids.length); - } - if (!ids[0].equals("0") && !ids[0].equals("*")) { - throw new InvalidArgumentException("Expecting ID of 0 or *, but was " + ids[0]); - } - } - - public void setCompactFormat() { - setQueryParameter(KEY_FORMAT, FORMAT_COMPACT); - this.format = COMPACT_FORMAT; - this.standardXmlVersion = null; - } - - public boolean isCompactFormat() { - return (this.format == COMPACT_FORMAT); - } - - public boolean isStandardXmlFormat() { - return (this.format == STANDARD_XML_FORMAT); - } - - public String getStandardXmlVersion() { - return this.standardXmlVersion; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/GetMetadataResponse.java b/src/main/java/com/ossez/usreio/client/GetMetadataResponse.java deleted file mode 100644 index f8af42c..0000000 --- a/src/main/java/com/ossez/usreio/client/GetMetadataResponse.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.InputStream; -import java.util.List; - -import com.ossez.usreio.tests.common.metadata.JDomCompactBuilder; -import com.ossez.usreio.tests.common.metadata.JDomStandardBuilder; -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataException; -import org.apache.commons.lang3.math.NumberUtils; -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; - - -public class GetMetadataResponse { - private MetaObject[] mMetadataObjs; - - public GetMetadataResponse(InputStream stream, boolean compact, boolean isStrict) throws RetsException { - try { - SAXReader builder = new SAXReader(); - Document document = builder.read(stream); - Element retsElement = document.getRootElement(); - if (!retsElement.getName().equals("RETS")) { - throw new RetsException("Expecting RETS"); - } - int replyCode = NumberUtils.toInt(retsElement.attributeValue("ReplyCode")); - if (ReplyCode.SUCCESS.equals(replyCode)) { - if (compact) { - handleCompactMetadata(document, isStrict); - } else { - handleStandardMetadata(document, isStrict); - } - } else if (ReplyCode.NO_METADATA_FOUND.equals(replyCode)) { - // No metadata is not an exceptional case - handleNoMetadataFound(retsElement); - } else { - InvalidReplyCodeException e = new InvalidReplyCodeException(replyCode); - e.setRemoteMessage(retsElement.attributeValue(retsElement.attributeValue("ReplyText"))); - throw e; - } - } catch (DocumentException e) { - throw new RetsException(e); - } - } - - private void handleNoMetadataFound(Element retsElement) throws RetsException { - List children = retsElement.elements(); - if (children.size() != 0) { - throw new RetsException("Expecting 0 children when results"); - } - this.mMetadataObjs = new MetaObject[0]; - } - - private void handleCompactMetadata(Document document, boolean isStrict) throws RetsException { - try { - JDomCompactBuilder builder = new JDomCompactBuilder(); - builder.setStrict(isStrict); - this.mMetadataObjs = builder.parse(document); - } catch (MetadataException e) { - throw new RetsException(e); - } - } - - private void handleStandardMetadata(Document document, boolean isStrict) throws RetsException { - try { - JDomStandardBuilder builder = new JDomStandardBuilder(); - builder.setStrict(isStrict); - this.mMetadataObjs = builder.parse(document); - } catch (MetadataException e) { - throw new RetsException(e); - } - } - - public MetaObject[] getMetadata() { - return this.mMetadataObjs; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/GetObjectIterator.java b/src/main/java/com/ossez/usreio/client/GetObjectIterator.java deleted file mode 100644 index f5cdd5e..0000000 --- a/src/main/java/com/ossez/usreio/client/GetObjectIterator.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.Closeable; -import java.util.Iterator; - -/** - * Iterator for SingleResoponseObjects - * @param - */ -public interface GetObjectIterator extends Closeable, Iterator{ - // noop -} diff --git a/src/main/java/com/ossez/usreio/client/GetObjectRequest.java b/src/main/java/com/ossez/usreio/client/GetObjectRequest.java deleted file mode 100644 index 6dceea0..0000000 --- a/src/main/java/com/ossez/usreio/client/GetObjectRequest.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.ossez.usreio.client; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.Map; - -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; - -public class GetObjectRequest extends VersionInsensitiveRequest { - public static final String KEY_RESOURCE = "Resource"; - public static final String KEY_TYPE = "Type"; - public static final String KEY_LOCATION = "Location"; - public static final String KEY_ID = "ID"; - - private final Map mMap; - - public GetObjectRequest(String resource, String type) { - this(resource, type, new String[] { "*/*" }); - } - - public GetObjectRequest(String resource, String type, String[] acceptMimeTypes) { - setQueryParameter(KEY_RESOURCE, resource); - setQueryParameter(KEY_TYPE, type); - this.mMap = new HashMap(); - setHeader("Accept", StringUtils.join(acceptMimeTypes, ", ")); - } - - @Override - public void setUrl(CapabilityUrls urls) { - setUrl(urls.getGetObjectUrl()); - } - - public void setLocationOnly(boolean flag) { - if (flag) { - setQueryParameter(KEY_LOCATION, "1"); - } else { - setQueryParameter(KEY_LOCATION, null); - } - } - - public void addObject(String resourceEntity, String id) { - if (id == null) - throw new NullPointerException("Object id should not be null. " - + "Cannot remove object already added to request."); - - Object cur = this.mMap.get(resourceEntity); - if (id.equals("*")) { - this.mMap.put(resourceEntity, id); - } else if (cur == null) { - this.mMap.put(resourceEntity, id); - } else if (cur instanceof String) { - if (ObjectUtils.equals(cur, "*")) { - return; - } - if (ObjectUtils.equals(cur, id)) { - return; - } - Set s = new HashSet(); - s.add(cur); - s.add(id); - this.mMap.put(resourceEntity, s); - } else if (cur instanceof Set) { - ((Set) cur).add(id); - } else { - /* NOTREACHED */ - throw new RuntimeException(resourceEntity + " has invalid value " + "of type " + cur.getClass().getName()); - } - setQueryParameter(KEY_ID, makeIdStr()); - } - - private String makeIdStr() { - StringBuffer id = new StringBuffer(); - Iterator iter = this.mMap.keySet().iterator(); - while (iter.hasNext()) { - String key = (String) iter.next(); - id.append(key); - Object cur = this.mMap.get(key); - if (cur instanceof String) { - id.append(":"); - id.append(cur); - } else if (cur instanceof Set) { - Iterator iter2 = ((Set) cur).iterator(); - while (iter2.hasNext()) { - String val = (String) iter2.next(); - id.append(":"); - id.append(val); - } - } else { - throw new RuntimeException(key + " has invalid value of " + "type " + cur.getClass().getName()); - } - if (iter.hasNext()) { - id.append(","); - } - } - return id.toString(); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/GetObjectResponse.java b/src/main/java/com/ossez/usreio/client/GetObjectResponse.java deleted file mode 100644 index 99ed9ff..0000000 --- a/src/main/java/com/ossez/usreio/client/GetObjectResponse.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import java.util.NoSuchElementException; - -import com.ossez.usreio.common.util.CaseInsensitiveTreeMap; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.http.HeaderElement; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicHeaderValueParser; -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; - -public class GetObjectResponse{ - private static final int DEFAULT_BUFFER_SIZE = 8192; - - private final static GetObjectIterator EMPTY_OBJECT_RESPONSE_ITERATOR = new GetObjectIterator() { - public boolean hasNext() { - return false; - } - public SingleObjectResponse next() { - throw new NoSuchElementException(); - } - public void close() { - /* no op */ - } - public void remove() { - /* no op */ - } - }; - private final Map headers; - private final InputStream inputStream; - private final boolean isMultipart; - /** Indicate whether the response was empty */ - private boolean emptyResponse; - /** Indicate whether this GetObjectResponse is exhausted, i.e. has no objects */ - private boolean exhausted; - - public GetObjectResponse(Map headers, InputStream in) throws RetsException { - this.emptyResponse = false; - this.exhausted = false; - this.headers = new CaseInsensitiveTreeMap(headers); - this.isMultipart = getType().contains("multipart"); - this.inputStream = in; - - boolean isXml = getType().equals("text/xml"); - boolean containsContentId = headers.containsKey(SingleObjectResponse.CONTENT_ID); - // non multipart request that returns text/xml and does NOT have a Context-ID header, must only be a non-zero response code - boolean nonMultiPart_xml_withoutContentId = !this.isMultipart && isXml && !containsContentId; - // multipart request that returns text/xml can only be a non-zero response code - boolean multiPart_xml = this.isMultipart && isXml; - - if ( multiPart_xml || nonMultiPart_xml_withoutContentId ) { - int replyCode = 0; - try { - // GetObjectResponse is empty, because we have a Rets ReplyCode - this.emptyResponse = true; - SAXReader builder = new SAXReader(); - Document mDocument = builder.read(in); - Element root = mDocument.getRootElement(); - if (root.getName().equals("RETS")) { - replyCode = NumberUtils.toInt(root.attributeValue("ReplyCode")); - - // success - if (ReplyCode.SUCCESS.equals(replyCode)) return; - - // no object found - that's fine - if (ReplyCode.NO_OBJECT_FOUND.equals(replyCode)) return; - - throw new InvalidReplyCodeException(replyCode); - } - // no other possibilities - throw new RetsException("Malformed response [multipart="+this.isMultipart+", content-type=text/xml]. " + - "Content id did not exist in response and response was not valid rets response."); - } catch (DocumentException e) { - throw new RetsException(e); - } - } - } - - public String getType() { - return (String) this.headers.get("Content-Type"); - } - - public String getBoundary() { - String contentTypeValue = getType(); - HeaderElement[] contentType = BasicHeaderValueParser.parseElements(contentTypeValue, new BasicHeaderValueParser()); - if (contentType.length != 1) - throw new IllegalArgumentException("Multipart response appears to have a bad Content-Type: header value: " - + contentTypeValue); - - NameValuePair boundaryNV = contentType[0].getParameterByName("boundary"); - if (boundaryNV == null) - return null; - return unescapeBoundary(boundaryNV.getValue()); - } - - private static String unescapeBoundary(String boundaryValue) { - if (boundaryValue.startsWith("\"")) - boundaryValue = boundaryValue.substring(1); - if (boundaryValue.endsWith("\"")) - boundaryValue = boundaryValue.substring(0, boundaryValue.length() - 1); - return boundaryValue; - } - - - /** - * @return GetObjectIterator, which iterates over SingleObjectResponse - * objects. - * - * @throws RetsException - */ - public GetObjectIterator iterator() throws RetsException { - return iterator(DEFAULT_BUFFER_SIZE); - } - - /** - * @return GetObjectIterator, which iterates over SingleObjectResponse - * objects. - * - * @param bufferSize How large a buffer should be used for underlying - * streams. - * - * @throws RetsException - */ - public GetObjectIterator iterator(int bufferSize) throws RetsException { - if(this.exhausted ) - throw new RetsException("response was exhausted - cannot request iterator a second time"); - - if( this.emptyResponse ) - return (GetObjectIterator) EMPTY_OBJECT_RESPONSE_ITERATOR; - - - if( this.isMultipart ){ - try { - return GetObjectResponseIterator.createIterator(this, bufferSize); - } catch (Exception e) { - throw new RetsException("Error creating multipart GetObjectIterator", e); - } - } - // no other possibilities - return new NonMultipartGetObjectResponseIterator(this.headers, this.inputStream); - } - - public InputStream getInputStream() { - return this.inputStream; - } - -} - - -/** Used to implement GetObjectIterator for a non multipart response. */ -final class NonMultipartGetObjectResponseIterator implements GetObjectIterator { - private boolean exhausted; - private final Map headers; - private final InputStream inputStream; - - public NonMultipartGetObjectResponseIterator(Map headers, InputStream in){ - this.exhausted = false; - this.headers = headers; - this.inputStream = in; - } - - - public void close() throws IOException { - this.inputStream.close(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return !this.exhausted; - } - - public SingleObjectResponse next() { - if( this.exhausted ) - throw new NoSuchElementException("stream exhausted"); - - this.exhausted = true; - return new SingleObjectResponse(this.headers, this.inputStream); - } -} diff --git a/src/main/java/com/ossez/usreio/client/GetObjectResponseIterator.java b/src/main/java/com/ossez/usreio/client/GetObjectResponseIterator.java deleted file mode 100644 index 4c6fde1..0000000 --- a/src/main/java/com/ossez/usreio/client/GetObjectResponseIterator.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.PushbackInputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; - -import org.apache.commons.lang3.StringUtils; - -public class GetObjectResponseIterator implements GetObjectIterator { - public static final char CR = '\r'; - public static final char LF = '\n'; - public static final String EOL = CR+""+LF; - public static final String BS = "--"; - - private final PushbackInputStream multipartStream; - private final String boundary; - private Boolean hasNext; - - public static GetObjectIterator createIterator(final GetObjectResponse response, int streamBufferSize) throws Exception { - String boundary = response.getBoundary(); - if (boundary != null) - return new GetObjectResponseIterator(response, boundary, streamBufferSize); - - return new GetObjectIterator() { - - public void close() throws IOException{ - response.getInputStream().close(); - } - - public boolean hasNext() { - return false; - } - - public T next() { - throw new NoSuchElementException(); - } - - public void remove() { - throw new UnsupportedOperationException(""); - } - }; - } - - private GetObjectResponseIterator(GetObjectResponse response, String boundary, int streamBufferSize) throws Exception { - this.boundary = boundary; - - BufferedInputStream input = new BufferedInputStream(response.getInputStream(), streamBufferSize); - this.multipartStream = new PushbackInputStream(input, BS.length() + this.boundary.length() + EOL.length()); - } - - - public boolean hasNext() { - if (this.hasNext != null) - return this.hasNext.booleanValue(); - - try { - this.hasNext = new Boolean(this.getHaveNext()); - return this.hasNext.booleanValue(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public T next() { - if (!this.hasNext()) - throw new NoSuchElementException(); - - this.hasNext = null; - try { - return getNext(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - - public void remove() { - throw new UnsupportedOperationException(); - } - - - public void close() throws IOException { - this.multipartStream.close(); - } - - private boolean getHaveNext() throws IOException { - String line = null; - while ((line = this.readLine()) != null) { - if (line.equals(BS+this.boundary)) - return true; - if (line.equals(BS+this.boundary+BS)) - return false; - } - return false; - } - - private T getNext() throws Exception { - Map headers = new HashMap(); - String header = null; - while (StringUtils.isNotEmpty(header = this.readLine())) { - int nvSeperatorIndex = header.indexOf(':'); - if (nvSeperatorIndex == -1){ - headers.put(header, ""); - } else { - String name = header.substring(0, nvSeperatorIndex); - String value = header.substring(nvSeperatorIndex + 1).trim(); - headers.put(name, value); - } - } - return (T)new SingleObjectResponse(headers, new SinglePartInputStream(this.multipartStream, BS+this.boundary)); - } - - // TODO find existing library to do this - private String readLine() throws IOException { - boolean eolReached = false; - StringBuffer line = new StringBuffer(); - int currentChar = -1; - while (!eolReached && (currentChar = this.multipartStream.read()) != -1) { - eolReached = (currentChar == CR || currentChar == LF); - if (!eolReached) - line.append((char) currentChar); - } - - if (currentChar == -1 && line.length() == 0) - return null; - - if (currentChar == CR) { - int nextChar = this.multipartStream.read(); - if (nextChar != LF) - this.multipartStream.unread(new byte[] { (byte) nextChar }); - } - - return line.toString(); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/InvalidArgumentException.java b/src/main/java/com/ossez/usreio/client/InvalidArgumentException.java deleted file mode 100644 index 9c9d510..0000000 --- a/src/main/java/com/ossez/usreio/client/InvalidArgumentException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ossez.usreio.client; - -public class InvalidArgumentException extends RetsException { - public InvalidArgumentException(String message) { - super(message); - } -} diff --git a/src/main/java/com/ossez/usreio/client/InvalidHttpStatusException.java b/src/main/java/com/ossez/usreio/client/InvalidHttpStatusException.java deleted file mode 100644 index e318c72..0000000 --- a/src/main/java/com/ossez/usreio/client/InvalidHttpStatusException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.http.StatusLine; - -public class InvalidHttpStatusException extends RetsException { - public InvalidHttpStatusException(StatusLine status) { - super("Status code (" + status.getStatusCode() + ") " + status.getReasonPhrase()); - } - public InvalidHttpStatusException(StatusLine status, String message) { - super("Status code (" + status.getStatusCode() + ") " + status.getReasonPhrase() +" '"+message+"'"); - } -} diff --git a/src/main/java/com/ossez/usreio/client/InvalidReplyCodeException.java b/src/main/java/com/ossez/usreio/client/InvalidReplyCodeException.java deleted file mode 100644 index 39bfd77..0000000 --- a/src/main/java/com/ossez/usreio/client/InvalidReplyCodeException.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.lang3.SystemUtils; -/** - * Exception class for invalid reply codes from a Rets server - */ -public class InvalidReplyCodeException extends RetsException { - private final ReplyCode mReplyCode; - private String mMsg; - private String mReqinfo; - - public InvalidReplyCodeException(int replyCodeValue) { - this.mReplyCode = ReplyCode.fromValue(replyCodeValue); - } - - public InvalidReplyCodeException(ReplyCode replyCode) { - this.mReplyCode = replyCode; - } - - @Override - public String getMessage() { - StringBuffer sb = new StringBuffer(this.mReplyCode.toString()); - if (this.mMsg != null) { - sb.append(SystemUtils.LINE_SEPARATOR + this.mMsg); - } - if (this.mReqinfo != null) { - sb.append(SystemUtils.LINE_SEPARATOR + this.mReqinfo); - } - return sb.toString(); - } - - public int getReplyCodeValue() { - return this.mReplyCode.getValue(); - } - - public void setRemoteMessage(String msg) { - this.mMsg = msg; - } - - public void setRequestInfo(String reqinfo) { - this.mReqinfo = reqinfo; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/InvalidReplyCodeHandler.java b/src/main/java/com/ossez/usreio/client/InvalidReplyCodeHandler.java deleted file mode 100644 index 4af39c3..0000000 --- a/src/main/java/com/ossez/usreio/client/InvalidReplyCodeHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ossez.usreio.client; - -public interface InvalidReplyCodeHandler { - InvalidReplyCodeHandler FAIL = new InvalidReplyCodeHandler() { - public void invalidRetsReplyCode(int replyCode) throws InvalidReplyCodeException { - throw new InvalidReplyCodeException(replyCode); - } - - public void invalidRetsStatusReplyCode(int replyCode) throws InvalidReplyCodeException { - throw new InvalidReplyCodeException(replyCode); - } - }; - - public void invalidRetsReplyCode(int replyCode) throws InvalidReplyCodeException; - - public void invalidRetsStatusReplyCode(int replyCode) throws InvalidReplyCodeException; -} diff --git a/src/main/java/com/ossez/usreio/client/KeyValueResponse.java b/src/main/java/com/ossez/usreio/client/KeyValueResponse.java deleted file mode 100644 index 2ea6bd2..0000000 --- a/src/main/java/com/ossez/usreio/client/KeyValueResponse.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.commons.logging.Log; -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -import static com.ossez.usreio.client.CapabilityUrls.LOGIN_URL; - -/** - * Process key and Value - * - * @author YuCheng Hu - */ -abstract public class KeyValueResponse { - protected static final String CRLF = "\r\n"; - private static final Logger logger = LoggerFactory.getLogger(KeyValueResponse.class); - - protected Document mDoc; - protected int mReplyCode; - protected boolean mStrict; - - public KeyValueResponse() { - this.mStrict = false; - } - - public void parse(InputStream stream, RetsVersion mVersion) throws RetsException { - try { - SAXReader builder = new SAXReader(); - this.mDoc = builder.read(stream); - Element retsElement = this.mDoc.getRootElement(); - if (!retsElement.getName().equals("RETS")) { - throw new RetsException("Expecting RETS"); - } - - int replyCode = NumberUtils.toInt(retsElement.attributeValue("ReplyCode")); - this.mReplyCode = replyCode; - if (!isValidReplyCode(replyCode)) { - throw new InvalidReplyCodeException(replyCode); - } - Element capabilityContainer; - if (RetsVersion.RETS_10.equals(mVersion)) { - capabilityContainer = retsElement; - } else { - List children = retsElement.elements(); - if (children.size() != 1) { - throw new RetsException("Invalid number of children: " + children.size()); - } - - capabilityContainer = (Element) children.get(0); - - if (!capabilityContainer.getName().equals("RETS-RESPONSE")) { - throw new RetsException("Expecting RETS-RESPONSE"); - } - } - this.handleRetsResponse(capabilityContainer); - } catch (DocumentException e) { - throw new RetsException(e); - } - } - - protected boolean isValidReplyCode(int replyCode) { - return (ReplyCode.SUCCESS.equals(replyCode)); - - } - - /** - * handleRetsResponse - * - * @param retsResponse - * @throws RetsException - */ - private void handleRetsResponse(Element retsResponse) throws RetsException { - List tokenizeList = Arrays.asList(StringUtils.split(retsResponse.getText(), CRLF)); - HashMap retsResponseMap = new HashMap(); - - for (String keyValueStr : tokenizeList) { - - String[] splits = StringUtils.split(keyValueStr, "="); - if (!ArrayUtils.isEmpty(splits) && splits.length > 1) { - String key = StringUtils.trimToNull(splits[0]); - String value = StringUtils.trimToEmpty(splits[1]); - - // PROCESS LOGIN_URL - if (StringUtils.equalsIgnoreCase(LOGIN_URL, key)) { - retsResponseMap.put(LOGIN_URL, value); - this.handleKeyValue(LOGIN_URL, value); - } else - retsResponseMap.put(key, value); - } - - } - - retsResponseMap.entrySet().parallelStream().forEach(entry -> { - try { - this.handleKeyValue(entry.getKey(), entry.getValue()); - } catch (RetsException ex) { - logger.warn("Unable process rests login response value", ex); - } - }); - - - } - - protected abstract void handleKeyValue(String key, String value) throws RetsException; - - public void setStrict(boolean strict) { - this.mStrict = strict; - } - - public boolean isStrict() { - return this.mStrict; - } - - protected boolean matchKey(String key, String value) { - if (this.mStrict) - return key.equals(value); - - return key.equalsIgnoreCase(value); - } - - protected void assertStrictWarning(Log log, String message) throws RetsException { - if (this.mStrict) - throw new RetsException(message); - - log.warn(message); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/LoginRequest.java b/src/main/java/com/ossez/usreio/client/LoginRequest.java deleted file mode 100644 index 1576132..0000000 --- a/src/main/java/com/ossez/usreio/client/LoginRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ossez.usreio.client; - -public class LoginRequest extends VersionInsensitiveRequest { - - @Override - public void setUrl(CapabilityUrls urls) { - setUrl(urls.getLoginUrl()); - } - - public void setBrokerCode(String code, String branch) { - if (code == null) { - setQueryParameter(KEY_BROKERCODE, null); - } else { - if (branch == null) { - setQueryParameter(KEY_BROKERCODE, code); - } else { - setQueryParameter(KEY_BROKERCODE, code + "," + branch); - } - } - } - - public static final String KEY_BROKERCODE = "BrokerCode"; -} diff --git a/src/main/java/com/ossez/usreio/client/LoginResponse.java b/src/main/java/com/ossez/usreio/client/LoginResponse.java deleted file mode 100644 index 4002463..0000000 --- a/src/main/java/com/ossez/usreio/client/LoginResponse.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashSet; -import java.util.Set; - -/** - * @author YuCheng Hu - */ -public class LoginResponse extends KeyValueResponse { - private static final String BROKER_KEY = "Broker"; - private static final String MEMBER_NAME_KEY = "MemberName"; - private static final String METADATA_VER_KEY = "MetadataVersion"; - private static final String MIN_METADATA_VER_KEY = "MinMetadataVersion"; - private static final String USER_INFO_KEY = "User"; - private static final String OFFICE_LIST_KEY = "OfficeList"; - private static final String BALANCE_KEY = "Balance"; - private static final String TIMEOUT_KEY = "TimeoutSeconds"; - private static final String PWD_EXPIRE_KEY = "Expr"; - private static final String METADATA_TIMESTAMP_KEY = "MetadataTimestamp"; - private static final String MIN_METADATA_TIMESTAMP_KEY = "MinMetadataTimestamp"; - private static final Log LOG = LogFactory.getLog(LoginResponse.class); - - private String sessionId; - private String memberName; - private String userInformation; - private String broker; - private String metadataVersion; - private String minMetadataVersion; - private String metadataTimestamp; - private String minMetadataTimestamp; - private String officeList; - private String balance; - private int sessionTimeout; - private String passwordExpiration; - private CapabilityUrls capabilityUrls; - private Set brokerCodes; - - public LoginResponse(String loginUrl) { - super(); - this.brokerCodes = new HashSet(); - URL url = null; - try { - url = new URL(loginUrl); - } catch (MalformedURLException e) { - LOG.warn("Bad URL: " + loginUrl); - } - this.capabilityUrls = new CapabilityUrls(url); - } - - public LoginResponse() { - super(); - this.capabilityUrls = new CapabilityUrls(); - } - - @Override - public void parse(InputStream stream, RetsVersion version) throws RetsException { - super.parse(stream, version); - if (ReplyCode.BROKER_CODE_REQUIRED.equals(this.mReplyCode)) { - throw new BrokerCodeRequredException(this.brokerCodes); - } - } - - @Override - protected boolean isValidReplyCode(int replyCode) { - return (super.isValidReplyCode(replyCode) || ReplyCode.BROKER_CODE_REQUIRED.equals(replyCode)); - } - - @Override - protected void handleKeyValue(String key, String value) throws RetsException { - if (ReplyCode.BROKER_CODE_REQUIRED.equals(this.mReplyCode)) { - if (matchKey(key, BROKER_KEY)) { - String[] strings = StringUtils.split(value, ","); - if (strings.length > 0 && strings.length < 3) { - this.brokerCodes.add(strings); - } else { - throw new RetsException("Invalid broker/branch code: " + value); - } - } - } - - if (matchKey(key, BROKER_KEY)) { - this.broker = value; - } else if (matchKey(key, MEMBER_NAME_KEY)) { - this.memberName = value; - } else if (matchKey(key, METADATA_VER_KEY)) { - this.metadataVersion = value; - } else if (matchKey(key, MIN_METADATA_VER_KEY)) { - this.minMetadataVersion = value; - } else if (matchKey(key, METADATA_TIMESTAMP_KEY)) { - this.metadataTimestamp = value; - } else if (matchKey(key, MIN_METADATA_TIMESTAMP_KEY)) { - this.minMetadataTimestamp = value; - } else if (matchKey(key, USER_INFO_KEY)) { - this.userInformation = value; - } else if (matchKey(key, OFFICE_LIST_KEY)) { - this.officeList = value; - } else if (matchKey(key, BALANCE_KEY)) { - this.balance = value; - } else if (matchKey(key, TIMEOUT_KEY)) { - this.sessionTimeout = NumberUtils.toInt(value); - } else if (matchKey(key, PWD_EXPIRE_KEY)) { - this.passwordExpiration = value; - } else if (matchKey(key, CapabilityUrls.ACTION_URL)) { - this.capabilityUrls.setActionUrl(value); - } else if (matchKey(key, CapabilityUrls.CHANGE_PASSWORD_URL)) { - this.capabilityUrls.setChangePasswordUrl(value); - } else if (matchKey(key, CapabilityUrls.GET_OBJECT_URL)) { - this.capabilityUrls.setGetObjectUrl(value); - } else if (matchKey(key, CapabilityUrls.LOGIN_URL)) { - this.capabilityUrls.setLoginUrl(value); - } else if (matchKey(key, CapabilityUrls.LOGIN_COMPLETE_URL)) { - this.capabilityUrls.setLoginCompleteUrl(value); - } else if (matchKey(key, CapabilityUrls.LOGOUT_URL)) { - this.capabilityUrls.setLogoutUrl(value); - } else if (matchKey(key, CapabilityUrls.SEARCH_URL)) { - this.capabilityUrls.setSearchUrl(value); - } else if (matchKey(key, CapabilityUrls.GET_METADATA_URL)) { - this.capabilityUrls.setGetMetadataUrl(value); - } else if (matchKey(key, CapabilityUrls.UPDATE_URL)) { - this.capabilityUrls.setUpdateUrl(value); - } else if (matchKey(key, CapabilityUrls.SERVER_INFO_URL)) { - this.capabilityUrls.setServerInfo(value); - LOG.warn("Depreciated: " + key + " -> " + value); - } else if (matchKey(key, "Get")) { - LOG.warn("Found bad key: Get -> " + value); - // FIX ME: Should not get this - } else { - if (key.substring(0, 2).equalsIgnoreCase("X-")) { - LOG.warn("Unknown experimental key: " + key + " -> " + value); - } else { - assertStrictWarning(LOG, "Invalid login response key: " + key + " -> " + value); - } - } - } - - public String getMemberName() { - return this.memberName; - } - - public String getUserInformation() { - return this.userInformation; - } - - public String getBroker() { - return this.broker; - } - - public String getMetadataVersion() { - return this.metadataVersion; - } - - public String getMinMetadataVersion() { - return this.minMetadataVersion; - } - - public String getMetadataTimestamp() { - return this.metadataTimestamp; - } - - public String getMinMetadataTimestamp() { - return this.minMetadataTimestamp; - } - - public String getOfficeList() { - return this.officeList; - } - - public String getBalance() { - return this.balance; - } - - public int getSessionTimeout() { - return this.sessionTimeout; - } - - public String getPasswordExpiration() { - return this.passwordExpiration; - } - - public CapabilityUrls getCapabilityUrls() { - return this.capabilityUrls; - } - - public String getSessionId() { - return this.sessionId; - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/LogoutRequest.java b/src/main/java/com/ossez/usreio/client/LogoutRequest.java deleted file mode 100644 index 30a1e43..0000000 --- a/src/main/java/com/ossez/usreio/client/LogoutRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ossez.usreio.client; - -public class LogoutRequest extends VersionInsensitiveRequest { - - @Override - public void setUrl(CapabilityUrls urls) { - setUrl(urls.getLogoutUrl()); - } -} diff --git a/src/main/java/com/ossez/usreio/client/LogoutResponse.java b/src/main/java/com/ossez/usreio/client/LogoutResponse.java deleted file mode 100644 index 02cb16b..0000000 --- a/src/main/java/com/ossez/usreio/client/LogoutResponse.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -public class LogoutResponse extends KeyValueResponse { - private static final Log LOG = LogFactory.getLog(LogoutResponse.class); - private static final String CONNECT_TIME_KEY = "ConnectTime"; - private static final String BILLING_KEY = "Billing"; - private static final String SIGNOFF_KEY = "SignOffMessage"; - - private String seconds; - private String billingInfo; - private String logoutMessage; - - @Override - protected void handleKeyValue(String key, String value) throws RetsException { - if (matchKey(key, CONNECT_TIME_KEY)) { - this.seconds = value; - } else if (matchKey(key, BILLING_KEY)) { - this.billingInfo = value; - } else if (matchKey(key, SIGNOFF_KEY)) { - this.logoutMessage = value; - } else { - assertStrictWarning(LOG, "Invalid logout response key: " + key + " -> " + value); - } - } - - public String getSeconds() { - return this.seconds; - } - - public String getBillingInfo() { - return this.billingInfo; - } - - public String getLogoutMessage() { - return this.logoutMessage; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/VersionInsensitiveRequest.java b/src/main/java/com/ossez/usreio/client/Main.java similarity index 70% rename from src/main/java/com/ossez/usreio/client/VersionInsensitiveRequest.java rename to src/main/java/com/ossez/usreio/client/Main.java index 3b72ee6..63764c5 100644 --- a/src/main/java/com/ossez/usreio/client/VersionInsensitiveRequest.java +++ b/src/main/java/com/ossez/usreio/client/Main.java @@ -1,10 +1,10 @@ package com.ossez.usreio.client; -public abstract class VersionInsensitiveRequest extends RetsHttpRequest { +public abstract class Main extends RetsHttpRequest { /** * Abstract class of subclasses where the Version of RETS is not needed (Password Request, Login Request, etc.) */ - public VersionInsensitiveRequest() { + public Main() { super(); } diff --git a/src/main/java/com/ossez/usreio/client/MetaCollectorAdapter.java b/src/main/java/com/ossez/usreio/client/MetaCollectorAdapter.java deleted file mode 100644 index 282a9bc..0000000 --- a/src/main/java/com/ossez/usreio/client/MetaCollectorAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.ossez.usreio.client; - -import com.ossez.usreio.tests.common.metadata.MetaCollector; -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -public abstract class MetaCollectorAdapter implements MetaCollector { - - - public MetaObject[] getMetadata(MetadataType type, String path) { - return getSome(type, path, "0"); - } - - - public MetaObject[] getMetadataRecursive(MetadataType type, String path) { - return getSome(type, path, "*"); - } - - private MetaObject[] getSome(MetadataType type, String path, String sfx) { - boolean compact = Boolean.getBoolean("rets-client.metadata.compact"); - try { - GetMetadataRequest req; - if (path == null || path.equals("")) { - req = new GetMetadataRequest(type.name(), sfx); - } else { - String[] ppath = StringUtils.split(path, ":"); - String[] id = new String[ppath.length + 1]; - System.arraycopy(ppath, 0, id, 0, ppath.length); - id[ppath.length] = sfx; - req = new GetMetadataRequest(type.name(), id); - } - if (compact) { - req.setCompactFormat(); - } - GetMetadataResponse response; - - response = doRequest(req); - - return response.getMetadata(); - } catch (RetsException e) { - LOG.error("bad metadata request", e); - return null; - } - } - - /** - * Perform operation of turning a GetMetadataRequest into - * a GetMetadataResponse - * - * @param req Requested metadata - * @return parsed MetaObjects - * - * @throws RetsException if an error occurs - */ - protected abstract GetMetadataResponse doRequest(GetMetadataRequest req) throws RetsException; - - private static final Log LOG = LogFactory.getLog(MetaCollectorAdapter.class); -} diff --git a/src/main/java/com/ossez/usreio/client/MetaCollectorImpl.java b/src/main/java/com/ossez/usreio/client/MetaCollectorImpl.java deleted file mode 100644 index 1c881d4..0000000 --- a/src/main/java/com/ossez/usreio/client/MetaCollectorImpl.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.client; - -public class MetaCollectorImpl extends MetaCollectorAdapter { - private final RetsTransport mTransport; - - public MetaCollectorImpl(RetsTransport transport) { - this.mTransport = transport; - } - - @Override - protected GetMetadataResponse doRequest(GetMetadataRequest req) throws RetsException { - return this.mTransport.getMetadata(req); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/NetworkEventMonitor.java b/src/main/java/com/ossez/usreio/client/NetworkEventMonitor.java deleted file mode 100644 index 04ffe4b..0000000 --- a/src/main/java/com/ossez/usreio/client/NetworkEventMonitor.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ossez.usreio.client; - -/** - * A client can register a monitor for network events - */ -public interface NetworkEventMonitor -{ - /** - * inform the client app that an event has started. - * the client app can return an object, which will be passed - * to eventFinish(). - * - * @param message a message describing the event - * @return an object to be passed to eventFinish, or null - */ - public Object eventStart(String message); - /** - * Inform the client app that the previous event has completed - * - * @param o the object returned from eventStart - */ - public void eventFinish(Object o); -} diff --git a/src/main/java/com/ossez/usreio/client/NullNetworkEventMonitor.java b/src/main/java/com/ossez/usreio/client/NullNetworkEventMonitor.java deleted file mode 100644 index 82526e5..0000000 --- a/src/main/java/com/ossez/usreio/client/NullNetworkEventMonitor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ossez.usreio.client; - -public class NullNetworkEventMonitor implements NetworkEventMonitor { - - public Object eventStart(String message) { - return null; - } - - - public void eventFinish(Object o) { - //noop - } -} diff --git a/src/main/java/com/ossez/usreio/client/ReplyCode.java b/src/main/java/com/ossez/usreio/client/ReplyCode.java deleted file mode 100644 index 82ac9bb..0000000 --- a/src/main/java/com/ossez/usreio/client/ReplyCode.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.ossez.usreio.client; - -import java.util.Map; -import java.util.HashMap; - -public class ReplyCode { - // static initialization loop.... this declaration _MUST_ come before the members - private static final Map CODES = new HashMap(); - - public static final ReplyCode SUCCESS = new ReplyCode(0, "Success"); - public static final ReplyCode ZERO_BALANCE = new ReplyCode(20003, "Zero balance"); - public static final ReplyCode BROKER_CODE_REQUIRED = new ReplyCode(20012, "Broker code required"); - public static final ReplyCode BROKER_CODE_INVALID = new ReplyCode(20013, "Broker Code Invalid"); - public static final ReplyCode ADDTIONAL_LOGIN_NOT_PREMITTED = new ReplyCode(20022, "Additional login not permitted"); - public static final ReplyCode MISCELLANEOUS_LOGIN_ERROR = new ReplyCode(20036, "Miscellaneous server login error"); - public static final ReplyCode CLIENT_PASSWORD_INVALID = new ReplyCode(20037, "Client passsword invalid"); - public static final ReplyCode SERVER_TEMPORARILY_DISABLED = new ReplyCode(20050, "Server temporarily disabled"); - public static final ReplyCode UNKNOWN_QUERY_FIELD = new ReplyCode(20200, "Unknown Query Field"); - public static final ReplyCode NO_RECORDS_FOUND = new ReplyCode(20201, "No Records Found"); - public static final ReplyCode INVALID_SELECT = new ReplyCode(20202, "Invalid select"); - public static final ReplyCode MISCELLANOUS_SEARCH_ERROR = new ReplyCode(20203, "Miscellaneous search error"); - public static final ReplyCode INVALID_QUERY_SYNTAX = new ReplyCode(20206, "Invalid query syntax"); - public static final ReplyCode UNAUTHORIZED_QUERY = new ReplyCode(20207, "Unauthorized query"); - public static final ReplyCode MAXIMUM_RECORDS_EXCEEDED = new ReplyCode(20208, "Maximum records exceeded"); - public static final ReplyCode SEARCH_TIMED_OUT = new ReplyCode(20209, "Search timed out"); - public static final ReplyCode TOO_MANY_OUTSTANDING_QUERIES = new ReplyCode(20210, "Too many outstanding queries"); - public static final ReplyCode INVALID_RESOURCE_GETOBJECT = new ReplyCode(20400, "Invalid Resource"); - public static final ReplyCode INVALID_TYPE_GETOBJECT = new ReplyCode(20401, "Invalid Type"); - public static final ReplyCode INVALID_IDENTIFIER_GETOBJECT = new ReplyCode(20402, "Invalid Identifier"); - public static final ReplyCode NO_OBJECT_FOUND = new ReplyCode(20403, "No Object Found"); - public static final ReplyCode UNSUPPORTED_MIME_TYPE_GETOBJECT = new ReplyCode(20406, "Unsupported MIME Type"); - public static final ReplyCode UNAUTHORIZED_RETRIEVAL_GETOBJECT = new ReplyCode(20407, "Unauthorized Retrieval"); - public static final ReplyCode RESOURCE_UNAVAILABLE_GETOBJECT = new ReplyCode(20408, "Resource Unavailable"); - public static final ReplyCode OBJECT_UNAVAILABLE = new ReplyCode(20409, "Object Unavailable"); - public static final ReplyCode REQUEST_TOO_LARGE_GETOBJECT = new ReplyCode(20410, "Request Too Large"); - public static final ReplyCode TIMEOUT_GETOBJECT = new ReplyCode(20411, "Timeout"); - public static final ReplyCode TOO_MANY_OUTSTANDING_QUERIES_GETOBJECT = new ReplyCode(20412,"Too Many Outstanding Queries"); - public static final ReplyCode MISCELLANEOUS_ERROR_GETOBJECT = new ReplyCode(20413, "Miscellaneous Error"); - public static final ReplyCode INVALID_RESOURCE = new ReplyCode(20500, "Invalid resource"); - public static final ReplyCode INVALID_TYPE = new ReplyCode(20501, "Invalid type"); - public static final ReplyCode INVALID_IDENTIFIER = new ReplyCode(20502, "Invalid identifier"); - public static final ReplyCode NO_METADATA_FOUND = new ReplyCode(20503, "No metadata found"); - public static final ReplyCode UNSUPPORTED_MIME_TYPE = new ReplyCode(20506, "Unsupported MIME type"); - public static final ReplyCode UNAUTHORIZED_RETRIEVAL = new ReplyCode(20507, "Unauthorized retrieval"); - public static final ReplyCode RESOURCE_UNAVAILABLE = new ReplyCode(20508, "Resource unavailable"); - public static final ReplyCode METADATA_UNAVAILABLE = new ReplyCode(20509, "Metadata unavailable"); - public static final ReplyCode REQUEST_TOO_LARGE = new ReplyCode(20510, "Request too large"); - public static final ReplyCode TIMEOUT = new ReplyCode(20511, "Timeout"); - public static final ReplyCode TOO_MANY_OUSTANDING_REQUESTS = new ReplyCode(20512, "Too many outstanding requests"); - public static final ReplyCode MISCELLANEOUS_ERROR = new ReplyCode(20513, "Miscellanous error"); - public static final ReplyCode REQUESTED_DTD_UNAVAILABLE = new ReplyCode(20514, "Requested DTD unvailable"); - - private final int mValue; - private final String mMessage; - - private ReplyCode(int value, String message) { - this.mValue = value; - this.mMessage = message; - if (CODES.containsValue(new Integer(value))) - throw new IllegalArgumentException(String.format("value already used: %s ( %s ) ",value,message)); - CODES.put(new Integer(value), this); - } - - - @Override - public boolean equals(Object o) { - if (!(o instanceof ReplyCode)) { - return false; - } - - ReplyCode rhs = (ReplyCode) o; - return (this.mValue == rhs.mValue); - } - - public boolean equals(int value) { - return this.mValue == value; - } - - @Override - public String toString() { - return String.format("%s (%s)",this.mValue,this.mMessage); - } - - public int getValue() { - return this.mValue; - } - - public String getMessage() { - return this.mMessage; - } - - public static ReplyCode fromValue(int value) { - ReplyCode replyCode = CODES.get(new Integer(value)); - if (replyCode != null) - return replyCode; - - return new ReplyCode(value, "Unknown"); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/ReplyCodeHandler.java b/src/main/java/com/ossez/usreio/client/ReplyCodeHandler.java deleted file mode 100644 index d99c1b9..0000000 --- a/src/main/java/com/ossez/usreio/client/ReplyCodeHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ossez.usreio.client; - -/** - * @author jrayburn - */ -public interface ReplyCodeHandler { - - /** - * ReplyCodeHandler can choose to handle reply codes - * that are non-zero reply codes in its own fashion. - * - * This is intended to be used to allow the SearchResultCollector - * to choose to throw InvalidReplyCodeException if the response is - * 20201 (Empty) or 20208 (MaxRowsExceeded). - * - * @param replyCode The RETS reply code - * - * @throws InvalidReplyCodeException Thrown if reply code is - * invalid for the SearchResultCollector. - */ - public void handleReplyCode(int replyCode) throws InvalidReplyCodeException; - -} diff --git a/src/main/java/com/ossez/usreio/client/RetsException.java b/src/main/java/com/ossez/usreio/client/RetsException.java deleted file mode 100644 index d115159..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsException.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.ossez.usreio.client; - -/** - * @author YuCheng Hu - */ -public class RetsException extends Exception { - public RetsException() { - super(); - } - - public RetsException(String message) { - super(message); - } - - public RetsException(String message, Throwable cause) { - super(message, cause); - } - - public RetsException(Throwable cause) { - super(cause); - } -} diff --git a/src/main/java/com/ossez/usreio/client/RetsHttpClient.java b/src/main/java/com/ossez/usreio/client/RetsHttpClient.java deleted file mode 100644 index 0c77aae..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsHttpClient.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ossez.usreio.client; - - -public abstract class RetsHttpClient { - - public static final String SESSION_ID_COOKIE = "RETS-Session-ID"; - public static final String LOGIN_SESSION_ID = "0"; - - public abstract void setUserCredentials(String userName, String password); - - /** - * The protocol specific implementation happens here. - * - * @param httpMethod - * @param request - * @return - * @throws RetsException - */ - public abstract RetsHttpResponse doRequest(String httpMethod, RetsHttpRequest request) throws RetsException; - - /** - * - * - * @param name header name, case should be preserved - * @param value static header value, if null then implementation should not include the header in requests - */ - - /** - * Add an HTTP header that should be included by default in all requests - * - * @param name header name, case should be preserved - * @param value static header value, if null then implementation should not include the header in requests - */ - public abstract void addDefaultHeader(String name, String value); - -} diff --git a/src/main/java/com/ossez/usreio/client/RetsHttpRequest.java b/src/main/java/com/ossez/usreio/client/RetsHttpRequest.java deleted file mode 100644 index f954583..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsHttpRequest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.Serializable; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import com.ossez.usreio.common.util.CaseInsensitiveTreeMap; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; - -/** Base Http Request object */ -public abstract class RetsHttpRequest implements Serializable { - private final Map mHeaders; - private final SortedMap mQueryParameters; - protected String mUrl; - - public RetsHttpRequest() { - this.mHeaders = new CaseInsensitiveTreeMap(); - this.mQueryParameters = new TreeMap(); - } - - public abstract void setUrl(CapabilityUrls urls); - - public void setUrl(String url) { - this.mUrl = url; - } - - public String getUrl() { - return this.mUrl; - } - - public void setHeader(String key, String value) { - this.mHeaders.put(key, value); - } - - public Map getHeaders() { - return this.mHeaders; - } - - public String getHttpParameters() { - if (this.mQueryParameters.isEmpty()) - return null; - - List params = new LinkedList(); - for(Map.Entry param : this.mQueryParameters.entrySet()){ - params.add(String.format("%s=%s",RetsUtil.urlEncode(param.getKey()),RetsUtil.urlEncode(param.getValue()))); - } - return StringUtils.join(params.iterator(),"&"); - } - - protected void setQueryParameter(String name, String value) { - if (value == null) { - this.mQueryParameters.remove(name); - } else { - this.mQueryParameters.put(name, value); - } - } - - @Override - public String toString() { - ToStringBuilder builder = new ToStringBuilder(this); - Iterator iterator = this.mQueryParameters.keySet().iterator(); - while (iterator.hasNext()) { - String s = (String) iterator.next(); - builder.append(s, this.mQueryParameters.get(s)); - } - return builder.toString(); - } - - /** - * any request with version-specific handling should deal with this. - * - * @param version - */ - public abstract void setVersion(RetsVersion version); - -} diff --git a/src/main/java/com/ossez/usreio/client/RetsHttpResponse.java b/src/main/java/com/ossez/usreio/client/RetsHttpResponse.java deleted file mode 100644 index 315464e..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsHttpResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.InputStream; -import java.util.Map; - -/** - * Interface for retrieving useful header fields from a RETS HTTP response - * - * @author YuCheng Hu - */ -public interface RetsHttpResponse { - public int getResponseCode() throws RetsException; - - public Map getHeaders() throws RetsException; - - public String getHeader(String hdr) throws RetsException; - - public String getCookie(String cookie) throws RetsException; - - public String getCharset() throws RetsException; - - public InputStream getInputStream() throws RetsException; - - public Map getCookies() throws RetsException; - -} diff --git a/src/main/java/com/ossez/usreio/client/RetsSession.java b/src/main/java/com/ossez/usreio/client/RetsSession.java deleted file mode 100644 index 6e91e1e..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsSession.java +++ /dev/null @@ -1,403 +0,0 @@ -package com.ossez.usreio.client; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.Metadata; -import com.ossez.usreio.tests.common.metadata.MetadataException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * RetsSession is the core class of the rets.client package. - */ -public class RetsSession { - public static final String METADATA_TABLES = "metadata_tables.xml"; - public static final String RETS_CLIENT_VERSION = "1.5";//change default version - - private static final Log LOG = LogFactory.getLog(RetsSession.class); - private static String sUserAgent = "crt-rets-client/" + RETS_CLIENT_VERSION; - - private CapabilityUrls capabilityUrls; - private RetsHttpClient httpClient; - private RetsTransport transport; - private String sessionId; - - - /** - * Creates a new RetsSession instance. - * You must call login(user, pass) before attempting any other - * transactions. - * - * Uses a default implementation of RetsHttpClient based on - * apache commons http client. - * - * Uses the RetsVersion.RETS_DEFAULT as the RetsVersion for - * this session. - * - * Uses sAgent at the User-Agent setting for this RetsSession. - * - * @param loginUrl URL of the Login transaction. - */ - public RetsSession(String loginUrl) { - this(loginUrl, new CommonsHttpClient()); - } - - /** - * Creates a new RetsSession instance. - * You must call login(user, pass) before attempting any other - * transactions. - * - * Uses the RetsVersion.RETS_DEFAULT as the RetsVersion for - * this session. - * - * Uses sAgent at the User-Agent setting for this RetsSession. - * - * @param loginUrl URL of the Login transaction - * @param httpClient a RetsHttpClient implementation. The default - * is CommonsHttpClient. - */ - public RetsSession(String loginUrl, RetsHttpClient httpClient) { - this(loginUrl, httpClient, RetsVersion.DEFAULT); - } - - /** - * Creates a new RetsSession instance. - * You must call login(user, pass) before attempting any other - * transactions. - * - * Uses sAgent at the User-Agent setting for this RetsSession. - * - * @param loginUrl URL of the Login transaction - * @param httpClient a RetsHttpClient implementation. The default - * is CommonsHttpClient. - * @param retsVersion The RetsVersion used by this RetsSession. - */ - public RetsSession(String loginUrl, RetsHttpClient httpClient, RetsVersion retsVersion) { - this(loginUrl, httpClient, retsVersion, sUserAgent,false); - } - - /** - * Creates a new RetsSession instance. - * You must call login(user, pass) before attempting any other - * transactions. - * - * @param loginUrl URL of the Login transaction - * @param httpClient a RetsHttpClient implementation. The default - * is CommonsHttpClient. - * @param retsVersion The RetsVersion used by this RetsSession. - * @param userAgent specific User-Agent to use for this session. - */ - public RetsSession(String loginUrl, RetsHttpClient httpClient, RetsVersion retsVersion, String userAgent, boolean strict) { - this.capabilityUrls = new CapabilityUrls(); - this.capabilityUrls.setLoginUrl(loginUrl); - - this.httpClient = httpClient; - this.transport = new RetsTransport(httpClient, this.capabilityUrls, retsVersion, strict); - this.httpClient.addDefaultHeader("User-Agent", userAgent); - } - - /** - * Query the current RetsVersion being used in this session. - * - * Initially, this will be the value passed to the RetsTransport. - * However, if during auto-negotiation the RetsTransport changes - * the RetsSession, this value may change throughout the session. - * - * @return the current RetsVersion value being used by the - * RetsTransport. - */ - public RetsVersion getRetsVersion() { - return this.transport.getRetsVersion(); - } - - /** - * Get the current RETS Session ID - * - * @return the current RETS Session ID or null is the server has - * not specified one - */ - public String getSessionId() { - return this.sessionId; - } - - public void setSessionId(String sessionId) { - LOG.debug("setting Session-ID to: " + sessionId); - this.sessionId = sessionId; - } - - public void setMonitor(NetworkEventMonitor monitor) { - this.transport.setMonitor(monitor); - } - - public void setStrict(boolean strict) { - this.transport.setStrict(strict); - } - public boolean isStrict() { - return this.transport.isStrict(); - } - - /** - * Sets the default User-Agent value for RetsSessions created without - * a specified User-Agent value. - * - * @param userAgent Default User-Agent value to use for all RetsSession - * objects created in the future. - */ - public static void setUserAgent(String userAgent) { - sUserAgent = userAgent; - } - - public String getLoginUrl() { - return this.capabilityUrls.getLoginUrl(); - } - - public Metadata getIncrementalMetadata() throws RetsException { - try { - return new Metadata(new MetaCollectorImpl(this.transport)); - } catch (MetadataException e) { - throw new RetsException(e); - } - } - - /** - * Get the complete RETS metadata. - * - * @return The RETS metadata object for these credentials. - * - * @throws RetsException - */ - public Metadata getMetadata() throws RetsException { - return this.transport.getMetadata("null"); - } - /** - * Ability to download the raw metadata to a location - * @param location - * @return - * @throws RetsException - */ - public Metadata getMetadata(String location) throws RetsException { - return this.transport.getMetadata(location); - } - - /** - * Perform a low level GetMetadatRequest. To retrieve - * structured metadata, - * - * @see #getMetadata() - * - * @param req GetMetadataRequest - * @return GetMetadataResponse, containing all MetaObjects - * returned - * - * @throws RetsException if an error occurs - */ - public GetMetadataResponse getMetadata(GetMetadataRequest req) throws RetsException { - return this.transport.getMetadata(req); - } - - /** - * Fetches the action (MOTD) from the server. - * - * @exception RetsException if an error occurs - */ - private void getAction() throws RetsException { - String actionUrl = this.capabilityUrls.getActionUrl(); - if (actionUrl == null) { - LOG.warn("No Action-URL available, skipping"); - return; - } - GenericHttpRequest actionRequest = new GenericHttpRequest(actionUrl){ - @Override - public Map getHeaders() { - return null; - } - }; - RetsHttpResponse httpResponse = this.httpClient.doRequest("GET", actionRequest); - try { - httpResponse.getInputStream().close(); - } catch (Exception e) { - LOG.error("Action URL weirdness", e); - } - } - - /** - * Implementation that allow for single or multi-part - * GetObject requests. - * - * @param req - * @return - * @exception RetsException if an error occurs - */ - public GetObjectResponse getObject(GetObjectRequest req) throws RetsException { - return this.transport.getObject(req); - } - - /** - * - * @param resource - * @param type - * @param entity - * @param id - * @return response - * @exception RetsException if an error occurs - */ - public GetObjectResponse getObject(String resource, String type, String entity, String id) throws RetsException { - GetObjectRequest req = new GetObjectRequest(resource, type); - req.addObject(entity, id); - return getObject(req); - } - - /** - * Log into the RETS server (see RETS 1.5, section 4). No other - * transactions will work until you have logged in. - * - * @param userName Username to authenticate - * @param password Password to authenticate with - * @return LoginResponse if success. - * @exception RetsException if authentication was denied - */ - public LoginResponse login(String userName, String password) throws RetsException { - return login(userName, password, null, null); - } - - /** - * Log into the RETS server (see RETS 1.5, section 4). No other - * transactions will work until you have logged in. - * - * @param userName username to authenticate - * @param password password to authenticate with - * @param brokerCode broker code if the same user belongs to multiple - * brokerages. May be null. - * @param brokerBranch branch code if the same user belongs to multiple - * branches. May be null. brokerCode is required if you want - * brokerBranch to work. - * @return LoginResponse if success. - * @exception RetsException if authentication was denied - */ - - public LoginResponse login(String userName, String password, String brokerCode, String brokerBranch) throws RetsException { - this.httpClient.setUserCredentials(userName, password); - - LoginRequest request = new LoginRequest(); - request.setBrokerCode(brokerCode, brokerBranch); - - LoginResponse response = this.transport.login(request); - this.capabilityUrls = response.getCapabilityUrls(); - this.transport.setCapabilities(this.capabilityUrls); - this.setSessionId(response.getSessionId()); - this.getAction(); - - return response; - } - - /** - * Log out of the current session. Another login _may_ re-establish a new connection - * depending the the behavior of the {#link RetsHttpClient} and its' ability to - * maintain and restablish a connection. - * - * @return a LogoutResponse - * @throws RetsException if the logout transaction failed - */ - public LogoutResponse logout() throws RetsException { - try { - return this.transport.logout(); - } finally { - this.setSessionId(null); - } - } - - /** - * Will perform a search as requested and return a filled - * SearchResult object. This method caches all result information - * in memory in the SearchResult object. - * - * @param req Contains parameters on which to search. - * @return a completed SearchResult - * @exception RetsException if an error occurs - */ - public SearchResult search(SearchRequest req) throws RetsException { - SearchResultImpl res = new SearchResultImpl(); - search(req, res); - return res; - } - - /** - * Execute a RETS Search. The collector object will be filled - * when this method is returned. See RETS 1.52d, Section 5. - * - * @param req Contains parameters on which to search. - * @param collector SearchResult object which will be informed of the results - * as they come in. If you don't need live results, see the other - * search invocation. - * @exception RetsException if an error occurs - */ - public void search(SearchRequest req, SearchResultCollector collector) throws RetsException { - this.transport.search(req, collector); - } - - /** - * Search and process the Search using a given SearchResultProcessor. - * - * @param req the search request - * @param processor the result object that will process the data - */ - public SearchResultSet search(SearchRequest req, SearchResultProcessor processor) throws RetsException { - return this.transport.search(req, processor); - } - - /** - * The lowest level integration. This method is not recommened for general use. - */ - public RetsHttpResponse request(RetsHttpRequest request) throws RetsException{ - return this.transport.doRequest(request); - } - - /** - * switch to a specific HttpMethodName, POST/GET, where the - * method is supported. Where GET is not supported, POST - * will be used. - * @param method the HttpMethodName to use - */ - public void setMethod(String method) { - this.transport.setMethod(method); - } - - /** Make sure GC'd sessions are logged out. */ - @Override - protected void finalize() throws Throwable { - try { - if( this.sessionId != null ) this.logout(); - } finally { - super.finalize(); - } - } - /** - * Performs a search returning only the number of records resulting from a query. - * - * Convenience method to get number records from a query - * - * @param req the search request - * @return the number of records that returned from the search request - * @throws RetsException - */ - public int getQueryCount(SearchRequest req) throws RetsException { - req.setCountOnly(); - SearchResult res = this.search(req); - return res.getCount(); - } - - /** - * Gives the URL's of an Object request instead of object themselves - * - * Convenience method to get the URL's of the requeseted object only - * - * @param req - * @return - * @throws RetsException - */ - public GetObjectResponse getObjectUrl(GetObjectRequest req) throws RetsException { - req.setLocationOnly(true); - GetObjectResponse res = this.getObject(req); - return res; - } -} diff --git a/src/main/java/com/ossez/usreio/client/RetsTransport.java b/src/main/java/com/ossez/usreio/client/RetsTransport.java deleted file mode 100644 index dbe9775..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsTransport.java +++ /dev/null @@ -1,334 +0,0 @@ -package com.ossez.usreio.client; - - -import java.io.FileWriter; -import java.util.HashMap; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.JDomCompactBuilder; -import com.ossez.usreio.tests.common.metadata.JDomStandardBuilder; -import com.ossez.usreio.tests.common.metadata.Metadata; -import com.ossez.usreio.tests.common.metadata.MetadataBuilder; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.dom4j.Document; -import org.dom4j.io.SAXReader; -import org.dom4j.io.XMLWriter; - - -/** - * Implements the basic transport mechanism. This class deals with the - * very basic parts of sending the request, returning a response object, - * and version negotiation. - * - */ -public class RetsTransport { - private static final String RETS_SESSION_ID_HEADER = "RETS-Session-ID"; // TODO spec says hyphen, Marketlinx uses an underscore - - private RetsHttpClient client; - private CapabilityUrls capabilities; - private String method = "GET"; - private RetsVersion version; - private boolean strict; - private NetworkEventMonitor monitor; - - private static final Log LOG = LogFactory.getLog(RetsTransport.class); - - private static Map MONITOR_MSGS = new HashMap(){{ - put(ChangePasswordRequest.class, "Transmitting change password request"); - put(GetObjectRequest.class, "Retrieving media object"); - put(LoginRequest.class, "Logging in"); - put(GetMetadataRequest.class, "Retrieving metadata"); - put(LogoutRequest.class, "Logging out"); - put(SearchRequest.class, "Executing search"); - }}; - - - /** - * Create a new transport instance. - * @param client An http client (make sure you call setUserCredentials - * on it before carrying out any transactions). - * @param capabilities the initial capabilities url list. This can be - * replaced with a more up to date version at any time (for example, - * post-login()) with setCapabilities() - * - * @see RetsHttpClient#setUserCredentials - */ - public RetsTransport(RetsHttpClient client, CapabilityUrls capabilities) { - this(client, capabilities, RetsVersion.DEFAULT, false); - } - - /** - * Create a new transport instance to speak a specific RETS version. - * @param client an http client - * @param capabilities the initial capabilities url list - * @param version the RETS version to use during initial negotiation - * (RetsTransport will automatically switch to whatever version the - * server supports). - */ - public RetsTransport(RetsHttpClient client, CapabilityUrls capabilities, RetsVersion version, boolean strict) { - this.client = client; - this.capabilities = capabilities; - this.doVersionHeader(version); - this.strict = strict; - this.client.addDefaultHeader("Accept", "*/*"); - this.monitor = new NullNetworkEventMonitor(); - } - - /** - * Query the current RetsVersion being used in this RetsTransport. - * - * Initially, this will be the value with which this object was - * constructed. - * - * However, this value may change after login. - * - * @return the current RetsVersion value being used by the - * RetsTransport. - */ - public RetsVersion getRetsVersion() { - return this.version; - } - - public boolean isStrict() { - return this.strict; - } - - public void setStrict(boolean strict) { - this.strict = strict; - } - - public void setMonitor(NetworkEventMonitor monitor) { - if (monitor == null) { - monitor = new NullNetworkEventMonitor(); - } - this.monitor = monitor; - } - - /** - * Set our RetsHttpClient up with the correct default RETS version to use, - * default to RETS 1.5. - * @param retsVersion - */ - private void doVersionHeader(RetsVersion retsVersion) { - if (this.client == null) - return; - if (retsVersion == null) - retsVersion = RetsVersion.DEFAULT; - this.version = retsVersion; - this.client.addDefaultHeader(RetsVersion.RETS_VERSION_HEADER, this.version.toString()); - } - - /** - * replace the capabilities url list with a new one - * @param capabilities the new capabilities url list - */ - public void setCapabilities(CapabilityUrls capabilities) { - this.capabilities = capabilities; - } - - /** - * switch to a specific HttpMethodName, POST/GET, where the - * method is supported. Where GET is not supported, POST - * will be used. - * @param method the HttpMethodName to use - */ - public void setMethod(String method) { - this.method = method; - } - - /** - * Available as an integration last resort - */ - public RetsHttpResponse doRequest(RetsHttpRequest req) throws RetsException { - Object monitorobj = null; - String msg = getMonitorMessage(req); - monitorobj = this.monitor.eventStart(msg); - - req.setVersion(this.version); - req.setUrl(this.capabilities); - - RetsHttpResponse httpResponse; - try { - httpResponse = this.client.doRequest(this.method, req); - } finally { - this.monitor.eventFinish(monitorobj); - } - return httpResponse; - } - - private String getMonitorMessage(RetsHttpRequest req) { - String msg = (String) MONITOR_MSGS.get(req.getClass()); - if (msg == null) { - msg = "communicating with network"; - } - return msg; - } - - /** - * Logs into the server. This transaction gets a list of capability URLs - * encapsulated in the LoginResponse that should typically be given back - * to the transport object with setCapabilities(). RETS Specification, - * section 4. - * - * @param req The login request - * @return the LoginResponse object - * @throws RetsException if the login failed or something went wrong on the - * network - * @see #setCapabilities - */ - public LoginResponse login(LoginRequest req) throws RetsException { - RetsHttpResponse retsHttpResponse = this.doRequest(req); - - String versionHeader = retsHttpResponse.getHeader(RetsVersion.RETS_VERSION_HEADER); - // may be null, which is fine, return null, dont throw - RetsVersion retsVersion = RetsVersion.getVersion(versionHeader); - if( retsVersion == null && this.strict ) - throw new RetsException(String.format("RETS Version is a required response header, version '%s' is unrecognized",versionHeader)); - // skip updating the client version if its not set (correctly) by the server - if( retsVersion != null ) this.doVersionHeader(retsVersion); - - LoginResponse response = new LoginResponse(this.capabilities.getLoginUrl()); - - String sessionId = retsHttpResponse.getCookie(RETS_SESSION_ID_HEADER); - response.setSessionId(sessionId); - response.setStrict(this.strict); - response.parse(retsHttpResponse.getInputStream(), this.version); - return response; - } - - /** - * Logs out of the server. No other transactions should be called until - * another login() succeeds. RETS Specification, Section 6. Logout is - * an optional transaction. This method returns null if the server does - * not support the Logout transaction. - * - * @return LogoutResponse or null if logout is not supported - * @throws RetsException if there is a network or remote server error - */ - public LogoutResponse logout() throws RetsException { - if (this.capabilities.getLogoutUrl() == null) { - return null; - } - RetsHttpRequest req = new LogoutRequest(); - RetsHttpResponse httpResponse = doRequest(req); - LogoutResponse response = new LogoutResponse(); - response.setStrict(this.strict); - try { - response.parse(httpResponse.getInputStream(), this.version); - } catch(RetsException e) { - if (e.getMessage().contains("Invalid number of children")){// most RETS servers have issues logging out for some reason. - LOG.warn("unsual response for logout request, but log out successful."); - } - - } - return response; - } - - /** - * Perform a non-streaming search and pass all results from the - * SearchRequest to the given collector. - * - * 12/06/20 Added charset, needed for sax parser - * @param req the search request - * @param collector the result object that will store the data - */ - public void search(SearchRequest req, SearchResultCollector collector) throws RetsException { - RetsHttpResponse httpResponse = doRequest(req); - new SearchResultHandler(collector).parse(httpResponse.getInputStream(), httpResponse.getCharset()); - } - - /** - * Override processing of the search completely by providing a - * SearchResultProcessor to process the results of the Search. - * - * @param req the search request - * @param processor the result object that will process the data - */ - public SearchResultSet search(SearchRequest req, SearchResultProcessor processor) throws RetsException { - RetsHttpResponse httpResponse = doRequest(req); - return processor.parse(httpResponse.getInputStream()); - } - - /** - * - * @param req GetObject request - * @return a GetObjectResponse - * @throws RetsException if the request is not valid or a network error - * occurs - */ - public GetObjectResponse getObject(GetObjectRequest req) throws RetsException { - if (this.capabilities.getGetObjectUrl() == null) { - throw new RetsException("Server does not support GetObject transaction."); - } - req.setUrl(this.capabilities); - RetsHttpResponse httpResponse = this.client.doRequest(this.method, req); - GetObjectResponse result = new GetObjectResponse(httpResponse.getHeaders(), httpResponse.getInputStream()); - return result; - } - - public Metadata getMetadata(String location) throws RetsException { - boolean compact = Boolean.getBoolean("rets-client.metadata.compact"); - GetMetadataRequest req = new GetMetadataRequest("SYSTEM", "*"); - if (compact) { - req.setCompactFormat(); - } - try { - RetsHttpResponse httpResponse = doRequest(req); - Object monitorobj = null; - monitorobj = this.monitor.eventStart("Parsing metadata"); - try { - SAXReader xmlBuilder = new SAXReader(); - Document xmlDocument = xmlBuilder.read(httpResponse.getInputStream()); - if (!location.equals("null")){ - - FileWriter writer = new FileWriter(location); - XMLWriter outputter = new XMLWriter(writer); - - outputter.write(xmlDocument); - outputter.close(); - - } - MetadataBuilder metadataBuilder; - if (req.isCompactFormat()) { - metadataBuilder = new JDomCompactBuilder(); - } else { - metadataBuilder = new JDomStandardBuilder(); - } - metadataBuilder.setStrict(this.strict); - - - - return metadataBuilder.doBuild(xmlDocument); - } finally { - this.monitor.eventFinish(monitorobj); - } - } catch (Exception e) { - throw new RetsException(e); - } - } - - public GetMetadataResponse getMetadata(GetMetadataRequest req) throws RetsException { - RetsHttpResponse httpResponse = doRequest(req); - Object monitorobj = null; - monitorobj = this.monitor.eventStart("Parsing metadata"); - try { - try { - return new GetMetadataResponse(httpResponse.getInputStream(), req.isCompactFormat(),this.strict); - } catch (InvalidReplyCodeException e) { - e.setRequestInfo(req.toString()); - throw e; - } - } finally { - this.monitor.eventFinish(monitorobj); - } - } - - public boolean changePassword(ChangePasswordRequest req) throws RetsException { - RetsHttpResponse httpResponse = doRequest(req); - ChangePasswordResponse response = new ChangePasswordResponse(httpResponse.getInputStream()); - // response will throw an exception if there is an error code - return (response != null); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/RetsUtil.java b/src/main/java/com/ossez/usreio/client/RetsUtil.java deleted file mode 100644 index 7eb6787..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsUtil.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.ossez.usreio.client; - -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.EncoderException; -import org.apache.commons.codec.net.URLCodec; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Random utility functions - * - * @author YuCheng Hu - */ -public class RetsUtil { - public static void copyStream(InputStream in, OutputStream out) throws IOException { - byte[] buf = new byte[512]; - int count; - while (true) { - count = in.read(buf); - if (count < 1) { - in.close(); - out.close(); - return; - } - while (count > 0) { - out.write(buf); - } - } - } - - public static String urlEncode(String string) { - try { - return new URLCodec().encode(string); - } catch (EncoderException e) { - throw new RuntimeException(e); - } - } - - public static String urlDecode(String string) { - try { - return new URLCodec().decode(string); - } catch (DecoderException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/ossez/usreio/client/RetsVersion.java b/src/main/java/com/ossez/usreio/client/RetsVersion.java deleted file mode 100644 index e7f7fdf..0000000 --- a/src/main/java/com/ossez/usreio/client/RetsVersion.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.Serializable; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; - -/** - * RetsVersion - * - * @author YuCheng Hu - */ -public class RetsVersion implements Serializable { - - public static final String RETS_VERSION_HEADER = "RETS-Version"; - - public static final RetsVersion RETS_10 = new RetsVersion(1, 0, 0, 0); - public static final RetsVersion RETS_15 = new RetsVersion(1, 5, 0, 0); - public static final RetsVersion RETS_16 = new RetsVersion(1, 6, 0, 0); - public static final RetsVersion RETS_17 = new RetsVersion(1, 7, 0, 0); - public static final RetsVersion RETS_1_7_2 = new RetsVersion(1, 7, 2, 0); - public static final RetsVersion RETS_1_8 = new RetsVersion(1, 8, 0, 0); - public static final RetsVersion RETS_1_9 = new RetsVersion(1, 9, 0, 0); - public static final RetsVersion DEFAULT = RETS_1_7_2; - - private int mMajor; - private int mMinor; - private int mRevision; - private int mDraft; - - public RetsVersion(int major, int minor) { - this(major, minor, 0, 0); - } - - /** - * @deprecated use new RetsVersion(major, minor, 0, draft) - */ - @Deprecated - public RetsVersion(int major, int minor, int draft) { - this(major, minor, 0, draft); - } - - public RetsVersion(int major, int minor, int revision, int draft) { - this.mMajor = major; - this.mMinor = minor; - this.mRevision = revision; - this.mDraft = draft; - } - - public int getMajor() { - return this.mMajor; - } - - public int getMinor() { - return this.mMinor; - } - - public int getRevision() { - return this.mRevision; - } - - public int getDraft() { - return this.mDraft; - } - - @Override - public String toString() { - if (this.mRevision == 0) { - if (this.mDraft == 0) { - return "RETS/" + this.mMajor + "." + this.mMinor; - } - return "RETS/" + this.mMajor + "." + this.mMinor + "d" + this.mDraft; - } - if (this.mDraft == 0) { - return "RETS/" + this.mMajor + "." + this.mMinor + "." + this.mRevision; - } - return "RETS/" + this.mMajor + "." + this.mMinor + "." + this.mRevision + "d" + this.mDraft; - } - - @Override - public boolean equals(Object o) { - if (o instanceof RetsVersion) { - RetsVersion v = (RetsVersion) o; - if ((v.getMajor() == this.mMajor) && (v.getMinor() == this.mMinor) && (v.getRevision() == this.mRevision) && (v.getDraft() == this.mDraft)) { - return true; - } - } - return false; - } - - public static RetsVersion getVersion(String ver) { - if (StringUtils.isEmpty(ver)) return null; - String[] split = StringUtils.trimToEmpty(ver).split("\\."); - int ma = NumberUtils.toInt(split[0], 1); - int mn = split.length > 1 ? NumberUtils.toInt(split[1], 0) : 0; - int re = 0; - int dr = 0; - if (split.length > 2) { - split = StringUtils.defaultString(split[2]).split("d"); - re = NumberUtils.toInt(split[0], 0); - dr = split.length > 1 ? NumberUtils.toInt(split[1], 0) : 0; - } - return new RetsVersion(ma, mn, re, dr); - } - -} diff --git a/src/main/java/com/ossez/usreio/client/SearchRequest.java b/src/main/java/com/ossez/usreio/client/SearchRequest.java deleted file mode 100644 index 9cbc3fa..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchRequest.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.ossez.usreio.client; - -/** - * - * The search request sent from search() in RetsSession - * - */ - -public class SearchRequest extends RetsHttpRequest { - - public static final int COUNT_NONE = 1; - public static final int COUNT_FIRST = 2; - public static final int COUNT_ONLY = 3; - public static final String FORMAT_STANDARD_XML = "STANDARD-XML"; - public static final String FORMAT_COMPACT = "COMPACT"; - public static final String FORMAT_COMPACT_DECODED = "COMPACT-DECODED"; - public static final String RETS_DMQL1 = "DMQL"; - public static final String RETS_DMQL2 = "DMQL2"; - public static final String KEY_TYPE = "SearchType"; - public static final String KEY_CLASS = "Class"; - public static final String KEY_DMQLVERSION = "QueryType"; - public static final String KEY_QUERY = "Query"; - public static final String KEY_COUNT = "Count"; - public static final String KEY_FORMAT = "Format"; - public static final String KEY_LIMIT = "Limit"; - public static final String KEY_OFFSET = "Offset"; - public static final String KEY_SELECT = "Select"; - public static final String KEY_RESTRICTEDINDICATOR = "RestrictedIndicator"; - public static final String KEY_STANDARDNAMES = "StandardNames"; - - - private String type; - - public SearchRequest(String stype, String sclass, String query) { - setQueryParameter(KEY_TYPE, stype); - this.type = stype; - setQueryParameter(KEY_CLASS, sclass); - setQueryParameter(KEY_QUERY, query); - setQueryParameter(KEY_FORMAT, FORMAT_COMPACT); - setQueryParameter(KEY_DMQLVERSION, RETS_DMQL2); - } - - - @Override - public void setUrl(CapabilityUrls urls) { - setUrl(urls.getSearchUrl()); - } - - public String getType() { - return this.type; - } - - public void setCountNone() { - setQueryParameter(KEY_COUNT, null); - } - - public void setCountFirst() { - setQueryParameter(KEY_COUNT, "1"); - } - - public void setCountOnly() { - setQueryParameter(KEY_COUNT, "2"); - } - - public void setFormatCompact() { - setQueryParameter(KEY_FORMAT, FORMAT_COMPACT); - } - - public void setFormatCompactDecoded() { - setQueryParameter(KEY_FORMAT, FORMAT_COMPACT_DECODED); - } - - public void setFormatStandardXml() { - setQueryParameter(KEY_FORMAT, FORMAT_STANDARD_XML); - } - - public void setFormatStandardXml(String dtdVersion) { - setQueryParameter(KEY_FORMAT, FORMAT_STANDARD_XML + ":" + dtdVersion); - } - - public void setLimit(int count) { - setQueryParameter(KEY_LIMIT, Integer.toString(count)); - } - - public void setLimitNone() { - setQueryParameter(KEY_LIMIT, null); - } - - public void setSelect(String sel) { - setQueryParameter(KEY_SELECT, sel); - } - - public void setRestrictedIndicator(String rest) { - setQueryParameter(KEY_RESTRICTEDINDICATOR, rest); - } - - public void setStandardNames() { - setQueryParameter(KEY_STANDARDNAMES, "1"); - } - - public void setSystemNames() { - setQueryParameter(KEY_STANDARDNAMES, null); - } - - public void setOffset(int offset) { - setQueryParameter(KEY_OFFSET, Integer.toString(offset)); - } - - public void setOffsetNone() { - setQueryParameter(KEY_OFFSET, null); - } - - /** TODO should the search automatically handle this??? shouldn't this be setable by vendor is that predicatable? */ - @Override - public void setVersion(RetsVersion ver) { - if (RetsVersion.RETS_10.equals(ver)) { - setQueryParameter(KEY_DMQLVERSION, RETS_DMQL1); - } else { - setQueryParameter(KEY_DMQLVERSION, RETS_DMQL2); - } - } -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResult.java b/src/main/java/com/ossez/usreio/client/SearchResult.java deleted file mode 100644 index 73dfc79..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResult.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ossez.usreio.client; - -import java.util.NoSuchElementException; -import java.util.Iterator; - -/** - * Interface for retrieving additional information from of a result from a RETS query/search - * - */ - -public interface SearchResult extends SearchResultInfo { - public String[] getRow(int idx) throws NoSuchElementException; - - public Iterator iterator(); - - public String[] getColumns(); - - public boolean isMaxrows(); - - public int getCount(); - - public boolean isComplete(); -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResultCollector.java b/src/main/java/com/ossez/usreio/client/SearchResultCollector.java deleted file mode 100644 index 7e346b6..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResultCollector.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.ossez.usreio.client; - -/** - * Interface for a setting properties of a result from a query (used by SearchResultHandler) - */ - -public interface SearchResultCollector { - - public void setCount(int count); - - public void setColumns(String[] columns); - - public boolean addRow(String[] row); - - public void setMaxrows(); - - public void setComplete(); -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResultHandler.java b/src/main/java/com/ossez/usreio/client/SearchResultHandler.java deleted file mode 100644 index b0003ed..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResultHandler.java +++ /dev/null @@ -1,280 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.IOException; -import java.io.InputStream; -import java.util.LinkedList; -import java.util.List; -import java.util.StringTokenizer; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; -/** - * - * Handles XML parsing from response setting the proper fields using a SearchResultCollector - * - */ -public class SearchResultHandler implements ContentHandler, ErrorHandler{ - private static final Log LOG = LogFactory.getLog(SearchResultHandler.class); - private static SAXParserFactory FACTORY = SAXParserFactory.newInstance(); - - private int dataCount; - private SearchResultCollector collector; - private StringBuffer currentEntry; - private String delimiter; - private Locator locator; - private String[] columns; - private InvalidReplyCodeHandler invalidReplyCodeHandler; - private CompactRowPolicy compactRowPolicy; - - public SearchResultHandler(SearchResultCollector r) { - this(r, InvalidReplyCodeHandler.FAIL, CompactRowPolicy.DEFAULT); - } - - public SearchResultHandler(SearchResultCollector r, InvalidReplyCodeHandler invalidReplyCodeHandler, CompactRowPolicy badRowPolicy) { - this.compactRowPolicy = badRowPolicy; - if (r == null) - throw new NullPointerException("SearchResultCollector must not be null"); - - if (invalidReplyCodeHandler == null) - throw new NullPointerException("InvalidReplyCodeHandler must not be null"); - - if (badRowPolicy == null) - throw new NullPointerException("BadRowPolicy must not be null"); - - this.collector = r; - this.dataCount = 0; - this.invalidReplyCodeHandler = invalidReplyCodeHandler; - } - - public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { - String name = localName; - if (localName.equals("")) { - name = qName; - } - if (name.equals("RETS") || name.equals("RETS-STATUS")) { - String rawrepcode = atts.getValue("ReplyCode"); - try { - int repcode = Integer.parseInt(rawrepcode); - if (repcode > 0) { - try { - if (ReplyCode.MAXIMUM_RECORDS_EXCEEDED.equals(repcode)) - return; - - if (ReplyCode.NO_RECORDS_FOUND.equals(repcode)) - return; - - if (name.equals("RETS")) - this.invalidReplyCodeHandler.invalidRetsReplyCode(repcode); - else - this.invalidReplyCodeHandler.invalidRetsStatusReplyCode(repcode); - } catch (InvalidReplyCodeException e) { - String text = atts.getValue("", "ReplyText"); - e.setRemoteMessage(text); - throw new SAXException(e); - } - } - } catch (NumberFormatException e) { - throw new SAXParseException("Invalid ReplyCode '" + rawrepcode + "'", this.locator); - } - return; - } - if (name == "COUNT") { - String s = atts.getValue("Records"); - if (s == null) { - s = atts.getValue("", "Records"); - if (s == null) { - throw new SAXParseException("COUNT tag has no Records " + "attribute", this.locator); - } - } - int i = Integer.parseInt(s, 10); - this.collector.setCount(i); - return; - } - if (name == "DELIMITER") { - String s = atts.getValue("value"); - if (s == null) { - s = atts.getValue("", "value"); - if (s == null) { - throw new RuntimeException("Invalid Delimiter"); - } - } - int i = Integer.parseInt(s, 16); - this.delimiter = "" + (char) i; - return; - } - if (name == "COLUMNS" || name == "DATA") { - this.currentEntry = new StringBuffer(); - return; - } - if (name == "MAXROWS") { - this.collector.setMaxrows(); - return; - } - // Unknown tag. danger, will. - LOG.warn("Unknown tag: " + name + ", qName = " + qName); - - } - - public void characters(char[] ch, int start, int length) { - if (this.currentEntry != null) { - this.currentEntry.append(ch, start, length); - } - } - - public void ignorableWhitespace(char[] ch, int start, int length) { - // we ignore NOZINK! - characters(ch, start, length); - } - - /** do NOT use string.split() unless your prepared to deal with loss due to token boundary conditions */ - private String[] split(String input) throws SAXParseException { - if (this.delimiter == null) { - throw new SAXParseException("Invalid compact format - DELIMITER not specified", this.locator); - } - if( !input.startsWith(this.delimiter) ){ - throw new SAXParseException("Invalid compact format", this.locator); - } - StringTokenizer tkn = new StringTokenizer(input, this.delimiter, true); - List list = new LinkedList(); - tkn.nextToken(); // junk the first element - String last = null; - while (tkn.hasMoreTokens()) { - String next = tkn.nextToken(); - if (next.equals(this.delimiter)) { - if (last == null) { - list.add(""); - } else { - last = null; - } - } else { - list.add(next); - last = next; - } - } - return (String[]) list.toArray(new String[0]); - } - - public void endElement(String uri, String localName, String qName) throws SAXParseException { - String name = localName; - if (name.equals("")) { - name = qName; - } - if (name.equals("COLUMNS") || name.equals("DATA")) { - String[] contents = split(this.currentEntry.toString()); - if (name.equals("COLUMNS")) { - this.collector.setColumns(contents); - this.columns = contents; - } else { - if( this.compactRowPolicy.apply(this.dataCount, this.columns, contents) ) { - this.dataCount++; - this.collector.addRow(contents); - } - } - this.currentEntry = null; - } - } - - public void startDocument() { - LOG.info("Start document"); - } - - public void endDocument() { - LOG.info("Document ended"); - this.collector.setComplete(); - } - - public void startPrefixMapping(String prefix, String uri) throws SAXException { - // LOG.debug("prefix mapping: " + prefix); - } - - public void endPrefixMapping(String prefix) throws SAXException { - // LOG.debug("prefix mapping: " + prefix); - } - - public void processingInstruction(String target, String data) throws SAXException { - throw new SAXException("processing instructions not supported: " + "target=" + target + ", data=" + data); - } - - public void skippedEntity(String name) throws SAXException { - throw new SAXException("skipped entities not supported: name=" + name); - } - - public void setDocumentLocator(Locator locator) { - this.locator = locator; - } - - public void error(SAXParseException e) throws SAXException { - throw e; - } - - public void fatalError(SAXParseException e) throws SAXException { - throw e; - } - - public void warning(SAXParseException e) { - LOG.warn("an error occured while parsing. Attempting to continue", e); - } - - - - public void parse(InputSource src) throws RetsException { - parse(src, null); - } - /** - * - * created in order to pass the charset to the parser for proper encoding - * @param str - * @param charset - * @throws RetsException - */ - - public void parse(InputStream str, String charset) throws RetsException { - parse(new InputSource(str), charset); - try { - str.close(); - } catch (IOException e) { - throw new RetsException(e); - } - } - /** - * Pareses given source with the given charset - * - * @param src - * @throws RetsException - */ - public void parse(InputSource src, String charset) throws RetsException { - String encoding = src.getEncoding(); - if (encoding == null && (charset != null)){ - encoding = charset; - LOG.warn("Charset from headers:" + charset + ". Setting as correct encoding for parsing"); - src.setEncoding(encoding); - } - try { - SAXParser p = FACTORY.newSAXParser(); - XMLReader r = p.getXMLReader(); - r.setContentHandler(this); - r.setErrorHandler(this); - r.parse(src); - } catch (SAXException se) { - if (se.getException() != null && se.getException() instanceof RetsException) { - throw (RetsException) se.getException(); - } - throw new RetsException(se); - } catch (Exception e) { - LOG.error("An exception occured", e); - throw new RetsException(e); - - } - } -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResultImpl.java b/src/main/java/com/ossez/usreio/client/SearchResultImpl.java deleted file mode 100644 index 7fdf99b..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResultImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.ossez.usreio.client; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; - -import org.apache.commons.logging.LogFactory; -/** - * Concrete Implementation of SearchResult interface - * - */ -public class SearchResultImpl implements SearchResult, SearchResultCollector { - - private String[] columnNames; - private int count; - private List rows; - private boolean maxRows; - private boolean complete; - - public SearchResultImpl() { - this.count = 0; - this.rows = new ArrayList(); - this.maxRows = false; - this.complete = false; - } - - public void setCount(int count) { - this.count = count; - } - - public int getCount() { - if (this.count > 0) { - return this.count; - } - return this.rows.size(); - } - - public int getRowCount() { - return this.rows.size(); - } - - public void setColumns(String[] columns) { - this.columnNames = columns; - } - - public String[] getColumns() { - return this.columnNames; - } - - public boolean addRow(String[] row) { - if (row.length > this.columnNames.length) { - throw new IllegalArgumentException(String.format("Invalid number of result columns: got %s, expected %s",row.length, this.columnNames.length)); - } - if (row.length < this.columnNames.length) { - LogFactory.getLog(SearchResultCollector.class).warn(String.format("Row %s: Invalid number of result columns: got %s, expected ",this.rows.size(), row.length, this.columnNames.length)); - } - return this.rows.add(row); - } - - public String[] getRow(int idx) { - if (idx >= this.rows.size()) { - throw new NoSuchElementException(); - } - return this.rows.get(idx); - } - - public Iterator iterator() { - return this.rows.iterator(); - } - - public void setMaxrows() { - this.maxRows = true; - } - - public boolean isMaxrows() { - return this.maxRows; - } - - public void setComplete() { - this.complete = true; - } - - public boolean isComplete() { - return this.complete; - } -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResultInfo.java b/src/main/java/com/ossez/usreio/client/SearchResultInfo.java deleted file mode 100644 index b6af74c..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResultInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.ossez.usreio.client; - -/** - * Interface that describes high level information - * about the results of a search. - * @author jrayburn - */ -public interface SearchResultInfo { - public int getCount() throws RetsException; - - public String[] getColumns() throws RetsException; - - /** @throws IllegalStateException */ - public boolean isMaxrows() throws RetsException, IllegalStateException; - - /** - * Indicates that processing of this search - * is complete. - * - * @return true if this SearchResultSet is finished processing. - * @throws RetsException Thrown if there is an error - * processing the SearchResultSet. - */ - public boolean isComplete() throws RetsException; -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResultProcessor.java b/src/main/java/com/ossez/usreio/client/SearchResultProcessor.java deleted file mode 100644 index 4614f6a..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResultProcessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.InputStream; -import java.io.Reader; - -/** - * Interface for parsing results from a RETS query/search - */ -public interface SearchResultProcessor { - public SearchResultSet parse(InputStream in) throws RetsException; - - public SearchResultSet parse(Reader in) throws RetsException; -} diff --git a/src/main/java/com/ossez/usreio/client/SearchResultSet.java b/src/main/java/com/ossez/usreio/client/SearchResultSet.java deleted file mode 100644 index ba2a4e5..0000000 --- a/src/main/java/com/ossez/usreio/client/SearchResultSet.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ossez.usreio.client; - -/** - * Iterator style interface for processing the results - * of a RETS search a single time. Information about the - * search can be retrieved once processing is complete by - * calling the getInfo() method. - * - * @author YuCheng Hu - */ -public interface SearchResultSet extends SearchResultInfo { - public String[] next() throws RetsException; - - public boolean hasNext() throws RetsException; -} diff --git a/src/main/java/com/ossez/usreio/client/SingleObjectResponse.java b/src/main/java/com/ossez/usreio/client/SingleObjectResponse.java deleted file mode 100644 index 7e07661..0000000 --- a/src/main/java/com/ossez/usreio/client/SingleObjectResponse.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.ossez.usreio.client; - -import com.ossez.usreio.common.util.CaseInsensitiveTreeMap; - -import java.io.InputStream; -import java.util.Map; - -/** - * Representation of a single object returned - * from a RETS server. - * - * @author jrayburn - */ -public class SingleObjectResponse { - - public static final String CONTENT_TYPE = "Content-Type"; - public static final String LOCATION = "Location"; - public static final String CONTENT_DESCRIPTION = "Content-Description"; - public static final String OBJECT_ID = "Object-ID"; - public static final String CONTENT_ID = "Content-ID"; - - private Map headers; - private InputStream inputStream; - - public SingleObjectResponse(Map headers, InputStream in) { - this.headers = new CaseInsensitiveTreeMap(headers); - this.inputStream = in; - } - - public String getType() { - return (String) this.headers.get(CONTENT_TYPE); - } - - public String getContentID() { - return (String) this.headers.get(CONTENT_ID); - } - - public String getObjectID() { - return (String) this.headers.get(OBJECT_ID); - } - - public String getDescription() { - return (String) this.headers.get(CONTENT_DESCRIPTION); - } - - public String getLocation() { - return (String) this.headers.get(LOCATION); - } - - public InputStream getInputStream() { - return this.inputStream; - } -} diff --git a/src/main/java/com/ossez/usreio/client/SinglePartInputStream.java b/src/main/java/com/ossez/usreio/client/SinglePartInputStream.java deleted file mode 100644 index b777a2e..0000000 --- a/src/main/java/com/ossez/usreio/client/SinglePartInputStream.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.PushbackInputStream; - -class SinglePartInputStream extends FilterInputStream { - private static final int EOS = -1; - - private final String boundary; - private boolean eos; - - - SinglePartInputStream(PushbackInputStream partInput, String boundary) { - super(partInput); - this.boundary = boundary; - } - - @Override - public int read() throws IOException { - int read = this.getPushBackStream().read(); - // was this the start of a boundary? - if( read != '\r' && read != '\n' ) return read; - this.getPushBackStream().unread(read); - byte[] peek = new byte[ "\r\n".length() + this.boundary.length()]; - // if so, check and see if the rest of the boundary is next - int peekRead = this.getPushBackStream().read(peek); - this.getPushBackStream().unread(peek, 0, peekRead); - if( new String(peek).contains(this.boundary) ) return EOS; - // if not, just a coincidence, just return the byte - return this.getPushBackStream().read(); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if(this.eos) return EOS; - - int read = off; - for( ; read < off + len; read++) { - int nextByte = this.read(); - if(nextByte == EOS) { - this.eos = true; - break; - } - - b[read] = (byte) nextByte; - } - return ( read - off ); - } - - @Override - public int read(byte[] b) throws IOException { - return this.read(b, 0, b.length); - } - - @Override - public void close() { - // noop - part of a larger stream - } - - private PushbackInputStream getPushBackStream() { - return (PushbackInputStream) this.in; - } -} diff --git a/src/main/java/com/ossez/usreio/client/StreamingSearchResultProcessor.java b/src/main/java/com/ossez/usreio/client/StreamingSearchResultProcessor.java deleted file mode 100644 index 6bd92e8..0000000 --- a/src/main/java/com/ossez/usreio/client/StreamingSearchResultProcessor.java +++ /dev/null @@ -1,324 +0,0 @@ -package com.ossez.usreio.client; - -import java.io.InputStream; -import java.io.Reader; -import java.util.LinkedList; - -import org.apache.commons.logging.LogFactory; -import org.xml.sax.InputSource; - -/** - * SearchResultProcessor that returns a streaming SearchResult implementation. - * - * @author jrayburn - */ -public class StreamingSearchResultProcessor implements SearchResultProcessor { - private final int mBufferSize; - private final int mTimeout; - private InvalidReplyCodeHandler mInvalidReplyCodeHandler; - private CompactRowPolicy mCompactRowPolicy; - - /** - * Construct a StreamingSearchResultProcessor. - * - * Waits indefinitely for buffer to be read from by - * client. - * - * @param bufferSize - * How many rows to buffer - */ - public StreamingSearchResultProcessor(int bufferSize) { - this(bufferSize, 0); - } - - /** - * Construct a StreamingSearchResultProcessor. - * - * Waits timeout milliseconds for buffer to - * be read from by client. - * - * @param bufferSize - * How many rows to buffer - * - * @param timeout - * How long to wait, in milliseconds, for the buffer - * to be read from when full. 0 indicates an indefinite - * wait. - */ - public StreamingSearchResultProcessor(int bufferSize, int timeout) { - super(); - this.mBufferSize = bufferSize; - this.mTimeout = timeout; - } - - /** how to deal with badly delimited data */ - public void setCompactRowPolicy(CompactRowPolicy badRowPolicy) { - this.mCompactRowPolicy = badRowPolicy; - } - - private CompactRowPolicy getCompactRowPolicy() { - if (this.mCompactRowPolicy == null) - return CompactRowPolicy.DEFAULT; - return this.mCompactRowPolicy; - } - - public void setInvalidRelyCodeHandler(InvalidReplyCodeHandler invalidReplyCodeHandler) { - this.mInvalidReplyCodeHandler = invalidReplyCodeHandler; - } - - private InvalidReplyCodeHandler getInvalidRelyCodeHandler() { - if (this.mInvalidReplyCodeHandler == null) - return InvalidReplyCodeHandler.FAIL; - return this.mInvalidReplyCodeHandler; - } - - public SearchResultSet parse(InputStream reader) { - return parse(new InputSource(reader)); - } - - public SearchResultSet parse(Reader reader) { - return parse(new InputSource(reader)); - } - - public SearchResultSet parse(InputSource source) { - StreamingSearchResult result = new StreamingSearchResult(this.mBufferSize, this.mTimeout); - StreamingThread thread = new StreamingThread(source, result, this.getInvalidRelyCodeHandler(), this.getCompactRowPolicy()); - thread.start(); - return result; - } - -} - -class StreamingThread extends Thread { - private StreamingSearchResult mResult; - private InputSource mSource; - private InvalidReplyCodeHandler mInvalidReplyCodeHandler; - private CompactRowPolicy badRowPolicy; - - public StreamingThread(InputSource source, StreamingSearchResult result,InvalidReplyCodeHandler invalidReplyCodeHandler, CompactRowPolicy badRowPolicy) { - this.mSource = source; - this.mResult = result; - this.mInvalidReplyCodeHandler = invalidReplyCodeHandler; - this.badRowPolicy = badRowPolicy; - } - - @Override - public void run() { - SearchResultHandler handler = new SearchResultHandler(this.mResult, this.mInvalidReplyCodeHandler, this.badRowPolicy); - try { - handler.parse(this.mSource); - } catch (RetsException e) { - this.mResult.setException(e); - } catch (Exception e) { - // socket timeouts, etc while obtaining xml bytes from InputSource ... - this.mResult.setException(new RetsException("Low level exception while attempting to parse input from source.", e)); - } - } - -} - -class StreamingSearchResult implements SearchResultSet, SearchResultCollector { - - private static final int PREPROCESS = 0; - private static final int BUFFER_AVAILABLE = 1; - private static final int BUFFER_FULL = 2; - private static final int COMPLETE = 3; - - private final int timeout; - private final int bufferSize; - private final LinkedList buffer; - - private boolean mMaxrows; - private int state; - private String[] columns; - private int count; - private RetsException exception; - - public StreamingSearchResult(int bufferSize, int timeout) { - if (bufferSize < 1) - throw new IllegalArgumentException("[bufferSize=" + bufferSize + "] must be greater than zero"); - if (timeout < 0) - throw new IllegalArgumentException("[timeout=" + timeout + "] must be greater than or equal to zero"); - - this.bufferSize = bufferSize; - this.timeout = timeout; - this.state = PREPROCESS; - this.buffer = new LinkedList(); - this.count = -1; - this.columns = null; - this.exception = null; - } - - // ------------ Producer Methods - - public synchronized boolean addRow(String[] row) { - if (row.length > this.columns.length) { - throw new IllegalArgumentException(String.format("Invalid number of result columns: got %s, expected %s",row.length, this.columns.length)); - } - if (row.length < this.columns.length) { - LogFactory.getLog(SearchResultCollector.class).warn(String.format("Row %s: Invalid number of result columns: got %s, expected ",this.count, row.length, this.columns.length)); - } - - if (state() > BUFFER_FULL) { - if (this.exception == null) - setException(new RetsException("Attempting to add rows to buffer when in complete state")); - throw new RuntimeException(this.exception); - } - - // check complete. - while (checkRuntime() && state() == BUFFER_FULL) { - _wait(); - - if (state() >= BUFFER_FULL) { - if (this.exception == null) - setException(new RetsException("Timeout writing to streaming result set buffer, timeout length = " - + this.timeout)); - throw new RuntimeException(this.exception); - } - } - - this.buffer.addLast(row); - - if (this.bufferSize == this.buffer.size()) - pushState(BUFFER_FULL); - else - pushState(BUFFER_AVAILABLE); - - this.notifyAll(); - return true; - } - - public synchronized void setComplete() { - pushState(COMPLETE); - notifyAll(); - } - - public synchronized void setCount(int count) { - this.count = count; - pushState(PREPROCESS); - notifyAll(); - } - - public synchronized void setColumns(String[] columns) { - this.columns = columns; - pushState(BUFFER_AVAILABLE); - notifyAll(); - } - - public synchronized void setMaxrows() { - this.mMaxrows = true; - pushState(COMPLETE); - notifyAll(); - } - - synchronized void setException(RetsException e) { - this.exception = e; - pushState(COMPLETE); - notifyAll(); - } - - // ----------- Consumer Methods - - public synchronized boolean hasNext() throws RetsException { - // wait for someone to add data to the queue - // or flag complete - while (checkException() && state() < COMPLETE) { - if (!this.buffer.isEmpty()) - return true; - - _wait(); - } - - return !this.buffer.isEmpty(); - } - - public synchronized String[] next() throws RetsException { - checkException(); - String[] row = this.buffer.removeFirst(); - if (this.state < COMPLETE) - pushState(BUFFER_AVAILABLE); - this.notifyAll(); - return row; - } - - public synchronized int getCount() throws RetsException { - while (checkException() && state() < BUFFER_AVAILABLE) { - _wait(); - } - return this.count; - } - - public synchronized String[] getColumns() throws RetsException { - while (checkException() && state() < BUFFER_AVAILABLE) { - _wait(); - } - return this.columns; - } - - public synchronized boolean isMaxrows() throws RetsException { - checkException(); - - if (!isComplete()) - throw new IllegalStateException("Cannot call isMaxRows until isComplete == true"); - - return this.mMaxrows; - } - - public synchronized SearchResultInfo getInfo() throws RetsException { - checkException(); - - if (!isComplete()) - throw new IllegalStateException("Cannot call isMaxRows until isComplete == true"); - - return this; - } - - public synchronized boolean isComplete() throws RetsException { - checkException(); - return state() >= COMPLETE; - } - - private synchronized boolean checkRuntime() { - try { - return checkException(); - } catch (RetsException e) { - throw new RuntimeException(e); - } - } - - private synchronized boolean checkException() throws RetsException { - // considering doing something here to maintain the original - // stack trace but also provide the stack trace from this - // location... - if (this.exception != null) - throw this.exception; - return true; - } - - private void _wait() { - try { - wait(this.timeout); - } catch (InterruptedException e) { - pushState(COMPLETE); - throw new RuntimeException(e); - } - } - - private void pushState(int newState) { - if (this.state >= COMPLETE && newState < COMPLETE) - throw new IllegalStateException("Cannot revert from complete state"); - - if (this.state > PREPROCESS && newState <= PREPROCESS) - throw new IllegalStateException("Cannot revert to preprocess state"); - - if (newState < this.state && newState != BUFFER_AVAILABLE && this.state != BUFFER_FULL) - throw new IllegalStateException("Cannot go back in state unless reverting to buffer available from full"); - - this.state = newState; - } - - private int state() { - return this.state; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/retsapi/InputStreamDataSource.java b/src/main/java/com/ossez/usreio/client/retsapi/InputStreamDataSource.java deleted file mode 100644 index 0865d3c..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/InputStreamDataSource.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.ossez.usreio.client.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/com/ossez/usreio/client/retsapi/RETSActionTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSActionTransaction.java deleted file mode 100644 index 4992803..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSActionTransaction.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * RETSActionTransaction.java - * - * This class is used to build an action transaction - * - * @author jbrush - * @version 1.0 - */ -public class RETSActionTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(RETSActionTransaction.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/com/ossez/usreio/client/retsapi/RETSBasicResponseParser.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSBasicResponseParser.java deleted file mode 100644 index 4ff1ca5..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSBasicResponseParser.java +++ /dev/null @@ -1,128 +0,0 @@ -// -// RETSBasicResponseParser.java -// NARRETSClasses -// -// Created by Bruce Toback on 1/3/05. -// Copyright (c) 2005 __MyCompanyName__. All rights reserved. -// - -package com.ossez.usreio.client.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/com/ossez/usreio/client/retsapi/RETSChangePasswordTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSChangePasswordTransaction.java deleted file mode 100644 index 7806f23..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSChangePasswordTransaction.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.ossez.usreio.client.retsapi; - - -import com.ossez.usreio.common.util.DesCrypter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.security.MessageDigest; - - -/** - * Send a Change Password transaction to the server. - * - * @author jbrush - * @version 1.0 - */ -public class RETSChangePasswordTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(RETSActionTransaction.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")); - - logger.debug("username=" + username); - logger.debug("oldPassword=" + oldPassword); - logger.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) { - logger.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/com/ossez/usreio/client/retsapi/RETSConnection.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSConnection.java deleted file mode 100644 index 793e3ea..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSConnection.java +++ /dev/null @@ -1,635 +0,0 @@ -/* - * RETSConnection.java - * - * Created on November 16, 2001, 1:33 PM - */ -package com.ossez.usreio.client.retsapi; - -//import com.aftexsw.util.bzip.CBZip2InputStream; - -import com.ossez.usreio.common.util.RETSConfigurator; -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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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 { - private final static Logger logger = LoggerFactory.getLogger(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 transaction transaction to execute - */ - public void execute(RETSTransaction transaction) { - execute(transaction, false); - } - - /** - * Executes a transaction - * - * @param transaction transaction to execute - */ - public void executeStreamResponse(RETSTransaction transaction) { - execute(transaction, true); - } - - /** - * Executes a transaction - * - * @param transaction transaction to execute - * @param asStream - */ - 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) { - logger.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(); - } - } - - logger.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; - - logger.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(); - - logger.debug(transaction.getRequestType() + " URL : {" + serverUrl + "}"); - - if (serverUrl == null) { - logger.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"; - } - logger.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) { - logger.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)); - logger.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/com/ossez/usreio/client/retsapi/RETSGetMetadataTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSGetMetadataTransaction.java deleted file mode 100644 index 57e91cd..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSGetMetadataTransaction.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * RETSGetMetadataTransaction.java - * - * @author jbrush - * @version - */ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/////////////////////////////////////////////////////////////////////// -public class RETSGetMetadataTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(RETSConnection.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/com/ossez/usreio/client/retsapi/RETSGetObjectTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSGetObjectTransaction.java deleted file mode 100644 index 7c9d721..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSGetObjectTransaction.java +++ /dev/null @@ -1,339 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.mail.MessagingException; -import javax.mail.internet.InternetHeaders; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMultipart; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; - -/** - * RETSGetObjectTransaction.java - * - * @author jbrush - * @version 1.0 - */ -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}. - */ - - private final static Logger logger = LoggerFactory.getLogger(RETSLoginTransaction.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) { - logger.debug("set Resource=" + str); - setRequestVariable("Resource", str); - } - - - /** - * Sets the type attribute to the string passed in. - * - * @param str type attribute value - */ - public void setType(String str) { - logger.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) { - logger.debug("set ID=" + str.trim()); - setRequestVariable("ID", str.trim()); - } else { - logger.debug("set ID=" + str); - } - } - - /** - * Sets the location attribute to the string passed in. - * - * @param str location attribute value - */ - public void setLocation(String str) { - logger.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. - * 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"); - logger.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) { - logger.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(); - - logger.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. - logger.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. - * 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 - */ - 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) - logger.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. - */ - 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. - *

- * 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; - logger.debug("--- MimeBodyPart Content--> " + content); - } - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Kablewie!"); - } - return inputStream; - } - -} diff --git a/src/main/java/com/ossez/usreio/client/retsapi/RETSLoginTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSLoginTransaction.java deleted file mode 100644 index 75ac5af..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSLoginTransaction.java +++ /dev/null @@ -1,184 +0,0 @@ -package com.ossez.usreio.client.retsapi; - - -//import org.apache.regexp.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - - -/** - * RETSLoginTransaction.java - * - * - * @author jbrush - * @version 1.0 - */ -public class RETSLoginTransaction extends RETSTransaction { - /** log4j Object */ - private final static Logger logger = LoggerFactory.getLogger(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. - * @return 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); - - logger.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(); - logger.debug("protocol = [" + protocol + "]"); - logger.debug("host = [" + host + "]"); - logger.debug("port = [" + port + "]"); - - //cat.debug("path = ["+path+"]"); - //cat.debug("file = ["+file+"]"); - return protocol + "://" + host + ((port > 0) ? (":" + port) : ""); - } catch (MalformedURLException e) { - logger.warn("getUrlRoot:MalformedURLException myUrl=\"" + myUrl + - "\""); - } - - return null; - } -} diff --git a/src/main/java/com/ossez/usreio/client/retsapi/RETSLogoutTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSLogoutTransaction.java deleted file mode 100644 index e560a86..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSLogoutTransaction.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Send a logout transaction to the server. - * - * @author jbrush - * @version 1.0 - */ -public class RETSLogoutTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(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/com/ossez/usreio/client/retsapi/RETSSearchAgentTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchAgentTransaction.java deleted file mode 100644 index b39da36..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchAgentTransaction.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * RETSSearchAgentTransaction.java - * Search for agents - * - * @author jbrush - * @version 1.0 - */ -public class RETSSearchAgentTransaction extends RETSSearchTransaction { - private final static Logger logger = LoggerFactory.getLogger(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/com/ossez/usreio/client/retsapi/RETSSearchOfficeTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchOfficeTransaction.java deleted file mode 100644 index f4a3324..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchOfficeTransaction.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * RETSSearchOfficeTransaction.java - * Performs a getOffice Transaction - * - * @author jbrush - * @version 1.0 - */ -public class RETSSearchOfficeTransaction extends RETSSearchTransaction { - private final static Logger logger = LoggerFactory.getLogger(RETSSearchOfficeTransaction.class); - - /** Creates new a RETSSearchOfficeTransaction - * - */ - public RETSSearchOfficeTransaction() { - super(); - setSearchType("Office"); - - setSearchClass("Office"); -// setSearchClass("OFF"); - } -} diff --git a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyBatchTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyBatchTransaction.java deleted file mode 100644 index 3fafce8..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyBatchTransaction.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * RETSSearchPropertyBatchTransaction.java - * - * @author jbrush - * @version 1.0 - */ -public class RETSSearchPropertyBatchTransaction extends RETSSearchTransaction { - private final static Logger logger = LoggerFactory.getLogger(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/com/ossez/usreio/client/retsapi/RETSSearchPropertyTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyTransaction.java deleted file mode 100644 index 25bae28..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchPropertyTransaction.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * RETSSearchPropertyBatchTransaction.java - * - * @author jbrush - * @version 1.0 - */ -public class RETSSearchPropertyTransaction extends RETSSearchTransaction { - private final static Logger logger = LoggerFactory.getLogger(RETSSearchPropertyTransaction.class); - - public RETSSearchPropertyTransaction() { - super(); - setSearchType("Property"); - } -} diff --git a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchTransaction.java deleted file mode 100644 index cd8b816..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSSearchTransaction.java +++ /dev/null @@ -1,321 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import com.ossez.usreio.common.util.AttributeExtracter; -import com.ossez.usreio.common.util.ResourceLocator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.helpers.DefaultHandler; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import java.io.ByteArrayInputStream; -import java.util.HashMap; - -/** - * RETSSearchTransaction - * - * @author YuCheng Hu - */ -public class RETSSearchTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(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) { - logger.warn("Hash Error:", 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/com/ossez/usreio/client/retsapi/RETSServerInformationTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSServerInformationTransaction.java deleted file mode 100644 index 43e6db9..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSServerInformationTransaction.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * RETSServerInformationTransaction.java - * - * @author pobrien - * @version - */ -package com.ossez.usreio.client.retsapi; - - -//import java.util.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/////////////////////////////////////////////////////////////////////// -public class RETSServerInformationTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(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/com/ossez/usreio/client/retsapi/RETSTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSTransaction.java deleted file mode 100644 index 8f6e844..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSTransaction.java +++ /dev/null @@ -1,277 +0,0 @@ -/** - * RETSTransaction.java - * - * @author jbrush - * @version - */ -package com.ossez.usreio.client.retsapi; - -import com.ossez.usreio.common.util.RETSRequestResponse; -import org.apache.regexp.RE; -import org.apache.regexp.RESyntaxException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.StringTokenizer; - - -/////////////////////////////////////////////////////////////////////// - -public class RETSTransaction extends RETSRequestResponse { - private final static Logger logger = LoggerFactory.getLogger(RETSConnection.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) { - logger.debug("RESPONSEHEADERMAP ==> " + responseHeaderMap.toString()); -// responseString = (String) responseHeaderMap.get(headerName.toLowerCase()); - logger.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 { - logger.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) { - logger.debug(logMessage); - } -} - - diff --git a/src/main/java/com/ossez/usreio/client/retsapi/RETSUpdateTransaction.java b/src/main/java/com/ossez/usreio/client/retsapi/RETSUpdateTransaction.java deleted file mode 100644 index edde09c..0000000 --- a/src/main/java/com/ossez/usreio/client/retsapi/RETSUpdateTransaction.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.ossez.usreio.client.retsapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Iterator; -import java.util.Map; - - -/** - * RETSUpdateTransaction.java - * - * @author pobrien - * @version 1.0 - */ -public class RETSUpdateTransaction extends RETSTransaction { - private final static Logger logger = LoggerFactory.getLogger(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 str type attribute value - */ - public void setType(String str) { - logger.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) { - logger.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) { - logger.debug("set Delimiter=" + str); - setRequestVariable("Delimiter", str); - } - - public String getDelimiter() { - return getRequestVariable("Delimiter"); - } - - public void setRecord(String str) { - logger.debug("set Record=" + str); - setRequestVariable("Record", str); - } - - public void setWarningResponse(String str) { - logger.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); - } - - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/AttrType.java b/src/main/java/com/ossez/usreio/tests/common/metadata/AttrType.java deleted file mode 100644 index dc4a680..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/AttrType.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -import java.io.Serializable; - -public interface AttrType extends Serializable { - public T parse(String value, boolean strict) throws MetaParseException; - public Class getType(); - public String render(T value); -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/JDomCompactBuilder.java b/src/main/java/com/ossez/usreio/tests/common/metadata/JDomCompactBuilder.java deleted file mode 100644 index d231522..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/JDomCompactBuilder.java +++ /dev/null @@ -1,702 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -import java.util.LinkedList; -import java.util.List; -import java.util.StringTokenizer; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.dom4j.*; - -import com.ossez.usreio.tests.common.metadata.types.MClass; -import com.ossez.usreio.tests.common.metadata.types.MEditMask; -import com.ossez.usreio.tests.common.metadata.types.MLookup; -import com.ossez.usreio.tests.common.metadata.types.MLookupType; -import com.ossez.usreio.tests.common.metadata.types.MObject; -import com.ossez.usreio.tests.common.metadata.types.MResource; -import com.ossez.usreio.tests.common.metadata.types.MSearchHelp; -import com.ossez.usreio.tests.common.metadata.types.MSystem; -import com.ossez.usreio.tests.common.metadata.types.MTable; -import com.ossez.usreio.tests.common.metadata.types.MUpdate; -import com.ossez.usreio.tests.common.metadata.types.MUpdateType; -import com.ossez.usreio.tests.common.metadata.types.MValidationExpression; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternal; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternalType; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookup; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookupType; -import org.dom4j.io.SAXReader; -import org.xml.sax.InputSource; - -public class JDomCompactBuilder extends MetadataBuilder { - public static final String CONTAINER_PREFIX = "METADATA-"; - public static final String CONTAINER_ROOT = "RETS"; - public static final String CONTAINER_METADATA = "METADATA"; - public static final String CONTAINER_SYSTEM = "METADATA-SYSTEM"; - public static final String CONTAINER_RESOURCE = "METADATA-RESOURCE"; - public static final String CONTAINER_FOREIGNKEY = "METADATA-FOREIGN_KEY"; - public static final String CONTAINER_CLASS = "METADATA-CLASS"; - public static final String CONTAINER_TABLE = "METADATA-TABLE"; - public static final String CONTAINER_UPDATE = "METADATA-UPDATE"; - public static final String CONTAINER_UPDATETYPE = "METADATA-UPDATE_TYPE"; - public static final String CONTAINER_OBJECT = "METADATA-OBJECT"; - public static final String CONTAINER_SEARCHHELP = "METADATA-SEARCH_HELP"; - public static final String CONTAINER_EDITMASK = "METADATA-EDITMASK"; - public static final String CONTAINER_UPDATEHELP = "METADATA-UPDATE_HELP"; - public static final String CONTAINER_LOOKUP = "METADATA-LOOKUP"; - public static final String CONTAINER_LOOKUPTYPE = "METADATA-LOOKUP_TYPE"; - public static final String CONTAINER_VALIDATIONLOOKUP = "METADATA-VALIDATION_LOOKUP"; - public static final String CONTAINER_VALIDATIONLOOKUPTYPE = "METADATA-VALIDATION_LOOKUP_TYPE"; - public static final String CONTAINER_VALIDATIONEXPRESSION = "METADATA-VALIDATION_EXPRESSION"; - public static final String CONTAINER_VALIDATIONEXTERNAL = "METADATA-VALIDATION_EXTERNAL"; - public static final String CONTAINER_VALIDATIONEXTERNALTYPE = "METADATA-VALIDATION_EXTERNAL_TYPE"; - public static final String ELEMENT_SYSTEM = "SYSTEM"; - public static final String COLUMNS = "COLUMNS"; - public static final String DATA = "DATA"; - public static final String ATTRIBUTE_RESOURCE = "Resource"; - public static final String ATTRIBUTE_CLASS = "Class"; - public static final String ATTRIBUTE_UPDATE = "Update"; - public static final String ATTRIBUTE_LOOKUP = "Lookup"; - public static final String ATTRIBUTE_VALIDATIONEXTERNAL = "ValidationExternal"; - public static final String ATTRIBUTE_VALIDATIONLOOKUP = "ValidationLookup"; - private static final Log LOG = LogFactory.getLog(JDomCompactBuilder.class); - - @Override - public Metadata doBuild(Object src) throws MetadataException { - return build((Document) src); - } - - public Metadata build(InputSource source) throws MetadataException { - SAXReader builder = new SAXReader(); - Document document; - try { - document = builder.read(source); - } catch (DocumentException e) { - throw new MetadataException("Couldn't build document", e); - } - return build(document); - } - - @Override - public MetaObject[] parse(Object src) throws MetadataException { - return parse((Document) src); - } - - public MetaObject[] parse(Document src) throws MetadataException { - Element root = src.getRootElement(); - if (!root.getName().equals(CONTAINER_ROOT)) { - throw new MetadataException("Invalid root element"); - } - Element container = root.element(CONTAINER_SYSTEM); - if (container != null) { - MSystem sys = processSystem(container); - if (root.element(CONTAINER_RESOURCE) != null) { - Metadata m = new Metadata(sys); - recurseAll(m, root); - } - return new MetaObject[] { sys }; - } - container = root.element(CONTAINER_RESOURCE); - if (container != null) { - return processResource(container); - } - container = root.element(CONTAINER_CLASS); - if (container != null) { - return processClass(container); - } - container = root.element(CONTAINER_TABLE); - if (container != null) { - return processTable(container); - } - container = root.element(CONTAINER_UPDATE); - if (container != null) { - return processUpdate(container); - } - container = root.element(CONTAINER_UPDATETYPE); - if (container != null) { - return processUpdateType(container); - } - container = root.element(CONTAINER_OBJECT); - if (container != null) { - return processObject(container); - } - container = root.element(CONTAINER_SEARCHHELP); - if (container != null) { - return processSearchHelp(container); - } - container = root.element(CONTAINER_EDITMASK); - if (container != null) { - return processEditMask(container); - } - container = root.element(CONTAINER_LOOKUP); - if (container != null) { - return processLookup(container); - } - container = root.element(CONTAINER_LOOKUPTYPE); - if (container != null) { - return processLookupType(container); - } - container = root.element(CONTAINER_VALIDATIONLOOKUP); - if (container != null) { - return processValidationLookup(container); - } - container = root.element(CONTAINER_VALIDATIONLOOKUPTYPE); - if (container != null) { - return processValidationLookupType(container); - } - container = root.element(CONTAINER_VALIDATIONEXTERNAL); - if (container != null) { - return processValidationExternal(container); - } - container = root.element(CONTAINER_VALIDATIONEXTERNALTYPE); - if (container != null) { - return processValidationExternalType(container); - } - container = root.element(CONTAINER_VALIDATIONEXPRESSION); - if (container != null) { - return processValidationExpression(container); - } - return null; - } - - public Metadata build(Document src) throws MetadataException { - Element root = src.getRootElement(); - if (!root.getName().equals(CONTAINER_ROOT)) { - throw new MetadataException("Invalid root element"); - } - Element element = root.element(CONTAINER_SYSTEM); - if (element == null) { - throw new MetadataException("Missing element " + CONTAINER_SYSTEM); - } - MSystem sys = processSystem(element); - Metadata metadata; - metadata = new Metadata(sys); - recurseAll(metadata, root); - return metadata; - } - - private void recurseAll(Metadata metadata, Element root) throws MetaParseException { - attachResource(metadata, root); - attachClass(metadata, root); - attachTable(metadata, root); - attachUpdate(metadata, root); - attachUpdateType(metadata, root); - attachObject(metadata, root); - attachSearchHelp(metadata, root); - attachEditMask(metadata, root); - attachLookup(metadata, root); - attachLookupType(metadata, root); - attachValidationLookup(metadata, root); - attachValidationLookupType(metadata, root); - attachValidationExternal(metadata, root); - attachValidationExternalType(metadata, root); - attachValidationExpression(metadata, root); - } - - private void setAttributes(MetaObject obj, String[] columns, String[] data) { - int count = columns.length; - if (count > data.length) { - count = data.length; - } - for (int i = 0; i < count; i++) { - String column = columns[i]; - String datum = data[i]; - if (!datum.equals("")) { - setAttribute(obj, column, datum); - } - } - } - - private String[] getColumns(Element el) { - Element cols = el.element(COLUMNS); - return split(cols); - } - - /** do NOT use string.split() unless your prepared to deal with loss due to token boundary conditions */ - private String[] split(Element el) { - if( el == null ) return null; - final String delimiter = "\t"; - StringTokenizer tkn = new StringTokenizer(el.getText(), delimiter, true); - List list = new LinkedList(); - tkn.nextToken(); // junk the first element - String last = null; - while (tkn.hasMoreTokens()) { - String next = tkn.nextToken(); - if (next.equals(delimiter)) { - if (last == null) { - list.add(""); - } else { - last = null; - } - } else { - list.add(next); - last = next; - } - } - return (String[]) list.toArray(new String[0]); - } - - /** - * Gets an attribute that is not expected to be null (i.e. an attribute that - * MUST exist). - * - * @param element Element - * @param name Attribute name - * @return value of attribute - * @throws MetaParseException if the value is null. - */ - private String getNonNullAttribute(Element element, String name) throws MetaParseException { - String value = element.attributeValue(name); - if (value == null) { - throw new MetaParseException("Attribute '" + name + "' not found on tag " + toString(element)); - } - return value; - } - - private String toString(Element element) { - StringBuffer buffer = new StringBuffer(); - List attributes = element.attributes(); - buffer.append("'").append(element.getName()).append("'"); - buffer.append(", attributes: ").append(attributes); - return buffer.toString(); - } - - private MSystem processSystem(Element container) { - Element element = container.element(ELEMENT_SYSTEM); - MSystem system = buildSystem(); - // system metadata is such a hack. the first one here is by far my favorite - String comment = container.elementText(MSystem.COMMENTS); - String systemId = element.attributeValue(MSystem.SYSTEMID); - String systemDescription = element.attributeValue(MSystem.SYSTEMDESCRIPTION); - String version = container.attributeValue(MSystem.VERSION); - String date = container.attributeValue(MSystem.DATE); - setAttribute(system, MSystem.COMMENTS, comment); - setAttribute(system, MSystem.SYSTEMID, systemId); - setAttribute(system, MSystem.SYSTEMDESCRIPTION, systemDescription); - setAttribute(system, MSystem.VERSION, version); - setAttribute(system, MSystem.DATE, date); - return system; - } - - private void attachResource(Metadata metadata, Element root) { - MSystem system = metadata.getSystem(); - List containers = root.elements(CONTAINER_RESOURCE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource[] resources = this.processResource(container); - for (int j = 0; j < resources.length; j++) { - system.addChild(MetadataType.RESOURCE, resources[j]); - } - } - } - - private MResource[] processResource(Element resourceContainer) { - String[] columns = getColumns(resourceContainer); - List rows = resourceContainer.elements(DATA); - MResource[] resources = new MResource[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MResource resource = buildResource(); - setAttributes(resource, columns, data); - resources[i] = resource; - } - return resources; - } - - private void attachClass(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_CLASS); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - String resourceId = getNonNullAttribute(container, ATTRIBUTE_RESOURCE); - MResource resource = metadata.getResource(resourceId); - MClass[] classes = processClass(container); - for (int j = 0; j < classes.length; j++) { - resource.addChild(MetadataType.CLASS, classes[j]); - } - } - } - - private MClass[] processClass(Element classContainer) throws MetaParseException { - String name = classContainer.getName(); - String resourceId = getNonNullAttribute(classContainer, ATTRIBUTE_RESOURCE); - LOG.debug("resource name: " + resourceId + " for container " + name); - String[] columns = getColumns(classContainer); - List rows = classContainer.elements(DATA); - MClass[] classes = new MClass[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MClass clazz = buildClass(); - setAttributes(clazz, columns, data); - classes[i] = clazz; - } - return classes; - } - - private void attachTable(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_TABLE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - String resourceId = getNonNullAttribute(container, ATTRIBUTE_RESOURCE); - String className = getNonNullAttribute(container, ATTRIBUTE_CLASS); - MClass clazz = metadata.getMClass(resourceId, className); - - if (clazz == null) { - //MarketLinx Strikes!!! - LOG.warn("Found table metadata for resource class: " + resourceId + ":" + className - + " but there is no class metadata for " + resourceId + ":" + className); - continue; - } - - MTable[] fieldMetadata = processTable(container); - for (int j = 0; j < fieldMetadata.length; j++) { - clazz.addChild(MetadataType.TABLE, fieldMetadata[j]); - } - } - } - - private MTable[] processTable(Element tableContainer) { - String[] columns = getColumns(tableContainer); - List rows = tableContainer.elements(DATA); - MTable[] fieldMetadata = new MTable[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MTable mTable = buildTable(); - setAttributes(mTable, columns, data); - fieldMetadata[i] = mTable; - } - return fieldMetadata; - } - - private void attachUpdate(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_UPDATE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MClass parent = metadata.getMClass(getNonNullAttribute(container, ATTRIBUTE_RESOURCE), getNonNullAttribute( - container, ATTRIBUTE_CLASS)); - MUpdate[] updates = processUpdate(container); - for (int j = 0; j < updates.length; j++) { - parent.addChild(MetadataType.UPDATE, updates[j]); - } - } - } - - private MUpdate[] processUpdate(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MUpdate[] updates = new MUpdate[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MUpdate update = buildUpdate(); - setAttributes(update, columns, data); - updates[i] = update; - } - return updates; - } - - private void attachUpdateType(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_UPDATETYPE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MUpdate parent = metadata.getUpdate(getNonNullAttribute(container, ATTRIBUTE_RESOURCE), - getNonNullAttribute(container, ATTRIBUTE_CLASS), getNonNullAttribute(container, ATTRIBUTE_UPDATE)); - MUpdateType[] updateTypes = processUpdateType(container); - for (int j = 0; j < updateTypes.length; j++) { - parent.addChild(MetadataType.UPDATE_TYPE, updateTypes[j]); - } - } - } - - private MUpdateType[] processUpdateType(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MUpdateType[] updateTypes = new MUpdateType[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MUpdateType updateType = buildUpdateType(); - setAttributes(updateType, columns, data); - updateTypes[i] = updateType; - } - return updateTypes; - } - - private void attachObject(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_OBJECT); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(getNonNullAttribute(container, ATTRIBUTE_RESOURCE)); - MObject[] objects = processObject(container); - for (int j = 0; j < objects.length; j++) { - parent.addChild(MetadataType.OBJECT, objects[j]); - } - } - } - - private MObject[] processObject(Element objectContainer) { - String[] columns = getColumns(objectContainer); - List rows = objectContainer.elements(DATA); - MObject[] objects = new MObject[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MObject object = buildObject(); - setAttributes(object, columns, data); - objects[i] = object; - } - return objects; - } - - private void attachSearchHelp(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_SEARCHHELP); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(getNonNullAttribute(container, ATTRIBUTE_RESOURCE)); - MSearchHelp[] searchHelps = processSearchHelp(container); - for (int j = 0; j < searchHelps.length; j++) { - parent.addChild(MetadataType.SEARCH_HELP, searchHelps[j]); - } - } - } - - private MSearchHelp[] processSearchHelp(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MSearchHelp[] searchHelps = new MSearchHelp[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MSearchHelp searchHelp = buildSearchHelp(); - setAttributes(searchHelp, columns, data); - searchHelps[i] = searchHelp; - } - return searchHelps; - } - - private void attachEditMask(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_EDITMASK); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(getNonNullAttribute(container, ATTRIBUTE_RESOURCE)); - MEditMask[] editMasks = processEditMask(container); - for (int j = 0; j < editMasks.length; j++) { - parent.addChild(MetadataType.EDITMASK, editMasks[j]); - } - } - } - - private MEditMask[] processEditMask(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MEditMask[] editMasks = new MEditMask[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MEditMask editMask = buildEditMask(); - setAttributes(editMask, columns, data); - editMasks[i] = editMask; - } - return editMasks; - } - - private void attachLookup(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_LOOKUP); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(getNonNullAttribute(container, ATTRIBUTE_RESOURCE)); - MLookup[] lookups = processLookup(container); - for (int j = 0; j < lookups.length; j++) { - parent.addChild(MetadataType.LOOKUP, lookups[j]); - } - } - } - - private MLookup[] processLookup(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MLookup[] lookups = new MLookup[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MLookup lookup = buildLookup(); - setAttributes(lookup, columns, data); - lookups[i] = lookup; - } - return lookups; - } - - private void attachLookupType(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_LOOKUPTYPE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MLookup parent = metadata.getLookup(getNonNullAttribute(container, ATTRIBUTE_RESOURCE), - getNonNullAttribute(container, ATTRIBUTE_LOOKUP)); - - if (parent == null) { - LOG.warn("Skipping lookup type: could not find lookup for tag " + toString(container)); - continue; - } - - MLookupType[] lookupTypes = processLookupType(container); - for (int j = 0; j < lookupTypes.length; j++) { - parent.addChild(MetadataType.LOOKUP_TYPE, lookupTypes[j]); - } - } - } - - private MLookupType[] processLookupType(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MLookupType[] lookupTypes = new MLookupType[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MLookupType lookupType = buildLookupType(); - setAttributes(lookupType, columns, data); - lookupTypes[i] = lookupType; - } - return lookupTypes; - } - - private void attachValidationLookup(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_VALIDATIONLOOKUP); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(getNonNullAttribute(container, ATTRIBUTE_RESOURCE)); - MValidationLookup[] validationLookups = processValidationLookup(container); - for (int j = 0; j < validationLookups.length; j++) { - parent.addChild(MetadataType.VALIDATION_LOOKUP, validationLookups[j]); - } - } - } - - private MValidationLookup[] processValidationLookup(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MValidationLookup[] validationLookups = new MValidationLookup[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MValidationLookup validationLookup = buildValidationLookup(); - setAttributes(validationLookup, columns, data); - validationLookups[i] = validationLookup; - } - return validationLookups; - } - - private void attachValidationLookupType(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_VALIDATIONLOOKUPTYPE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MValidationLookup parent = metadata.getValidationLookup(getNonNullAttribute(container, ATTRIBUTE_RESOURCE), - getNonNullAttribute(container, ATTRIBUTE_VALIDATIONLOOKUP)); - MValidationLookupType[] validationLookupTypes = processValidationLookupType(container); - for (int j = 0; j < validationLookupTypes.length; j++) { - parent.addChild(MetadataType.VALIDATION_LOOKUP_TYPE, validationLookupTypes[j]); - } - } - } - - private MValidationLookupType[] processValidationLookupType(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MValidationLookupType[] validationLookupTypes = new MValidationLookupType[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MValidationLookupType validationLookupType = buildValidationLookupType(); - setAttributes(validationLookupType, columns, data); - validationLookupTypes[i] = validationLookupType; - } - return validationLookupTypes; - } - - private void attachValidationExternal(Metadata metadata, Element root) { - List containers = root.elements(CONTAINER_VALIDATIONEXTERNAL); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(container.attributeValue(ATTRIBUTE_RESOURCE)); - MValidationExternal[] validationExternals = processValidationExternal(container); - for (int j = 0; j < validationExternals.length; j++) { - parent.addChild(MetadataType.VALIDATION_EXTERNAL, validationExternals[j]); - } - } - } - - private MValidationExternal[] processValidationExternal(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MValidationExternal[] validationExternals = new MValidationExternal[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MValidationExternal validationExternal = buildValidationExternal(); - setAttributes(validationExternal, columns, data); - validationExternals[i] = validationExternal; - } - return validationExternals; - } - - private void attachValidationExternalType(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_VALIDATIONEXTERNALTYPE); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MValidationExternal parent = metadata.getValidationExternal(getNonNullAttribute(container, - ATTRIBUTE_RESOURCE), getNonNullAttribute(container, ATTRIBUTE_VALIDATIONEXTERNAL)); - MValidationExternalType[] validationExternalTypes = processValidationExternalType(container); - for (int j = 0; j < validationExternalTypes.length; j++) { - parent.addChild(MetadataType.VALIDATION_EXTERNAL_TYPE, validationExternalTypes[j]); - } - } - } - - private MValidationExternalType[] processValidationExternalType(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MValidationExternalType[] validationExternalTypes = new MValidationExternalType[rows.size()]; - for (int i = 0; i < rows.size(); i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MValidationExternalType validationExternalType = buildValidationExternalType(); - setAttributes(validationExternalType, columns, data); - validationExternalTypes[i] = validationExternalType; - } - return validationExternalTypes; - } - - private void attachValidationExpression(Metadata metadata, Element root) throws MetaParseException { - List containers = root.elements(CONTAINER_VALIDATIONEXPRESSION); - for (int i = 0; i < containers.size(); i++) { - Element container = (Element) containers.get(i); - MResource parent = metadata.getResource(getNonNullAttribute(container, ATTRIBUTE_RESOURCE)); - MValidationExpression[] expressions = processValidationExpression(container); - for (int j = 0; j < expressions.length; j++) { - parent.addChild(MetadataType.VALIDATION_EXPRESSION, expressions[j]); - } - } - } - - private MValidationExpression[] processValidationExpression(Element container) { - String[] columns = getColumns(container); - List rows = container.elements(DATA); - MValidationExpression[] expressions = new MValidationExpression[rows.size()]; - for (int i = 0; i < expressions.length; i++) { - Element element = (Element) rows.get(i); - String[] data = split(element); - MValidationExpression expression = buildValidationExpression(); - setAttributes(expression, columns, data); - expressions[i] = expression; - } - return expressions; - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/JDomStandardBuilder.java b/src/main/java/com/ossez/usreio/tests/common/metadata/JDomStandardBuilder.java deleted file mode 100644 index 7fd0d3c..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/JDomStandardBuilder.java +++ /dev/null @@ -1,628 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - -import com.ossez.usreio.tests.common.metadata.types.MClass; -import com.ossez.usreio.tests.common.metadata.types.MEditMask; -import com.ossez.usreio.tests.common.metadata.types.MForeignKey; -import com.ossez.usreio.tests.common.metadata.types.MLookup; -import com.ossez.usreio.tests.common.metadata.types.MLookupType; -import com.ossez.usreio.tests.common.metadata.types.MObject; -import com.ossez.usreio.tests.common.metadata.types.MResource; -import com.ossez.usreio.tests.common.metadata.types.MSearchHelp; -import com.ossez.usreio.tests.common.metadata.types.MSystem; -import com.ossez.usreio.tests.common.metadata.types.MTable; -import com.ossez.usreio.tests.common.metadata.types.MUpdate; -import com.ossez.usreio.tests.common.metadata.types.MUpdateHelp; -import com.ossez.usreio.tests.common.metadata.types.MUpdateType; -import com.ossez.usreio.tests.common.metadata.types.MValidationExpression; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternal; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternalType; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookup; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookupType; -import org.dom4j.Attribute; -import org.dom4j.Document; -import org.dom4j.Element; - -/** Parses apart a complete Standard-XML response, returns a Metadata object */ -public class JDomStandardBuilder extends MetadataBuilder { - public static final String ELEMENT_SYSTEM = "System"; - public static final String ELEMENT_RESOURCE = "Resource"; - public static final String ELEMENT_FOREIGNKEY = "ForeignKey"; - public static final String ELEMENT_CLASS = "Class"; - public static final String ELEMENT_TABLE = "Field"; - public static final String ELEMENT_UPDATE = "UpdateType"; - public static final String ELEMENT_UPDATETYPE = "UpdateField"; - public static final String ELEMENT_OBJECT = "Object"; - public static final String ELEMENT_SEARCHHELP = "SearchHelp"; - public static final String ELEMENT_EDITMASK = "EditMask"; - public static final String ELEMENT_UPDATEHELP = "UpdateHelp"; - public static final String ELEMENT_LOOKUP = "Lookup"; - public static final String ELEMENT_LOOKUPTYPE = "LookupType"; - public static final String ELEMENT_VALIDATIONLOOKUP = "ValidationLookup"; - public static final String ELEMENT_VALIDATIONLOOKUPTYPE = "ValidationLookupType"; - public static final String ELEMENT_VALIDATIONEXPRESSION = "ValidationExpression"; - public static final String ELEMENT_VALIDATIONEXTERNAL = "ValidationExternalType"; - public static final String ELEMENT_VALIDATIONEXTERNALTYPE = "ValidationExternal"; - public static final String ATTRIBUTE_RESOURCEID = ELEMENT_RESOURCE; - public static final String ATTRIBUTE_CLASSNAME = ELEMENT_CLASS; - public static final String ATTRIBUTE_UPDATE = ELEMENT_UPDATE; - public static final String ATTRIBUTE_LOOKUP = ELEMENT_LOOKUP; - public static final String ATTRIBUTE_VALIDATIONLOOKUP = ELEMENT_VALIDATIONLOOKUP; - public static final String ATTRIBUTE_VALIDATIONEXTERNAL = ELEMENT_VALIDATIONEXTERNAL; - public static final Map sType2Element = new HashMap(); - - static { - sType2Element.put(MetadataType.SYSTEM, ELEMENT_SYSTEM); - sType2Element.put(MetadataType.RESOURCE, ELEMENT_RESOURCE); - sType2Element.put(MetadataType.FOREIGNKEYS, ELEMENT_FOREIGNKEY); - sType2Element.put(MetadataType.CLASS, ELEMENT_CLASS); - sType2Element.put(MetadataType.TABLE, ELEMENT_TABLE); - sType2Element.put(MetadataType.UPDATE, ELEMENT_UPDATE); - sType2Element.put(MetadataType.UPDATE_TYPE, ELEMENT_UPDATETYPE); - sType2Element.put(MetadataType.SEARCH_HELP, ELEMENT_SEARCHHELP); - sType2Element.put(MetadataType.EDITMASK, ELEMENT_EDITMASK); - sType2Element.put(MetadataType.UPDATE_HELP, ELEMENT_UPDATEHELP); - sType2Element.put(MetadataType.LOOKUP, ELEMENT_LOOKUP); - sType2Element.put(MetadataType.LOOKUP_TYPE, ELEMENT_LOOKUPTYPE); - sType2Element.put(MetadataType.VALIDATION_LOOKUP, ELEMENT_VALIDATIONLOOKUP); - sType2Element.put(MetadataType.VALIDATION_LOOKUP_TYPE, ELEMENT_VALIDATIONLOOKUPTYPE); - sType2Element.put(MetadataType.VALIDATION_EXTERNAL, ELEMENT_VALIDATIONEXTERNAL); - sType2Element.put(MetadataType.VALIDATION_EXTERNAL_TYPE, ELEMENT_VALIDATIONEXTERNALTYPE); - sType2Element.put(MetadataType.VALIDATION_EXPRESSION, ELEMENT_VALIDATIONEXPRESSION); - } - - @Override - public Metadata doBuild(Object src) throws MetadataException { - return build((Document) src); - } - - public Metadata build(Document src) throws MetadataException { - Element element = src.getRootElement(); - expectElement(element, CONTAINER_ROOT); - element = getElement(element, CONTAINER_METADATA); - return build(element); - } - - @Override - public MetaObject[] parse(Object src) throws MetadataException { - return parse((Document) src); - } - - public MetaObject[] parse(Document src) throws MetadataException { - Element element = src.getRootElement(); - expectElement(element, CONTAINER_ROOT); - Element container = getElement(element, CONTAINER_METADATA); - boolean recurse = checkForRecursion(container); - List list = container.elements(); - if (list.size() == 0) { - return null; - } - return processContainer(null, (Element) list.get(0), recurse); - } - - /** - * Function to determine if a request contains recursive data or not. - * This is done here instead of inside processContainer because, well, - * it's easier and more reliable (processContainer might not figure out - * that a request is recursive until the third or 4th child if there are - * no children for the first couple of elements. - * - * @param top The outside METADATA container. - * @return true if the request is recursive - * - */ - private boolean checkForRecursion(Element top) { - /* - * this seems like a really nasty loop. However, if there are a - * lot of recursive elements, we'll find out pretty quickly, and if - * we fall all the way to the end then there probably wasn't that - * much to look through. - */ - Iterator children = top.elements().iterator(); - while (children.hasNext()) { - /* each of these is a container (METADATA-*) type */ - Element element = (Element) children.next(); - Iterator iterator = element.elements().iterator(); - while (iterator.hasNext()) { - /* each of these is an item element */ - Element child = (Element) iterator.next(); - Iterator subtypes = child.elements().iterator(); - while (subtypes.hasNext()) { - Element subtype = (Element) subtypes.next(); - if (subtype.getName().startsWith(CONTAINER_PREFIX)) { - return true; - } - } - } - } - return false; - } - - private MetaObject[] processContainer(MetaObject parent, Element container, boolean recursion) { - MetadataType type = (MetadataType) sContainer2Type.get(container.getName()); - if (type == null) { - throw new RuntimeException("no matching type for container " + container.getName()); - } - List elements = container.elements((String) sType2Element.get(type)); - String path = getPath(container); - List output = null; - if (parent == null) { - output = new LinkedList(); - } - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MetaObject obj = newType(type); - setAttributes(obj, element); - if (output != null) { - output.add(obj); - } - if (parent != null) { - parent.addChild(type, obj); - } else { - /** - * Weirdness abounds. There IS an ID attribute of System, - * and the SystemID is included in the Metadata container - * attributes, but the system id is not part of the metadata - * request path for a getMetadata request, so we ignore it. - */ - if (!type.equals(MetadataType.SYSTEM)) { - obj.setPath(path); - } - } - if (recursion) { - MetadataType[] childTypes = obj.getChildTypes(); - for (int j = 0; j < childTypes.length; j++) { - MetadataType childType = childTypes[j]; - Element childContainer = element.element(CONTAINER_PREFIX + childType.name()); - if (childContainer == null) { - obj.addChild(childType, null); - } else { - processContainer(obj, childContainer, true); - } - } - } - } - if (output == null) { - return null; - } - return (MetaObject[]) output.toArray(new MetaObject[0]); - } - - String getPath(Element container) { - String resource = container.attributeValue(ATTRIBUTE_RESOURCEID); - if (resource == null) { - return null; - } - String classname = container.attributeValue(ATTRIBUTE_CLASSNAME); - if (classname != null) { - String update = container.attributeValue(ATTRIBUTE_UPDATE); - if (update != null) { - return resource + ":" + classname + ":" + update; - } - return resource + ":" + classname; - } - String lookup = container.attributeValue(ATTRIBUTE_LOOKUP); - if (lookup != null) { - return resource + ":" + lookup; - } - String vallkp = container.attributeValue(ATTRIBUTE_VALIDATIONLOOKUP); - if (vallkp != null) { - return resource + ":" + vallkp; - } - String vale = container.attributeValue(ATTRIBUTE_VALIDATIONEXTERNAL); - if (vale != null) { - return resource + ":" + vale; - } - return resource; - } - - public Metadata build(Element element) throws MetadataException { - expectElement(element, CONTAINER_METADATA); - element = getElement(element, CONTAINER_SYSTEM); - //maybe i get the attribute here - MSystem sys = processSystem(element); - return new Metadata(sys); - } - - private Element getElement(Element parent, String type) throws MetadataException { - Element element = parent.element(type); - if (element == null) { - throw new MetadataException("Missing element " + type); - } - return element; - } - - - private void expectElement(Element element, String type) throws MetadataException { - if (!element.getName().equalsIgnoreCase(type)) {// changed to ignore case - throw new MetadataException("Expecting element " + type + ", got " + element.getName()); - } - } - - private void setAttributes(MetaObject obj, Element el) { - - List children = el.elements(); - for (int i = 0; i < children.size(); i++) { - Element child = (Element) children.get(i); - String name = child.getName(); - if (!name.startsWith(CONTAINER_PREFIX)) { - String value = child.getTextTrim(); - setAttribute(obj, name, value); - } else { - // LOG.info("skipping container element " + name); - } - } - } - - //when atrributes from the xml element are needed - public void setAttributesFromXMLAttr(MetaObject obj, Element el) { - - Iterator attrIter = el.getParent().attributes().iterator(); - - while(attrIter.hasNext()){ - Attribute attr = (Attribute) attrIter.next(); - String name = attr.getName(); - String value= attr.getValue().trim(); - setAttribute(obj, name, value); - } - } - - - /** - * If we're a recursive request, initialize all possible child types so - * we don't have to try to pull them later, dynamically - */ - private void init(MetaObject item) { - MetadataType[] childTypes = item.getChildTypes(); - for (int i = 0; i < childTypes.length; i++) { - MetadataType type = childTypes[i]; - item.addChild(type, null); - } - } - - private MSystem processSystem(Element container) { - Element element = container.element(ELEMENT_SYSTEM); - if (element == null){ - element = container.element(ELEMENT_SYSTEM.toUpperCase()); - } - MSystem system = buildSystem(); - init(system); - setAttributesFromXMLAttr(system, element); - setAttributes(system, element); - Element child; - child = element.element(CONTAINER_RESOURCE); - if (child != null) { - processResource(system, child); - } - child = element.element(CONTAINER_FOREIGNKEY); - if (child != null) { - processForeignKey(system, child); - } - return system; - } - - private void processResource(MSystem system, Element container) { - List resources = container.elements(ELEMENT_RESOURCE); - for (int i = 0; i < resources.size(); i++) { - Element element = (Element) resources.get(i); - MResource resource = buildResource(); - init(resource); - setAttributes(resource, element); - system.addChild(MetadataType.RESOURCE, resource); - Element child; - child = element.element(CONTAINER_CLASS); - if (child != null) { - processClass(resource, child); - } - child = element.element(CONTAINER_OBJECT); - if (child != null) { - processObject(resource, child); - } - child = element.element(CONTAINER_SEARCH_HELP); - if (child != null) { - processSearchHelp(resource, child); - } - child = element.element(CONTAINER_EDITMASK); - if (child != null) { - processEditMask(resource, child); - } - child = element.element(CONTAINER_LOOKUP); - if (child != null) { - processLookup(resource, child); - } - child = element.element(CONTAINER_UPDATEHELP); - if (child != null) { - processUpdateHelp(resource, child); - } - child = element.element(CONTAINER_VALIDATIONLOOKUP); - if (child != null) { - processValidationLookup(resource, child); - } - child = element.element(CONTAINER_VALIDATIONEXPRESSION); - if (child != null) { - processValidationExpression(resource, child); - } - child = element.element(CONTAINER_VALIDATIONEXTERNAL); - if (child != null) { - processValidationExternal(resource, child); - } - } - } - - private void processEditMask(MResource parent, Element container) { - List elements = container.elements(ELEMENT_EDITMASK); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MEditMask mask = buildEditMask(); - setAttributes(mask, element); - parent.addChild(MetadataType.EDITMASK, mask); - } - } - - private void processLookup(MResource parent, Element container) { - List elements15 = container.elements(ELEMENT_LOOKUP); - List elements17 = container.elements(ELEMENT_LOOKUPTYPE); - List elements; - //some Rets Servers have lookuptype and lookup elements interchanged - if (elements15.isEmpty()){ - elements = elements17; - } else { - elements = elements15; - } - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MLookup lookup = buildLookup(); - init(lookup); - setAttributes(lookup, element); - parent.addChild(MetadataType.LOOKUP, lookup); - Element child = element.element(CONTAINER_LOOKUPTYPE); - if (child != null) { - processLookupType(lookup, child); - } - } - } - - private void processLookupType(MLookup parent, Element container) { - - List elements15 = container.elements(ELEMENT_LOOKUPTYPE);// check spec - List elements17 = container.elements(ELEMENT_LOOKUP); - List elements; - //some Rets Servers have lookuptype and lookup elements interchanged - if (elements15.isEmpty()){ - elements = elements17; - } else { - elements = elements15; - } - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MLookupType type = buildLookupType(); - setAttributes(type, element); - parent.addChild(MetadataType.LOOKUP_TYPE, type); - } - } - - private void processUpdateHelp(MResource parent, Element container) { - List elements = container.elements(ELEMENT_UPDATEHELP); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MUpdateHelp help = buildUpdateHelp(); - setAttributes(help, element); - parent.addChild(MetadataType.UPDATE_HELP, help); - } - } - - private void processValidationLookup(MResource parent, Element container) { - List elements = container.elements(ELEMENT_VALIDATIONLOOKUP); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MValidationLookup lookup = buildValidationLookup(); - init(lookup); - setAttributes(lookup, element); - parent.addChild(MetadataType.VALIDATION_LOOKUP, lookup); - Element child = element.element(CONTAINER_VALIDATIONLOOKUPTYPE); - if (child != null) { - processValidationLookupType(lookup, child); - } - } - } - - private void processValidationLookupType(MValidationLookup parent, Element container) { - List elements = container.elements(ELEMENT_VALIDATIONLOOKUPTYPE); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MValidationLookupType lookupType = buildValidationLookupType(); - setAttributes(lookupType, element); - parent.addChild(MetadataType.VALIDATION_LOOKUP_TYPE, lookupType); - } - } - - private void processValidationExpression(MResource parent, Element container) { - List elements = container.elements(ELEMENT_VALIDATIONEXPRESSION); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MValidationExpression expression = buildValidationExpression(); - setAttributes(expression, element); - parent.addChild(MetadataType.VALIDATION_EXPRESSION, expression); - } - } - - private void processValidationExternal(MResource parent, Element container) { - List elements = container.elements(ELEMENT_VALIDATIONEXTERNAL); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MValidationExternal external = buildValidationExternal(); - init(external); - setAttributes(external, element); - parent.addChild(MetadataType.VALIDATION_EXTERNAL, external); - Element child = element.element(CONTAINER_VALIDATIONEXTERNALTYPE); - if (child != null) { - processValidationExternalType(external, child); - } - } - } - - private void processValidationExternalType(MValidationExternal parent, Element container) { - List elements = container.elements(ELEMENT_VALIDATIONEXTERNALTYPE); - for (int i = 0; i < elements.size(); i++) { - Element element = (Element) elements.get(i); - MValidationExternalType type = buildValidationExternalType(); - setAttributes(type, element); - parent.addChild(MetadataType.VALIDATION_EXTERNAL_TYPE, type); - } - } - - private void processSearchHelp(MResource parent, Element container) { - List searchhelps = container.elements(ELEMENT_SEARCHHELP); - for (int i = 0; i < searchhelps.size(); i++) { - Element element = (Element) searchhelps.get(i); - MSearchHelp searchhelp = buildSearchHelp(); - setAttributes(searchhelp, element); - parent.addChild(MetadataType.SEARCH_HELP, searchhelp); - } - } - - private void processObject(MResource parent, Element container) { - List objects = container.elements(ELEMENT_OBJECT); - for (int i = 0; i < objects.size(); i++) { - Element element = (Element) objects.get(i); - MObject obj = buildObject(); - setAttributes(obj, element); - parent.addChild(MetadataType.OBJECT, obj); - } - } - - private void processClass(MResource parent, Element container) { - List classes = container.elements(ELEMENT_CLASS); - for (int i = 0; i < classes.size(); i++) { - Element element = (Element) classes.get(i); - MClass clazz = buildClass(); - init(clazz); - setAttributes(clazz, element); - parent.addChild(MetadataType.CLASS, clazz); - Element child; - child = element.element(CONTAINER_TABLE); - if (child != null) { - processTable(clazz, child); - } - child = element.element(CONTAINER_UPDATE); - if (child != null) { - processUpdate(clazz, child); - } - } - } - - private void processTable(MClass parent, Element container) { - List tables = container.elements(ELEMENT_TABLE); - for (int i = 0; i < tables.size(); i++) { - Element element = (Element) tables.get(i); - MTable table = buildTable(); - setAttributes(table, element); - parent.addChild(MetadataType.TABLE, table); - } - } - - private void processUpdate(MClass parent, Element container) { - List updates = container.elements(ELEMENT_UPDATE); - for (int i = 0; i < updates.size(); i++) { - Element element = (Element) updates.get(i); - MUpdate update = buildUpdate(); - init(update); - setAttributes(update, element); - parent.addChild(MetadataType.UPDATE, update); - Element child = element.element(CONTAINER_UPDATE_TYPE); - if (child != null) { - processUpdateType(update, child); - } - } - } - - private void processUpdateType(MUpdate parent, Element container) { - List updateFields = container.elements(ELEMENT_UPDATETYPE); - for (int i = 0; i < updateFields.size(); i++) { - Element element = (Element) updateFields.get(i); - MUpdateType updateType = buildUpdateType(); - parent.addChild(MetadataType.UPDATE_TYPE, updateType); - setAttributes(updateType, element); - } - } - - private void processForeignKey(MSystem system, Element container) { - List fkeys = container.elements("ForeignKey"); - for (int i = 0; i < fkeys.size(); i++) { - Element element = (Element) fkeys.get(i); - MForeignKey foreignKey = buildForeignKey(); - setAttributes(foreignKey, element); - system.addChild(MetadataType.FOREIGNKEYS, foreignKey); - } - } - - public static final String CONTAINER_PREFIX = "METADATA-"; - - public static final String CONTAINER_ROOT = "RETS"; - - public static final String CONTAINER_METADATA = "METADATA"; - - public static final String CONTAINER_SYSTEM = "METADATA-SYSTEM"; - - public static final String CONTAINER_RESOURCE = "METADATA-RESOURCE"; - - public static final String CONTAINER_FOREIGNKEY = "METADATA-FOREIGN_KEYS"; - - public static final String CONTAINER_CLASS = "METADATA-CLASS"; - - public static final String CONTAINER_TABLE = "METADATA-TABLE"; - - public static final String CONTAINER_UPDATE = "METADATA-UPDATE"; - - public static final String CONTAINER_UPDATE_TYPE = "METADATA-UPDATE_TYPE"; - - public static final String CONTAINER_OBJECT = "METADATA-OBJECT"; - - public static final String CONTAINER_SEARCH_HELP = "METADATA-SEARCH_HELP"; - - public static final String CONTAINER_EDITMASK = "METADATA-EDITMASK"; - - public static final String CONTAINER_UPDATEHELP = "METADATA-UPDATE_HELP"; - - public static final String CONTAINER_LOOKUP = "METADATA-LOOKUP"; - - public static final String CONTAINER_LOOKUPTYPE = "METADATA-LOOKUP_TYPE"; - - public static final String CONTAINER_VALIDATIONLOOKUP = "METADATA-VALIDATION_LOOKUP"; - - public static final String CONTAINER_VALIDATIONLOOKUPTYPE = "METADATA-VALIDATION_LOOKUP_TYPE"; - - public static final String CONTAINER_VALIDATIONEXPRESSION = "METADATA-VALIDATION_EXPRESSION"; - - public static final String CONTAINER_VALIDATIONEXTERNAL = "METADATA-VALIDATION_EXTERNAL"; - - public static final String CONTAINER_VALIDATIONEXTERNALTYPE = "METADATA-VALIDATION_EXTERNAL_TYPE"; - - public static final Map sContainer2Type = new HashMap(); - static { - for (int i = 0; i < MetadataType.values().length; i++) { - MetadataType type = MetadataType.values()[i]; - sContainer2Type.put(CONTAINER_PREFIX + type.name(), type); - } - /* you have got to be kidding me. The spec (compact) says - METADATA-FOREIGNKEYS and that's the request type but the DTD says - METADATA-FOREIGN_KEY. - I think I'm going to be sick. - */ - sContainer2Type.remove(CONTAINER_PREFIX + MetadataType.FOREIGNKEYS.name()); - sContainer2Type.put(CONTAINER_FOREIGNKEY, MetadataType.FOREIGNKEYS); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetaCollector.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetaCollector.java deleted file mode 100644 index 18ac1a8..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetaCollector.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -import java.io.Serializable; - -/** Interface for Metadata objects to collect their children. */ -public interface MetaCollector extends Serializable { - /** - * @param path path to the parent object. - */ - public MetaObject[] getMetadata(MetadataType type, String path) throws MetadataException; - - public MetaObject[] getMetadataRecursive(MetadataType type, String path) throws MetadataException; -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetaObject.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetaObject.java deleted file mode 100644 index 6e30a2d..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetaObject.java +++ /dev/null @@ -1,366 +0,0 @@ -package com.ossez.usreio.tests.common.metadata; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -//import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.ossez.usreio.common.util.CaseInsensitiveTreeMap; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import com.ossez.usreio.tests.common.metadata.attrib.AttrAlphanum; -import com.ossez.usreio.tests.common.metadata.attrib.AttrBoolean; -import com.ossez.usreio.tests.common.metadata.attrib.AttrDate; -import com.ossez.usreio.tests.common.metadata.attrib.AttrNumeric; -import com.ossez.usreio.tests.common.metadata.attrib.AttrNumericPositive; -import com.ossez.usreio.tests.common.metadata.attrib.AttrPlaintext; -import com.ossez.usreio.tests.common.metadata.attrib.AttrText; -import com.ossez.usreio.tests.common.metadata.attrib.AttrVersion; - -public abstract class MetaObject implements Serializable { - private static final Log LOG = LogFactory.getLog(MetaObject.class); - - /** a standard parser used by different child types */ - protected static final AttrType sAlphanum = new AttrAlphanum(0, 0); - protected static final AttrType sAlphanum64 = new AttrAlphanum(1, 64); - protected static final AttrType sAlphanum32 = new AttrAlphanum(1, 32); - protected static final AttrType sAlphanum24 = new AttrAlphanum(1, 24); - protected static final AttrType sAlphanum10 = new AttrAlphanum(1, 10); - protected static final AttrType sPlaintext = new AttrPlaintext(0, 0); - protected static final AttrType sPlaintext1024 = new AttrPlaintext(1, 1024); - protected static final AttrType sPlaintext512 = new AttrPlaintext(1, 512); - protected static final AttrType sPlaintext128 = new AttrPlaintext(1, 128); - protected static final AttrType sPlaintext64 = new AttrPlaintext(1, 64); - protected static final AttrType sPlaintext32 = new AttrPlaintext(1, 32); - protected static final AttrType sText = new AttrText(0, 0); - protected static final AttrType sText1024 = new AttrText(1, 1024); - protected static final AttrType sText512 = new AttrText(1, 512); - protected static final AttrType sText256 = new AttrText(1, 256); - protected static final AttrType sText128 = new AttrText(1, 128); - protected static final AttrType sText64 = new AttrText(1, 64); - protected static final AttrType sText32 = new AttrText(1, 32); - protected static final AttrType sAttrBoolean = new AttrBoolean(); - protected static final AttrType sAttrDate = new AttrDate(); - protected static final AttrType sAttrNumeric = new AttrNumeric(); - protected static final AttrType sAttrNumericPositive = new AttrNumericPositive(); - protected static final AttrType sAttrVersion = new AttrVersion(); - protected static final AttrType sAttrMetadataEntryId = sAlphanum32; - protected static final MetadataType[] sNoChildren = new MetadataType[0]; - - protected static final AttrType retsid = sAlphanum32; - protected static final AttrType retsname = sAlphanum64; - - public static final boolean STRICT_PARSING = true; - public static final boolean LOOSE_PARSING = false; - public static final boolean DEFAULT_PARSING = LOOSE_PARSING; - - /** the metdata path to this object */ - protected String path; - /** map of child type to map of child id to child object */ - protected Map childTypes; - /** map of attribute name to attribute object (as parsed by attrtype) */ - protected Map attributes; - /** map of attribute name to AttrType parser */ - protected Map attrTypes; - - - private static Map sAttributeMapCache = new HashMap(); - private MetaCollector mCollector; - private boolean strict; - - public MetaObject(boolean strictParsing) { - this.strict = strictParsing; - if (strictParsing) { - this.attributes = new HashMap(); - } else { - this.attributes = new CaseInsensitiveTreeMap(); - } - this.attrTypes = this.getAttributeMap(strictParsing); - MetadataType[] types = getChildTypes(); - this.childTypes = new HashMap(); - for (int i = 0; i < types.length; i++) { - this.childTypes.put(types[i], null); - } - } - - private Map getAttributeMap(boolean strictParsing) { - synchronized (sAttributeMapCache) { - Map map = sAttributeMapCache.get(new CacheKey(this, strictParsing)); - if (map == null) { - if (strictParsing) { - map = new HashMap(); - } else { - map = new CaseInsensitiveTreeMap(); - } - addAttributesToMap(map); - // Let's make sure no one mucks with the map later - map = Collections.unmodifiableMap(map); - sAttributeMapCache.put(new CacheKey(this, strictParsing), map); - if (LOG.isDebugEnabled()) { - LOG.debug("Adding to attribute cache: " + this.getClass().getName() + ", " + strictParsing); - } - } - return map; - } - } - - public static void clearAttributeMapCache() { - synchronized (sAttributeMapCache) { - sAttributeMapCache.clear(); - } - } - - public Collection getChildren(MetadataType type) { - if (!this.childTypes.containsKey(type)) { - // throw new IllegalArgumentException? - return null; - } - Object o = this.childTypes.get(type); - if (o == null) { - if (!fetchChildren(type)) { - return Collections.EMPTY_SET; - } - o = this.childTypes.get(type); - } - if (o instanceof Map) { - Map m = (Map) o; - return m.values(); - } - return (Collection) o; - } - - private boolean fetchChildren(MetadataType type) { - this.childTypes.put(type, new HashMap()); - try { - MetaObject[] children = null; - if (this.mCollector != null) { - children = this.mCollector.getMetadata(type, getPath()); - } - if (children == null) { - return false; - } - for (int i = 0; i < children.length; i++) { - MetaObject child = children[i]; - addChild(type, child); - } - } catch (MetadataException e) { - LOG.error(toString() + " unable to fetch " + type.name() + " children"); - return false; - } - return true; - } - - public MetaObject getChild(MetadataType type, String id) { - if (id == null) { - return null; - } - try { - if (this.childTypes.get(type) == null && this.mCollector != null) { - if (!fetchChildren(type)) { - return null; - } - } - Map m = (Map) this.childTypes.get(type); - if (m == null) { - return null; - } - return (MetaObject) m.get(id); - } catch (ClassCastException e) { - return null; - } - } - - public Object getAttribute(String key) { - return this.attributes.get(key); - } - - public Set getKnownAttributes() { - return this.attrTypes.keySet(); - } - - public String getAttributeAsString(String key) { - Object value = this.attributes.get(key); - if (value == null) { - return null; - } - if (this.attrTypes.containsKey(key)) { - AttrType type = (AttrType) this.attrTypes.get(key); - return type.render(value); - } - return value.toString(); - } - - protected Object getTypedAttribute(String key, Class type) { - AttrType atype = (AttrType) this.attrTypes.get(key); - if (atype == null) { - return null; - } - if (atype.getType() == type) { - return this.attributes.get(key); - } - LOG.warn("type mismatch, expected " + type.getName() + " but" + " got " + atype.getType().getName()); - return null; - } - - public String getDateAttribute(String key) { - return (String) getTypedAttribute(key, String.class); - } - - public String getStringAttribute(String key) { - return (String) getTypedAttribute(key, String.class); - } - - public int getIntAttribute(String key) { - Integer i = (Integer) getTypedAttribute(key, Integer.class); - if (i == null) { - return 0; - } - return i.intValue(); - } - - public boolean getBooleanAttribute(String key) { - Boolean b = (Boolean) getTypedAttribute(key, Boolean.class); - if (b == null) { - return false; - } - return b.booleanValue(); - } - - public void setAttribute(String key, String value) { - if (value == null) { - // LOG.warning() - return; - } - if (this.attrTypes.containsKey(key)) { - AttrType type = (AttrType) this.attrTypes.get(key); - try { - this.attributes.put(key, type.parse(value,this.strict)); - } catch (MetaParseException e) { - LOG.warn(toString() + " couldn't parse attribute " + key + ", value " + value + ": " + e.getMessage()); - } - } else { - this.attributes.put(key, value); - LOG.warn("Unknown key (" + toString() + "): " + key); - } - } - - public void addChild(MetadataType type, MetaObject child) { - if (this.childTypes.containsKey(type)) { - Object obj = this.childTypes.get(type); - Map map; - if (obj == null) { - map = new HashMap(); - this.childTypes.put(type, map); - } else { - map = (Map) obj; - } - if (child == null) { - return; - } - String id = child.getId(); - - child.setPath(this.getPath()); - child.setCollector(this.mCollector); - if (id != null) { - map.put(id, child); - } - return; - } - } - - public String getId() { - String idAttr = getIdAttr(); - if (idAttr == null) { - /** cheap hack so everything's a damn map */ - return Integer.toString(hashCode()); - } - return getAttributeAsString(idAttr); - } - - public String getPath() { - return this.path; - } - - protected void setPath(String parent) { - if (parent == null || parent.equals("")) { - this.path = getId(); - } else { - this.path = parent + ":" + getId(); - } - } - - @Override - public String toString() { - ToStringBuilder tsb = new ToStringBuilder(this); - Iterator iter = getKnownAttributes().iterator(); - while (iter.hasNext()) { - String key = (String) iter.next(); - tsb.append(key, getAttributeAsString(key)); - } - return tsb.toString(); - } - - public void setCollector(MetaCollector c) { - this.mCollector = c; - Iterator iterator = this.childTypes.keySet().iterator(); - while (iterator.hasNext()) { - MetadataType type = (MetadataType) iterator.next(); - Map map = (Map) this.childTypes.get(type); - if (map == null) { - continue; - } - Collection children = map.values(); - for (Iterator iter = children.iterator(); iter.hasNext();) { - MetaObject object = (MetaObject) iter.next(); - object.setCollector(c); - } - } - } - - public abstract MetadataType[] getChildTypes(); - - protected abstract String getIdAttr(); - - /** - * Adds attributes to an attribute map. This is called by the MetaObject - * constructor to initialize a map of atributes. This map may be cached, - * so this method may not be called for every object construction. - * - * @param attributeMap Map to add attributes to - */ - protected abstract void addAttributesToMap(Map attributeMap); - -} - -class CacheKey { - private Class mClass; - private boolean strictParsing; - - public CacheKey(MetaObject metaObject, boolean strictParsing) { - this.mClass = metaObject.getClass(); - this.strictParsing = strictParsing; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CacheKey)) { - return false; - } - CacheKey rhs = (CacheKey) obj; - return new EqualsBuilder().append(this.mClass, rhs.mClass).append(this.strictParsing, rhs.strictParsing).isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder().append(this.mClass).append(this.strictParsing).toHashCode(); - } - -} - diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetaParseException.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetaParseException.java deleted file mode 100644 index aa85f15..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetaParseException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -public class MetaParseException extends MetadataException { - public MetaParseException() { - super(); - } - - public MetaParseException(String msg) { - super(msg); - } - - public MetaParseException(Throwable cause) { - super(cause); - } - - public MetaParseException(String msg, Throwable cause) { - super(msg, cause); - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/Metadata.java b/src/main/java/com/ossez/usreio/tests/common/metadata/Metadata.java deleted file mode 100644 index 2775002..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/Metadata.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.ossez.usreio.tests.common.metadata; - -import java.io.Serializable; - -import com.ossez.usreio.tests.common.metadata.types.MSystem; -import com.ossez.usreio.tests.common.metadata.types.MResource; -import com.ossez.usreio.tests.common.metadata.types.MForeignKey; -import com.ossez.usreio.tests.common.metadata.types.MClass; -import com.ossez.usreio.tests.common.metadata.types.MTable; -import com.ossez.usreio.tests.common.metadata.types.MUpdate; -import com.ossez.usreio.tests.common.metadata.types.MUpdateType; -import com.ossez.usreio.tests.common.metadata.types.MObject; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternal; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookup; -import com.ossez.usreio.tests.common.metadata.types.MLookup; -import com.ossez.usreio.tests.common.metadata.types.MSearchHelp; - -public class Metadata implements Serializable { - - protected MSystem system; - - public Metadata(MetaCollector collector) throws MetadataException { - MetaObject[] sys = collector.getMetadata(MetadataType.SYSTEM, null); - if (sys != null && sys.length == 1) { - try { - this.system = (MSystem) sys[0]; - } catch (ClassCastException e) { - throw new MetadataException(e); - } - this.system.setCollector(collector); - } - } - - public Metadata(MSystem system) { - this.system = system; - } - - public MSystem getSystem() { - return this.system; - } - - public MResource getResource(String resourceId) { - return this.system.getMResource(resourceId); - } - - public MForeignKey getForeignKey(String foreignKeyId) { - return this.system.getMForeignKey(foreignKeyId); - } - - public MClass getMClass(String resourceId, String className) { - MResource resource = getResource(resourceId); - if (resource == null) { - return null; - } - return resource.getMClass(className); - } - - public MTable getTable(String resourceId, String className, String systemName) { - MClass clazz = getMClass(resourceId, className); - if (clazz == null) { - return null; - } - return clazz.getMTable(systemName); - } - - public MUpdate getUpdate(String resourceId, String className, String updateName) { - MClass clazz = getMClass(resourceId, className); - if (clazz == null) { - return null; - } - return clazz.getMUpdate(updateName); - } - - public MUpdateType getUpdateType(String resourceId, String className, String updateName, String systemName) { - MUpdate update = getUpdate(resourceId, className, updateName); - if (update == null) { - return null; - } - return update.getMUpdateType(systemName); - } - - public MObject getObject(String resourceId, String objectType) { - MResource resource = getResource(resourceId); - if (resource == null) { - return null; - } - return resource.getMObject(objectType); - } - - public MLookup getLookup(String resourceId, String lookupName) { - MResource resource = getResource(resourceId); - if (resource == null) { - return null; - } - return resource.getMLookup(lookupName); - } - - public MSearchHelp getSearchHelp(String resourceId, String searchHelpId) { - MResource resource = getResource(resourceId); - if (resource == null) { - return null; - } - return resource.getMSearchHelp(searchHelpId); - } - - public MValidationExternal getValidationExternal(String resourceId, String validationExternalName) { - MResource resource = getResource(resourceId); - if (resource == null) { - return null; - } - return resource.getMValidationExternal(validationExternalName); - } - - public MValidationLookup getValidationLookup(String resourceId, String validationLookupName) { - MResource resource = getResource(resourceId); - if (resource == null) { - return null; - } - return resource.getMValidationLookup(validationLookupName); - } - - private String getResourceId(MetaObject obj) { - String path = obj.getPath(); - int index = path.indexOf(':'); - if (index == -1) { - return null; - } - String resource = path.substring(0, index); - return resource; - } - - public MResource getResource(MTable field) { - String resource = getResourceId(field); - return getResource(resource); - } - - public MLookup getLookup(MTable field) { - String resource = getResourceId(field); - return getLookup(resource, field.getLookupName()); - } - - public MSearchHelp getSearchHelp(MTable field) { - String searchHelpID = field.getSearchHelpID(); - if (searchHelpID == null) { - return null; - } - String resource = getResourceId(field); - return getSearchHelp(resource, searchHelpID); - } - - public MResource getResource(MClass clazz) { - return getResource(getResourceId(clazz)); - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataBuilder.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataBuilder.java deleted file mode 100644 index 19597cd..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataBuilder.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -import com.ossez.usreio.tests.common.metadata.types.MClass; -import com.ossez.usreio.tests.common.metadata.types.MEditMask; -import com.ossez.usreio.tests.common.metadata.types.MForeignKey; -import com.ossez.usreio.tests.common.metadata.types.MLookup; -import com.ossez.usreio.tests.common.metadata.types.MLookupType; -import com.ossez.usreio.tests.common.metadata.types.MObject; -import com.ossez.usreio.tests.common.metadata.types.MResource; -import com.ossez.usreio.tests.common.metadata.types.MSearchHelp; -import com.ossez.usreio.tests.common.metadata.types.MSystem; -import com.ossez.usreio.tests.common.metadata.types.MTable; -import com.ossez.usreio.tests.common.metadata.types.MUpdate; -import com.ossez.usreio.tests.common.metadata.types.MUpdateHelp; -import com.ossez.usreio.tests.common.metadata.types.MUpdateType; -import com.ossez.usreio.tests.common.metadata.types.MValidationExpression; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternal; -import com.ossez.usreio.tests.common.metadata.types.MValidationExternalType; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookup; -import com.ossez.usreio.tests.common.metadata.types.MValidationLookupType; - -public abstract class MetadataBuilder { - protected MetadataBuilder() { - this.mStrict = false; - } - - public boolean isStrict() { - return this.mStrict; - } - - public void setStrict(boolean strict) { - this.mStrict = strict; - } - - protected Metadata finish(MSystem system) { - return new Metadata(system); - } - - protected static void setAttribute(MetaObject obj, String key, String value) { - obj.setAttribute(key, value); - } - - protected MSystem buildSystem() { - MSystem system = new MSystem(this.mStrict); - return system; - } - - protected MResource buildResource() { - MResource resource = new MResource(this.mStrict); - return resource; - } - - protected MForeignKey buildForeignKey() { - MForeignKey key = new MForeignKey(this.mStrict); - return key; - } - - protected MClass buildClass() { - MClass clazz = new MClass(this.mStrict); - return clazz; - } - - protected MTable buildTable() { - MTable table = new MTable(this.mStrict); - return table; - } - - protected MUpdate buildUpdate() { - MUpdate update = new MUpdate(this.mStrict); - return update; - } - - protected MUpdateType buildUpdateType() { - MUpdateType updatetype = new MUpdateType(this.mStrict); - return updatetype; - } - - protected MObject buildObject() { - MObject obj = new MObject(this.mStrict); - return obj; - } - - protected MSearchHelp buildSearchHelp() { - MSearchHelp help = new MSearchHelp(this.mStrict); - return help; - } - - protected MEditMask buildEditMask() { - MEditMask mask = new MEditMask(this.mStrict); - return mask; - } - - protected MLookup buildLookup() { - MLookup lookup = new MLookup(this.mStrict); - return lookup; - } - - protected MLookupType buildLookupType() { - MLookupType type = new MLookupType(this.mStrict); - return type; - } - - protected MUpdateHelp buildUpdateHelp() { - MUpdateHelp help = new MUpdateHelp(this.mStrict); - return help; - } - - protected MValidationLookup buildValidationLookup() { - MValidationLookup lookup = new MValidationLookup(this.mStrict); - return lookup; - } - - protected MValidationExternalType buildValidationExternalType() { - MValidationExternalType type = new MValidationExternalType(this.mStrict); - return type; - } - - protected MValidationExpression buildValidationExpression() { - MValidationExpression expression = new MValidationExpression(this.mStrict); - return expression; - } - - protected MValidationExternal buildValidationExternal() { - MValidationExternal external = new MValidationExternal(this.mStrict); - return external; - } - - protected MValidationLookupType buildValidationLookupType() { - MValidationLookupType lookupType = new MValidationLookupType(this.mStrict); - return lookupType; - } - - public abstract Metadata doBuild(Object src) throws MetadataException; - - public abstract MetaObject[] parse(Object src) throws MetadataException; - - protected MetaObject newType(MetadataType type) { - if (type == MetadataType.SYSTEM) { - return buildSystem(); - } - if (type == MetadataType.RESOURCE) { - return buildResource(); - } - if (type == MetadataType.FOREIGNKEYS) { - return buildForeignKey(); - } - if (type == MetadataType.CLASS) { - return buildClass(); - } - if (type == MetadataType.TABLE) { - return buildTable(); - } - if (type == MetadataType.UPDATE) { - return buildUpdate(); - } - if (type == MetadataType.UPDATE_TYPE) { - return buildUpdateType(); - } - if (type == MetadataType.OBJECT) { - return buildObject(); - } - if (type == MetadataType.SEARCH_HELP) { - return buildSearchHelp(); - } - if (type == MetadataType.EDITMASK) { - return buildEditMask(); - } - if (type == MetadataType.UPDATE_HELP) { - return buildUpdateHelp(); - } - if (type == MetadataType.LOOKUP) { - return buildLookup(); - } - if (type == MetadataType.LOOKUP_TYPE) { - return buildLookupType(); - } - if (type == MetadataType.VALIDATION_LOOKUP) { - return buildValidationLookup(); - } - if (type == MetadataType.VALIDATION_LOOKUP_TYPE) { - return buildValidationLookupType(); - } - if (type == MetadataType.VALIDATION_EXTERNAL) { - return buildValidationExternal(); - } - if (type == MetadataType.VALIDATION_EXTERNAL_TYPE) { - return buildValidationExternalType(); - } - if (type == MetadataType.VALIDATION_EXPRESSION) { - return buildValidationExpression(); - } - throw new RuntimeException("No metadata type class found for " + type.name()); - } - - private boolean mStrict; -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataElement.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataElement.java deleted file mode 100644 index 3afebc7..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataElement.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.ossez.usreio.tests.common.metadata; - -public enum MetadataElement { - SYSTEM("System"),// might need to provide enumeration for different versions 1.5 vs 1.7 - RESOURCE("Resource"), - FOREIGNKEY("ForeignKey"), - CLASS("Class"), - TABLE("Field"), - UPDATE("UpdateType"), - UPDATETYPE("UpdateField"), - OBJECT("Object"), - SEARCHHELP("SearchHelp"), - EDITMASK("EditMask"), - UPDATEHELP("UpdateHelp"), - LOOKUP("Lookup"), - LOOKUPTYPE("LookupType"), - VALIDATIONLOOKUP("ValidationLookup"), - VALIDATIONLOOKUPTYPE("ValidationLookupType"), - VALIDATIONEXPRESSION("ValidationExpression"), - VALIDATIONEXTERNAL("ValidationExternalType"), - VALIDATIONEXTERNALTYPE("ValidationExternal"); - - private final String elementName; - - MetadataElement(String elementName){ - this.elementName = elementName; - } - - public String elementName(){ return this.elementName;} - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataException.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataException.java deleted file mode 100644 index 0ebb29d..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - - -public class MetadataException extends Exception { - public MetadataException() { - super(); - } - - public MetadataException(String msg) { - super(msg); - } - - public MetadataException(Throwable cause) { - super(cause); - } - - public MetadataException(String msg, Throwable cause) { - super(msg, cause); - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataType.java b/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataType.java deleted file mode 100644 index 61059ec..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/MetadataType.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata; - -public enum MetadataType { - EDITMASK, - FOREIGNKEYS, - RESOURCE, - LOOKUP, - LOOKUP_TYPE, - OBJECT, - SEARCH_HELP, - SYSTEM, - TABLE, - UPDATE, - UPDATE_HELP, - UPDATE_TYPE, - VALIDATION_EXPRESSION, - VALIDATION_EXTERNAL, - VALIDATION_EXTERNAL_TYPE, - VALIDATION_LOOKUP, - VALIDATION_LOOKUP_TYPE, - CLASS; - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAbstractText.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAbstractText.java deleted file mode 100644 index 139fac5..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAbstractText.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public abstract class AttrAbstractText implements AttrType { - protected int min; - protected int max; - - public AttrAbstractText(int min, int max) { - this.min = min; - this.max = max; - } - - - public String parse(String value, boolean strict) throws MetaParseException { - if( !strict ) - return value; - int l = value.length(); - if (this.min != 0 && l < this.min) { - throw new MetaParseException("Value too short (min " + this.min + "): " + l); - } - if (this.max != 0 && l > this.max) { - throw new MetaParseException("Value too long (max " + this.max + "): " + l); - } - checkContent(value); - return value; - } - - - public Class getType() { - return String.class; - } - - - public String render(String value) { - return value; - } - - protected abstract void checkContent(String value) throws MetaParseException; - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAlphanum.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAlphanum.java deleted file mode 100644 index 54d07a2..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrAlphanum.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrAlphanum extends AttrAbstractText { - - public AttrAlphanum(int min, int max) { - super(min, max); - } - - @Override - protected void checkContent(String value) throws MetaParseException { - char[] chars = value.toCharArray(); - for (int i = 0; i < chars.length; i++) { - char c = chars[i]; - if (!Character.isLetterOrDigit(c)) { - // illegal but exist in CRT metadata - if ("_- ".indexOf(c) == -1) { - throw new MetaParseException("Invalid Alphanum character at position " + i + ": " + c); - } - } - } - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrBoolean.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrBoolean.java deleted file mode 100644 index ac3a3b8..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrBoolean.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrBoolean implements AttrType { - public Boolean parse(String value, boolean strict) throws MetaParseException { - if (value.equals("1")) { - return Boolean.TRUE; - } - if (value.equals("0")) { - return Boolean.FALSE; - } - - if (value.equalsIgnoreCase("true")) { - return Boolean.TRUE; - } - if (value.equalsIgnoreCase("false")) { - return Boolean.FALSE; - } - - if (value.equalsIgnoreCase("Y")) { - return Boolean.TRUE; - } - if (value.equalsIgnoreCase("N")) { - return Boolean.FALSE; - } - - if (value.equals("")) { - return Boolean.FALSE; - } - - if( strict ) - throw new MetaParseException("Invalid boolean value: " + value); - return false; - } - - public String render(Boolean value) { - if( value.booleanValue() ) return "1"; - - return "0"; - } - - public Class getType() { - return Boolean.class; - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrDate.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrDate.java deleted file mode 100644 index 425802f..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrDate.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - * - * - * Vangulo Changed: - * gives ability to handle dates in this format - * 2011-06-01T18:06:58 - * should find a more elegant way - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -//import java.text.DateFormat; -//import java.text.ParseException; -//import java.text.SimpleDateFormat; -//import java.util.Date; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -/** - * Converted this class to return a String instead of a - * Date object which allows for more flexiblity since - * Many Rets Servers format their dates differently - * - * @author vangulo - * - */ -public class AttrDate implements AttrType { - - // need date attribute to be flexible since different MLS's have - // different formats for dates - public String parse(String value, boolean strict) throws MetaParseException { - return value; -// Date d; -// try { -// d = this.df.parse(value); -// } catch (ParseException e) { -// if( strict ) -// throw new MetaParseException(e); -// try { -// value = value.replaceAll("[A-Za-z]", " "); -// d = this.df1.parse(value); -// } catch (ParseException e1) { -// //e1.printStackTrace(); -// return value; -// } -// return d; -// } -// return d; - } - - public String render(String value) { - return value; - //Date date = value; - //return this.df.format(date); - } - - public Class getType() { - return String.class; - } - - //private DateFormat df = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss z"); - //2011-06-01T18:06:58 - //private DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - //Tuesday, 22-Dec-2009 21:03:18 GMT - //private DateFormat df2 = new SimpleDateFormat("E, dd-MMM-yyyy HH:mm:ss z"); -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrEnum.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrEnum.java deleted file mode 100644 index 2464d57..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import java.util.HashMap; -import java.util.Map; -import java.util.Collections; - -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrEnum extends AttrAbstractText { - public AttrEnum(String[] values) { - super(0, 0); - this.map = new HashMap(); - for (String value : values) this.map.put(value, value); - this.map = Collections.unmodifiableMap(this.map); - } - - @Override - protected void checkContent(String value) throws MetaParseException { - if( !this.map.containsKey(value) ) - throw new MetaParseException("Invalid key: " + value); - } - - private Map map; -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrGenericText.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrGenericText.java deleted file mode 100644 index 4c411f2..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrGenericText.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrGenericText extends AttrAbstractText { - private String mChars; - - public AttrGenericText(int min, int max, String chars) { - super(min, max); - this.mChars = chars; - } - - @Override - protected void checkContent(String value) throws MetaParseException { - char[] chars = value.toCharArray(); - for (int i = 0; i < chars.length; i++) { - char c = chars[i]; - if (this.mChars.indexOf(c) == -1) { - throw new MetaParseException("Invalid char (" + c + ") at position " + i); - } - } - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumeric.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumeric.java deleted file mode 100644 index 91d8e48..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumeric.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrNumeric implements AttrType { - public Integer parse(String value, boolean strict) throws MetaParseException { - try { - return new Integer(value); - } catch (NumberFormatException e) { - if( strict ) - throw new MetaParseException(e); - return 0; - } - } - - public String render(Integer value) { - return value.toString(); - } - - public Class getType() { - return Integer.class; - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumericPositive.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumericPositive.java deleted file mode 100644 index c3126ba..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrNumericPositive.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrNumericPositive implements AttrType { - - public Integer parse(String value, boolean strict) throws MetaParseException { - try { - Integer integer = new Integer(value); - if (strict && integer < 1) throw new IllegalArgumentException(String.format("%s is not positive", value)); - return integer; - } catch (Exception e) { - if( strict ) - throw new MetaParseException(e); - return 1; - } - } - - - public String render(Integer value) { - return value.toString(); - } - - - public Class getType() { - return Integer.class; - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrPlaintext.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrPlaintext.java deleted file mode 100644 index 2a707dc..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrPlaintext.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrPlaintext extends AttrAbstractText { - public AttrPlaintext(int min, int max) { - super(min, max); - } - - @Override - protected void checkContent(String value) throws MetaParseException { - char[] chars = value.toCharArray(); - for (int i = 0; i < chars.length; i++) { - char c = chars[i]; - if (c < 31 || c > 126) { - throw new MetaParseException("Invalid character (ordinal " + (int) c + ") at position " + i); - } - } - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrText.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrText.java deleted file mode 100644 index 20772a4..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrText.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -public class AttrText extends AttrAbstractText { - public AttrText(int min, int max) { - super(min, max); - } - - @Override - protected void checkContent(String value) throws MetaParseException { - char[] chars = value.toCharArray(); - for (int i = 0; i < chars.length; i++) { - char c = chars[i]; - if (!(c == '\n' || c == '\r' || c == ' ' || c == '\t' || (c > 31 && c < 127))) { - throw new MetaParseException("Invalid character (ordinal " + (int) c + ") at position " + i); - } - } - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrVersion.java b/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrVersion.java deleted file mode 100644 index 2ae194b..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/attrib/AttrVersion.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.common.metadata.attrib; - -import org.apache.commons.lang3.StringUtils; -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaParseException; - -/** - * A version is a string formatted "major.minor.release". This gets converted - * to an integer such as major * 10,000,000 + minor * 100,000 + release. - */ -public class AttrVersion implements AttrType { - - public Integer parse(String value, boolean strict) throws MetaParseException { - String[] parts = StringUtils.split(value, "."); - int major, minor, release; - if (strict && parts != null && parts.length != 3) { - throw new MetaParseException("Invalid version: " + value + ", " + parts.length + " parts"); - } - try { - major = Integer.parseInt(this.getPart(parts,0)); - minor = Integer.parseInt(this.getPart(parts,1)); - release = Integer.parseInt(this.getPart(parts,2)); - } catch (NumberFormatException e) { - throw new MetaParseException("Invalid version: " + value, e); - } - if ((major < 100) && (major >= 0) && (minor < 100) && (minor >= 0) && (release < 100000) && (release >= 0)) { - return new Integer(major * 10000000 + minor * 100000 + release); - } - if( strict ) - throw new MetaParseException("Invalid version: " + value); - return 0; - } - private String getPart(String[] parts, int part){ - if( parts != null && parts.length > part ) return parts[part]; - return "0"; - } - - - public String render(Integer value) { - int ver = value.intValue(); - int release = ver % 100000; - int minor = (ver / 100000) % 100; - int major = (ver / 10000000); - String minstr = Integer.toString(minor); - String relstr = Integer.toString(release); - while (minstr.length() < 2) { - minstr = "0" + minstr; - } - while (relstr.length() < 5) { - relstr = "0" + relstr; - } - return major + "." + minstr + "." + relstr; - } - - - public Class getType() { - return Integer.class; - } -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MClass.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MClass.java deleted file mode 100644 index 665185a..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MClass.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Collection; -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MClass extends MetaObject { - public static final String CLASSNAME = "ClassName"; - public static final String VISIBLENAME = "VisibleName"; - public static final String STANDARDNAME = "StandardName"; - public static final String DESCRIPTION = "Description"; - public static final String TABLEVERSION = "TableVersion"; - public static final String TABLEDATE = "TableDate"; - public static final String UPDATEVERSION = "UpdateVersion"; - public static final String UPDATEDATE = "UpdateDate"; - public static final String DELETEDFLAGFIELD = "DeletedFlagField"; - public static final String DELETEDFLAGVALUE = "DeletedFlagValue"; - public static final String CLASSTIMESTAMP = "ClassTimeStamp"; - public static final String HASHKEYINDEX = "HasKeyIndex"; - private static MetadataType[] sTypes = { MetadataType.UPDATE, MetadataType.TABLE }; - - public MClass() { - this(DEFAULT_PARSING); - } - - public MClass(boolean strictParsing) { - super(strictParsing); - } - - @Override - public MetadataType[] getChildTypes() { - return sTypes; - } - - public String getClassName() { - return getStringAttribute(CLASSNAME); - } - - public String getVisibleName() { - return getStringAttribute(VISIBLENAME); - } - - public String getStandardName() { - return getStringAttribute(STANDARDNAME); - } - - public String getDescription() { - return getStringAttribute(DESCRIPTION); - } - - public int getTableVersion() { - return getIntAttribute(TABLEVERSION); - } - - public String getTableDate() { - return getDateAttribute(TABLEDATE); - } - - public int getUpdateVersion() { - return getIntAttribute(UPDATEVERSION); - } - - public String getUpdateDate() { - return getDateAttribute(UPDATEDATE); - } - - public MUpdate getMUpdate(String updateName) { - return (MUpdate) getChild(MetadataType.UPDATE, updateName); - } - - public MUpdate[] getMUpdates() { - MUpdate[] tmpl = new MUpdate[0]; - return (MUpdate[]) getChildren(MetadataType.UPDATE).toArray(tmpl); - } - - public MTable getMTable(String systemName) { - return (MTable) getChild(MetadataType.TABLE, systemName); - } - - public MTable[] getMTables() { - Collection children = getChildren(MetadataType.TABLE); - return (MTable[]) children.toArray(new MTable[0]); - } - - @Override - protected String getIdAttr() { - return CLASSNAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(CLASSNAME, sAlphanum32); - attributeMap.put(VISIBLENAME, sPlaintext32); - attributeMap.put(STANDARDNAME, sAlphanum32); - attributeMap.put(DESCRIPTION, sPlaintext128); - attributeMap.put(TABLEVERSION, sAttrVersion); - attributeMap.put(TABLEDATE, sAttrDate); - attributeMap.put(UPDATEVERSION, sAttrVersion); - attributeMap.put(UPDATEDATE, sAttrDate); - attributeMap.put(DELETEDFLAGFIELD, retsname); - attributeMap.put(DELETEDFLAGVALUE, sAlphanum32); - attributeMap.put(CLASSTIMESTAMP, retsname); - attributeMap.put(HASHKEYINDEX, sAttrBoolean); - } - - - public String getDeletedFlagField() { - return getStringAttribute(DELETEDFLAGFIELD); - } - - public String getDeletedFlagValue() { - return getStringAttribute(DELETEDFLAGVALUE); - } - - public String getClassTimeStamp() { - return getStringAttribute(CLASSTIMESTAMP); - } - - public String getHashKeyIndex() { - return getStringAttribute(HASHKEYINDEX); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MEditMask.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MEditMask.java deleted file mode 100644 index 7ccca7e..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MEditMask.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MEditMask extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String EDITMASKID = "EditMaskID"; - public static final String VALUE = "Value"; - - public MEditMask() { - this(DEFAULT_PARSING); - } - - public MEditMask(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getEditMaskID() { - return getStringAttribute(EDITMASKID); - } - - public String getValue() { - return getStringAttribute(VALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return EDITMASKID; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(EDITMASKID, sAlphanum32); - attributeMap.put(VALUE, sText256); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MForeignKey.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MForeignKey.java deleted file mode 100644 index 7ae1de7..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MForeignKey.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MForeignKey extends MetaObject { - public static final String FOREIGNKEYID = "ForeignKeyID"; - public static final String PARENTRESOURCEID = "ParentResourceID"; - public static final String PARENTCLASSID = "ParentClassID"; - public static final String PARENTSYSTEMNAME = "ParentSystemName"; - public static final String CHILDRESOURCEID = "ChildResourceID"; - public static final String CHILDCLASSID = "ChildClassID"; - public static final String CHILDSYSTEMNAME = "ChildSystemName"; - public static final String CONDITIONALPARENTFIELD = "ConditionalParentField"; - public static final String CONDITIONALPARENTVALUE = "ConditionalParentValue"; - - - public MForeignKey() { - this(DEFAULT_PARSING); - } - - public MForeignKey(boolean strictParsing) { - super(strictParsing); - } - - public String getForeignKeyID() { - return getStringAttribute(FOREIGNKEYID); - } - - public String getParentResourceID() { - return getStringAttribute(PARENTRESOURCEID); - } - - public String getParentClassID() { - return getStringAttribute(PARENTCLASSID); - } - - public String getParentSystemName() { - return getStringAttribute(PARENTSYSTEMNAME); - } - - public String getChildResourceID() { - return getStringAttribute(CHILDRESOURCEID); - } - - public String getChildClassID() { - return getStringAttribute(CHILDCLASSID); - } - - public String getChildSystemName() { - return getStringAttribute(CHILDSYSTEMNAME); - } - - public String getConditionalParentField() { - return getStringAttribute(CONDITIONALPARENTFIELD); - } - - public String getConditionalParentValue() { - return getStringAttribute(CONDITIONALPARENTVALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return FOREIGNKEYID; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(FOREIGNKEYID, sAlphanum32); - attributeMap.put(PARENTRESOURCEID, sAlphanum32); - attributeMap.put(PARENTCLASSID, sAlphanum32); - attributeMap.put(PARENTSYSTEMNAME, sAlphanum32); - attributeMap.put(CHILDRESOURCEID, sAlphanum32); - attributeMap.put(CHILDCLASSID, sAlphanum32); - attributeMap.put(CHILDSYSTEMNAME, sAlphanum32); - attributeMap.put(CONDITIONALPARENTFIELD, retsname); - attributeMap.put(CONDITIONALPARENTVALUE, retsname); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookup.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookup.java deleted file mode 100644 index 144c5ed..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookup.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MLookup extends MetaObject { - private static final MetadataType[] CHILDREN = { MetadataType.LOOKUP_TYPE }; - private static final MLookupType[] EMPTYLOOKUPTYPES = {}; - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String LOOKUPNAME = "LookupName"; - public static final String VISIBLENAME = "VisibleName"; - public static final String VERSION = "Version"; - public static final String DATE = "Date"; - public static final String LOOKUPTYPEVERSION = "LookupTypeVersion"; - public static final String LOOKUPTYPEDATE = "LookupTypeDate"; - - public MLookup() { - this(DEFAULT_PARSING); - } - - public MLookup(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getLookupName() { - return getStringAttribute(LOOKUPNAME); - } - - public String getVisibleName() { - return getStringAttribute(VISIBLENAME); - } - - public int getVersion() { - - int ver = getIntAttribute(VERSION); - if (ver == 0){ - ver = getIntAttribute(LOOKUPTYPEVERSION); - } - return ver; - } - - public String getDate() { - String date = getDateAttribute(DATE); - if (date == null) { - date = getDateAttribute(LOOKUPTYPEDATE); - } - return date; - } - - public MLookupType getMLookupType(String value) { - return (MLookupType) getChild(MetadataType.LOOKUP_TYPE, value); - } - - public MLookupType[] getMLookupTypes() { - return (MLookupType[]) getChildren(MetadataType.LOOKUP_TYPE).toArray(EMPTYLOOKUPTYPES); - } - - @Override - public MetadataType[] getChildTypes() { - return CHILDREN; - } - - @Override - protected String getIdAttr() { - return LOOKUPNAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(LOOKUPNAME, sAlphanum32); - attributeMap.put(VISIBLENAME, sPlaintext32); - attributeMap.put(VERSION, sAttrVersion); - attributeMap.put(DATE, sAttrDate); - attributeMap.put(LOOKUPTYPEVERSION, sAttrVersion); - attributeMap.put(LOOKUPTYPEDATE, sAttrDate); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookupType.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookupType.java deleted file mode 100644 index f1917f7..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MLookupType.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MLookupType extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String LONGVALUE = "LongValue"; - public static final String SHORTVALUE = "ShortValue"; - public static final String VALUE = "Value"; - - public MLookupType() { - this(DEFAULT_PARSING); - } - - public MLookupType(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getLongValue() { - return getStringAttribute(LONGVALUE); - } - - public String getShortValue() { - return getStringAttribute(SHORTVALUE); - } - - public String getValue() { - return getStringAttribute(VALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return VALUE; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(LONGVALUE, sText128); - attributeMap.put(SHORTVALUE, sText32); - attributeMap.put(VALUE, sAlphanum32); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MObject.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MObject.java deleted file mode 100644 index 53a9e47..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MObject.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MObject extends MetaObject { - - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String OBJECTTYPE = "ObjectType"; - public static final String MIMETYPE = "MimeType"; - public static final String VISIBLENAME = "VisibleName"; - public static final String DESCRIPTION = "Description"; - public static final String OBJECTTIMESTAMPNAME = "ObjectTimeStamp"; - public static final String OBJECTCOUNT = "ObjectCount"; - public static final String STANDARDNAME = "StandardName"; - public MObject() { - this(DEFAULT_PARSING); - } - - public MObject(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getObjectType() { - return getStringAttribute(OBJECTTYPE); - } - - public String getMIMEType() { - return getStringAttribute(MIMETYPE); - } - - public String getVisibleName() { - return getStringAttribute(VISIBLENAME); - } - - public String getDescription() { - return getStringAttribute(DESCRIPTION); - } - public String getStandardName() { - return getStringAttribute(STANDARDNAME); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return OBJECTTYPE; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAlphanum24); - attributeMap.put(OBJECTTYPE, sAlphanum24); - attributeMap.put(MIMETYPE, sText64); - attributeMap.put(VISIBLENAME, sPlaintext64); - attributeMap.put(DESCRIPTION, sPlaintext128); - attributeMap.put(OBJECTTIMESTAMPNAME, retsname); - attributeMap.put(OBJECTCOUNT, retsname); - attributeMap.put(STANDARDNAME, retsname); - } - - public String getObjectTimeStampName() { - return getStringAttribute(OBJECTTIMESTAMPNAME); - } - - public String getObjectCount() { - return getStringAttribute(OBJECTCOUNT); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MResource.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MResource.java deleted file mode 100644 index a59a13e..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MResource.java +++ /dev/null @@ -1,270 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MResource extends MetaObject { - private static final MetadataType[] CHILDREN = { - MetadataType.VALIDATION_EXPRESSION, - MetadataType.LOOKUP, - MetadataType.CLASS, - MetadataType.OBJECT, - MetadataType.VALIDATION_EXTERNAL, - MetadataType.VALIDATION_LOOKUP, - MetadataType.EDITMASK, - MetadataType.UPDATE_HELP, - MetadataType.SEARCH_HELP - }; - - public static final String RESOURCEID = "ResourceID"; - public static final String STANDARDNAME = "StandardName"; - public static final String VISIBLENAME = "VisibleName"; - public static final String DESCRIPTION = "Description"; - public static final String KEYFIELD = "KeyField"; - public static final String CLASSCOUNT = "ClassCount"; - public static final String CLASSVERSION = "ClassVersion"; - public static final String CLASSDATE = "ClassDate"; - public static final String OBJECTVERSION = "ObjectVersion"; - public static final String OBJECTDATE = "ObjectDate"; - public static final String SEARCHHELPVERSION = "SearchHelpVersion"; - public static final String SEARCHHELPDATE = "SearchHelpDate"; - public static final String EDITMASKVERSION = "EditMaskVersion"; - public static final String EDITMASKDATE = "EditMaskDate"; - public static final String LOOKUPVERSION = "LookupVersion"; - public static final String LOOKUPDATE = "LookupDate"; - public static final String UPDATEHELPVERSION = "UpdateHelpVersion"; - public static final String UPDATEHELPDATE = "UpdateHelpDate"; - public static final String VALIDATIONEXPRESSIONVERSION = "ValidationExpressionVersion"; - public static final String VALIDATIONEXPRESSIONDATE = "ValidationExpressionDate"; - public static final String VALIDATIONLOOKUPVERSION = "ValidationLookupVersion"; - public static final String VALIDATIONLOOKUPDATE = "ValidationLookupDate"; - public static final String VALIDATIONEXTERNALVERSION = "ValidationExternalVersion"; - public static final String VALIDATIONEXTERNALDATE = "ValidationExternalDate"; - - public MResource() { - this(DEFAULT_PARSING); - } - - public MResource(boolean strictParsing) { - super(strictParsing); - } - - public String getResourceID() { - return getStringAttribute(RESOURCEID); - } - - public String getStandardName() { - return getStringAttribute(STANDARDNAME); - } - - public String getVisibleName() { - return getStringAttribute(VISIBLENAME); - } - - public String getDescription() { - return getStringAttribute(DESCRIPTION); - } - - public String getKeyField() { - return getStringAttribute(KEYFIELD); - } - - public int getClassCount() { - return getIntAttribute(CLASSCOUNT); - } - - public int getClassVersion() { - return getIntAttribute(CLASSVERSION); - } - - public String getClassDate() { - return getDateAttribute(CLASSDATE); - } - - public int getObjectVersion() { - return getIntAttribute(OBJECTVERSION); - } - - public String getObjectDate() { - return getDateAttribute(OBJECTDATE); - } - - public int getSearchHelpVersion() { - return getIntAttribute(SEARCHHELPVERSION); - } - - public String getSearchHelpDate() { - return getDateAttribute(SEARCHHELPDATE); - } - - public int getEditMaskVersion() { - return getIntAttribute(EDITMASKVERSION); - } - - public String getEditMaskDate() { - return getDateAttribute(EDITMASKDATE); - } - - public int getLookupVersion() { - return getIntAttribute(LOOKUPVERSION); - } - - public String getLookupDate() { - return getDateAttribute(LOOKUPDATE); - } - - public int getUpdateHelpVersion() { - return getIntAttribute(UPDATEHELPVERSION); - } - - public String getUpdateHelpDate() { - return getDateAttribute(UPDATEHELPDATE); - } - - public int getValidationExpressionVersion() { - return getIntAttribute(VALIDATIONEXPRESSIONVERSION); - } - - public String getValidationExpressionDate() { - return getDateAttribute(VALIDATIONEXPRESSIONDATE); - } - - public int getValidationLookupVersion() { - return getIntAttribute(VALIDATIONLOOKUPVERSION); - } - - public String getValidationLookupDate() { - return getDateAttribute(VALIDATIONLOOKUPDATE); - } - - public int getValidationExternalVersion() { - return getIntAttribute(VALIDATIONEXTERNALVERSION); - } - - public String getValidationExternalDate() { - return getDateAttribute(VALIDATIONEXTERNALDATE); - } - - public MValidationExpression getMValidationExpression(String validationExpressionID) { - return (MValidationExpression) getChild(MetadataType.VALIDATION_EXPRESSION, validationExpressionID); - } - - public MValidationExpression[] getMValidationExpressions() { - MValidationExpression[] tmpl = new MValidationExpression[0]; - return (MValidationExpression[]) getChildren(MetadataType.VALIDATION_EXPRESSION).toArray(tmpl); - } - - public MLookup getMLookup(String lookupName) { - return (MLookup) getChild(MetadataType.LOOKUP, lookupName); - } - - public MLookup[] getMLookups() { - MLookup[] tmpl = new MLookup[0]; - return (MLookup[]) getChildren(MetadataType.LOOKUP).toArray(tmpl); - } - - public MClass getMClass(String className) { - return (MClass) getChild(MetadataType.CLASS, className); - } - - public MClass[] getMClasses() { - MClass[] tmpl = new MClass[0]; - return (MClass[]) getChildren(MetadataType.CLASS).toArray(tmpl); - } - - public MObject getMObject(String objectType) { - return (MObject) getChild(MetadataType.OBJECT, objectType); - } - - public MObject[] getMObjects() { - MObject[] tmpl = new MObject[0]; - return (MObject[]) getChildren(MetadataType.OBJECT).toArray(tmpl); - } - - public MValidationExternal getMValidationExternal(String validationExternalName) { - return (MValidationExternal) getChild(MetadataType.VALIDATION_EXTERNAL, validationExternalName); - } - - public MValidationExternal[] getMValidationExternal() { - MValidationExternal[] tmpl = new MValidationExternal[0]; - return (MValidationExternal[]) getChildren(MetadataType.VALIDATION_EXTERNAL).toArray(tmpl); - } - - public MValidationLookup getMValidationLookup(String validationLookupName) { - return (MValidationLookup) getChild(MetadataType.VALIDATION_LOOKUP, validationLookupName); - } - - public MValidationLookup[] getMValidationLookups() { - MValidationLookup[] tmpl = new MValidationLookup[0]; - return (MValidationLookup[]) getChildren(MetadataType.VALIDATION_LOOKUP).toArray(tmpl); - } - - public MEditMask getMEditMask(String editMaskID) { - return (MEditMask) getChild(MetadataType.EDITMASK, editMaskID); - } - - public MEditMask[] getMEditMasks() { - MEditMask[] tmpl = new MEditMask[0]; - return (MEditMask[]) getChildren(MetadataType.EDITMASK).toArray(tmpl); - } - - public MUpdateHelp getMUpdateHelp(String updateHelpID) { - return (MUpdateHelp) getChild(MetadataType.UPDATE_HELP, updateHelpID); - } - - public MUpdateHelp[] getMUpdateHelps() { - MUpdateHelp[] tmpl = new MUpdateHelp[0]; - return (MUpdateHelp[]) getChildren(MetadataType.UPDATE_HELP).toArray(tmpl); - } - - public MSearchHelp getMSearchHelp(String searchHelpID) { - return (MSearchHelp) getChild(MetadataType.SEARCH_HELP, searchHelpID); - } - - public MSearchHelp[] getMSearchHelps() { - MSearchHelp[] tmpl = new MSearchHelp[0]; - return (MSearchHelp[]) getChildren(MetadataType.SEARCH_HELP).toArray(tmpl); - } - - @Override - public MetadataType[] getChildTypes() { - return CHILDREN; - } - - @Override - protected String getIdAttr() { - return RESOURCEID; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(RESOURCEID, sAlphanum32); - attributeMap.put(STANDARDNAME, sAlphanum32); - attributeMap.put(VISIBLENAME, sPlaintext32); - attributeMap.put(DESCRIPTION, sPlaintext64); - attributeMap.put(KEYFIELD, sAlphanum32); - attributeMap.put(CLASSCOUNT, sAttrNumeric); - attributeMap.put(CLASSVERSION, sAttrVersion); - attributeMap.put(CLASSDATE, sAttrDate); - attributeMap.put(OBJECTVERSION, sAttrVersion); - attributeMap.put(OBJECTDATE, sAttrDate); - attributeMap.put(SEARCHHELPVERSION, sAttrVersion); - attributeMap.put(SEARCHHELPDATE, sAttrDate); - attributeMap.put(EDITMASKVERSION, sAttrVersion); - attributeMap.put(EDITMASKDATE, sAttrDate); - attributeMap.put(LOOKUPVERSION, sAttrVersion); - attributeMap.put(LOOKUPDATE, sAttrDate); - attributeMap.put(UPDATEHELPVERSION, sAttrVersion); - attributeMap.put(UPDATEHELPDATE, sAttrDate); - attributeMap.put(VALIDATIONEXPRESSIONVERSION, sAttrVersion); - attributeMap.put(VALIDATIONEXPRESSIONDATE, sAttrDate); - attributeMap.put(VALIDATIONLOOKUPVERSION, sAttrVersion); - attributeMap.put(VALIDATIONLOOKUPDATE, sAttrDate); - attributeMap.put(VALIDATIONEXTERNALVERSION, sAttrVersion); - attributeMap.put(VALIDATIONEXTERNALDATE, sAttrDate); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MSearchHelp.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MSearchHelp.java deleted file mode 100644 index 3b04d32..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MSearchHelp.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MSearchHelp extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String SEARCHHELPID = "SearchHelpID"; - public static final String VALUE = "Value"; - - public MSearchHelp() { - this(DEFAULT_PARSING); - } - - public MSearchHelp(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getSearchHelpID() { - return getStringAttribute(SEARCHHELPID); - } - - public String getValue() { - return getStringAttribute(VALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return SEARCHHELPID; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(SEARCHHELPID, sAlphanum32); - attributeMap.put(VALUE, sText1024); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MSystem.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MSystem.java deleted file mode 100644 index 2ebf5bd..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MSystem.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MSystem extends MetaObject { - public static final String SYSTEMID = "SystemID"; - public static final String SYSTEMDESCRIPTION = "SystemDescription"; - public static final String COMMENTS = "Comments"; - public static final String DATE = "Date"; - public static final String VERSION = "Version"; - public static final String TIMEZONEOFFSET = "TimeZoneOffset"; - - public MSystem() { - this(DEFAULT_PARSING); - } - - public MSystem(boolean strictParsing) { - super(strictParsing); - } - - public String getSystemID() { - return getStringAttribute(SYSTEMID); - } - - public String getComment() { - return getStringAttribute(COMMENTS); - } - - public String getSystemDescription() { - return getStringAttribute(SYSTEMDESCRIPTION); - } - - public String getDate() { - return getDateAttribute(DATE); - } - - public String getTimeZoneOffset() { - return getDateAttribute(TIMEZONEOFFSET); - } - - public int getVersion() { - return getIntAttribute(VERSION); - } - - public MResource getMResource(String resourceID) { - return (MResource) getChild(MetadataType.RESOURCE, resourceID); - } - - public MResource[] getMResources() { - MResource[] tmpl = new MResource[0]; - return (MResource[]) getChildren(MetadataType.RESOURCE).toArray(tmpl); - } - - public MForeignKey getMForeignKey(String foreignKeyID) { - return (MForeignKey) getChild(MetadataType.FOREIGNKEYS, foreignKeyID); - } - - public MForeignKey[] getMForeignKeys() { - MForeignKey[] tmpl = new MForeignKey[0]; - return (MForeignKey[]) getChildren(MetadataType.FOREIGNKEYS).toArray(tmpl); - } - - @Override - public MetadataType[] getChildTypes() { - return CHILDREN; - } - - @Override - protected String getIdAttr() { - return null; - } - - public static final MetadataType[] CHILDREN = { MetadataType.RESOURCE, MetadataType.FOREIGNKEYS }; - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(SYSTEMID, sAlphanum10); - attributeMap.put(SYSTEMDESCRIPTION, sPlaintext64); - attributeMap.put(DATE, sAttrDate); - attributeMap.put(VERSION, sAttrVersion); - attributeMap.put(COMMENTS, sText); - attributeMap.put(TIMEZONEOFFSET, sAttrDate); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MTable.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MTable.java deleted file mode 100644 index 9b6f8ce..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MTable.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; -import com.ossez.usreio.tests.common.metadata.attrib.AttrEnum; - -public class MTable extends MetaObject { - - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String SYSTEMNAME = "SystemName"; - public static final String STANDARDNAME = "StandardName"; - public static final String LONGNAME = "LongName"; - public static final String DBNAME = "DBName"; - public static final String SHORTNAME = "ShortName"; - public static final String MAXIMUMLENGTH = "MaximumLength"; - public static final String DATATYPE = "DataType"; - public static final String PRECISION = "Precision"; - public static final String SEARCHABLE = "Searchable"; - public static final String INTERPRETATION = "Interpretation"; - public static final String ALIGNMENT = "Alignment"; - public static final String USESEPARATOR = "UseSeparator"; - public static final String EDITMASKID = "EditMaskID"; - public static final String LOOKUPNAME = "LookupName"; - public static final String MAXSELECT = "MaxSelect"; - public static final String UNITS = "Units"; - public static final String INDEX = "Index"; - public static final String MINIMUM = "Minimum"; - public static final String MAXIMUM = "Maximum"; - public static final String DEFAULT = "Default"; - public static final String REQUIRED = "Required"; - public static final String SEARCHHELPID = "SearchHelpID"; - public static final String UNIQUE = "Unique"; - public static final String MODTIMESTAMP = "ModTimeStamp"; - public static final String MODTIMESTAMPNAME = "ModTimeStampName"; - public static final String FOREIGNKEYNAME = "ForeignKeyName"; - public static final String FOREIGNFIELD = "ForeignField"; - public static final String INKEYINDEX = "InKeyIndex"; - public static final String KEYQUERY = "KeyQuery"; - public static final String KEYSELECT = "KeySelect"; - - private static final String[] DATATYPES = "Boolean,Character,Date,DateTime,Time,Tiny,Small,Int,Long,Decimal".split(","); - private static final AttrType sDataTypes = new AttrEnum(DATATYPES); - private static final String[] INTERPRETATIONS = "Number,Currency,Lookup,LookupMulti,LookupBitstring,LookupBitmask".split(","); - private static final AttrType sInterpretations = new AttrEnum(INTERPRETATIONS); - private static final String[] ALIGNMENTS = "Left,Right,Center,Justify".split(","); - private static final AttrType sAlignments = new AttrEnum(ALIGNMENTS); - private static final String[] UNITSS = "Feet,Meters,SqFt,SqMeters,Acres,Hectares".split(","); - private static final AttrType sUnits = new AttrEnum(UNITSS); - - public MTable() { - this(DEFAULT_PARSING); - } - - public MTable(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - - String metadataEntryID = getStringAttribute(METADATAENTRYID); - if (metadataEntryID == null){ - metadataEntryID = this.getSystemName(); - } - return metadataEntryID; - } - - public String getSystemName() { - return getStringAttribute(SYSTEMNAME); - } - - public String getStandardName() { - return getStringAttribute(STANDARDNAME); - } - - public String getLongName() { - return getStringAttribute(LONGNAME); - } - - public String getDBName() { - return getStringAttribute(DBNAME); - } - - public String getShortName() { - return getStringAttribute(SHORTNAME); - } - - public int getMaximumLength() { - return getIntAttribute(MAXIMUMLENGTH); - } - - public String getDataType() { - return getStringAttribute(DATATYPE); - } - - public int getPrecision() { - return getIntAttribute(PRECISION); - } - - public boolean getSearchable() { - return getBooleanAttribute(SEARCHABLE); - } - - public String getInterpretation() { - return getStringAttribute(INTERPRETATION); - } - - public boolean isLookup() { - String interp = getInterpretation(); - if (interp != null && interp.startsWith("Lookup")) { - return true; - } - if (getSystemName().equalsIgnoreCase("status")) { - System.out.println("Field is " + getSystemName() + " and interp " + "is " + interp - + " but isLookup() is false"); - } - return false; - } - - public String getAlignment() { - return getStringAttribute(ALIGNMENT); - } - - public boolean getUseSeparator() { - return getBooleanAttribute(USESEPARATOR); - } - - public String getEditMaskID() { - return getStringAttribute(EDITMASKID); - } - - public String getLookupName() { - return getStringAttribute(LOOKUPNAME); - } - - public int getMaxSelect() { - return getIntAttribute(MAXSELECT); - } - - public String getUnits() { - return getStringAttribute(UNITS); - } - - public int getIndex() { - return getIntAttribute(INDEX); - } - - public int getMinimum() { - return getIntAttribute(MINIMUM); - } - - public int getMaximum() { - return getIntAttribute(MAXIMUM); - } - - public int getDefault() { - return getIntAttribute(DEFAULT); - } - - public int getRequired() { - return getIntAttribute(REQUIRED); - } - - public String getSearchHelpID() { - return getStringAttribute(SEARCHHELPID); - } - - public boolean getUnique() { - return getBooleanAttribute(UNIQUE); - } - - public boolean getModTimestamp() { - return getBooleanAttribute(MODTIMESTAMP); - } - - public boolean getModTimestampName() { - return getBooleanAttribute(MODTIMESTAMPNAME); - } - - public boolean getInKeyIndex() { - return getBooleanAttribute(INKEYINDEX); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return SYSTEMNAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, retsid); - attributeMap.put(SYSTEMNAME, retsname); - attributeMap.put(STANDARDNAME, retsname); - attributeMap.put(LONGNAME, sText256); - attributeMap.put(DBNAME, sAlphanum10); - attributeMap.put(SHORTNAME, sText64); - attributeMap.put(MAXIMUMLENGTH, sAttrNumericPositive); - attributeMap.put(DATATYPE, sDataTypes); - attributeMap.put(PRECISION, sAttrNumeric); - attributeMap.put(SEARCHABLE, sAttrBoolean); - attributeMap.put(INTERPRETATION, sInterpretations); - attributeMap.put(ALIGNMENT, sAlignments); - attributeMap.put(USESEPARATOR, sAttrBoolean); - // XXX: but multiples are separated by commas - attributeMap.put(EDITMASKID, retsname); - attributeMap.put(LOOKUPNAME, retsname); - attributeMap.put(MAXSELECT, sAttrNumeric); - attributeMap.put(UNITS, sUnits); - attributeMap.put(INDEX, sAttrNumeric); - attributeMap.put(MINIMUM, sAttrNumeric); - attributeMap.put(MAXIMUM, sAttrNumeric); - // XXX: serial - attributeMap.put(DEFAULT, sAttrNumeric); - attributeMap.put(REQUIRED, sAttrNumeric); - attributeMap.put(SEARCHHELPID, retsname); - attributeMap.put(UNIQUE, sAttrBoolean); - attributeMap.put(MODTIMESTAMP, sAttrBoolean); - attributeMap.put(MODTIMESTAMPNAME, retsname); - attributeMap.put(FOREIGNKEYNAME,retsid); - attributeMap.put(FOREIGNFIELD,retsname); - attributeMap.put(INKEYINDEX, sAttrBoolean); - - attributeMap.put(KEYQUERY, sAttrBoolean); - attributeMap.put(KEYSELECT, sAttrBoolean); - } - - public String getForeignKeyName() { - return getStringAttribute(FOREIGNKEYNAME); - } - - public String getForeignField() { - return getStringAttribute(FOREIGNFIELD); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdate.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdate.java deleted file mode 100644 index d20acbf..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdate.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MUpdate extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String UPDATENAME = "UpdateName"; - public static final String DESCRIPTION = "Description"; - public static final String KEYFIELD = "KeyField"; - public static final String VERSION = "Version"; - public static final String DATE = "Date"; - public static final String UPDATETYPEVERSION = "UpdateTypeVersion"; - public static final String UPDATETYPEDATE = "UpdateTypeDate"; - - public MUpdate() { - this(DEFAULT_PARSING); - } - - public MUpdate(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getUpdateName() { - return getStringAttribute(UPDATENAME); - } - - public String getDescription() { - return getStringAttribute(DESCRIPTION); - } - - public String getKeyField() { - return getStringAttribute(KEYFIELD); - } - - public int getVersion() { - int v = getIntAttribute(VERSION); - if (v == 0){ - v = getIntAttribute(UPDATETYPEVERSION); - } - return v; - } - - public String getDate() { - String d = getDateAttribute(DATE); - if (d == null ){ - d = getDateAttribute(UPDATETYPEDATE); - } - return d; - } - - public MUpdateType getMUpdateType(String systemName) { - return (MUpdateType) getChild(MetadataType.UPDATE_TYPE, systemName); - } - - public MUpdateType[] getMUpdateTypes() { - MUpdateType[] tmpl = new MUpdateType[0]; - return (MUpdateType[]) getChildren(MetadataType.UPDATE_TYPE).toArray(tmpl); - } - - @Override - public MetadataType[] getChildTypes() { - return sTypes; - } - - @Override - protected String getIdAttr() { - return UPDATENAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(UPDATENAME, sAlphanum24); - attributeMap.put(DESCRIPTION, sPlaintext64); - attributeMap.put(KEYFIELD, sAlphanum32); - attributeMap.put(VERSION, sAttrVersion); - attributeMap.put(DATE, sAttrDate); - attributeMap.put(UPDATETYPEVERSION, sAttrVersion); - attributeMap.put(UPDATETYPEDATE, sAttrDate); - } - - private static final MetadataType[] sTypes = { MetadataType.UPDATE_TYPE }; -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateHelp.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateHelp.java deleted file mode 100644 index 23d7c52..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateHelp.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MUpdateHelp extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String UPDATEHELPID = "UpdateHelpID"; - public static final String VALUE = "Value"; - - public MUpdateHelp() { - this(DEFAULT_PARSING); - } - - public MUpdateHelp(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getUpdateHelpID() { - return getStringAttribute(UPDATEHELPID); - } - - public String getValue() { - return getStringAttribute(VALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return UPDATEHELPID; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(UPDATEHELPID, sAlphanum32); - attributeMap.put(VALUE, sText1024); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateType.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateType.java deleted file mode 100644 index 38ab5f2..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MUpdateType.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; -import com.ossez.usreio.tests.common.metadata.attrib.AttrGenericText; - -public class MUpdateType extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String SYSTEMNAME = "SystemName"; - public static final String SEQUENCE = "Sequence"; - public static final String ATTRIBUTES = "Attributes"; - public static final String DEFAULT = "Default"; - public static final String VALIDATIONEXPRESSIONID = "ValidationExpressionID"; - public static final String UPDATEHELPID = "UpdateHelpID"; - public static final String VALIDATIONLOOKUPNAME = "ValidationLookupName"; - public static final String VALIDATIONEXTERNALNAME = "ValidationExternalName"; - public static final String MAXCHOICE = "MaxChoice"; - public static final String MAXUPDATE = "MaxUpdate"; - - private static final AttrType sAttributes = new AttrGenericText(0, 10, "12345,"); - - public MUpdateType() { - this(DEFAULT_PARSING); - - } - - public MUpdateType(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getSystemName() { - return getStringAttribute(SYSTEMNAME); - } - - public int getSequence() { - return getIntAttribute(SEQUENCE); - } - - public String getAttributes() { - return getStringAttribute(ATTRIBUTES); - } - - public String getDefault() { - return getStringAttribute(DEFAULT); - } - - public String getValidationExpressionID() { - return getStringAttribute(VALIDATIONEXPRESSIONID); - } - - public String getUpdateHelpID() { - return getStringAttribute(UPDATEHELPID); - } - - public String getValidationLookupName() { - return getStringAttribute(VALIDATIONLOOKUPNAME); - } - - public String getValidationExternalName() { - return getStringAttribute(VALIDATIONEXTERNALNAME); - } - - public int getMaxChoice() { - return getIntAttribute(MAXCHOICE); - } - - public int getMaxUpdate() { - return getIntAttribute(MAXUPDATE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return SYSTEMNAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(SYSTEMNAME, sAlphanum32); - attributeMap.put(SEQUENCE, sAttrNumeric); - attributeMap.put(ATTRIBUTES, sAttributes); - attributeMap.put(DEFAULT, sPlaintext); - attributeMap.put(VALIDATIONEXPRESSIONID, sAlphanum32); - attributeMap.put(UPDATEHELPID, sAlphanum32); - attributeMap.put(VALIDATIONLOOKUPNAME, sAlphanum32); - attributeMap.put(VALIDATIONEXTERNALNAME, sAlphanum32); - attributeMap.put(MAXCHOICE, sAttrNumeric); - attributeMap.put(MAXUPDATE, sAttrNumeric); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExpression.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExpression.java deleted file mode 100644 index 4e62748..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExpression.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.AttrType; -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; -import com.ossez.usreio.tests.common.metadata.attrib.AttrEnum; - -public class MValidationExpression extends MetaObject { - - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String VALIDATIONEXPRESSIONID = "ValidationExpressionID"; - public static final String VALIDATIONEXPRESSIONTYPE = "ValidationExpressionType"; - public static final String VALUE = "Value"; - private static final String[] VALIDATIONEXPRESSIONTYPES = "ACCEPT,REJECT,SET".split(","); - private static final AttrType sExpressionType = new AttrEnum(VALIDATIONEXPRESSIONTYPES); - - public MValidationExpression() { - this(DEFAULT_PARSING); - } - - public MValidationExpression(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getValidationExpressionID() { - return getStringAttribute(VALIDATIONEXPRESSIONID); - } - - public String getValidationExpressionType() { - return getStringAttribute(VALIDATIONEXPRESSIONTYPE); - } - - public String getValue() { - return getStringAttribute(VALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return VALIDATIONEXPRESSIONID; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(VALIDATIONEXPRESSIONID, sAlphanum32); - attributeMap.put(VALIDATIONEXPRESSIONTYPE, sExpressionType); - attributeMap.put(VALUE, sText512); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternal.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternal.java deleted file mode 100644 index 2d266f5..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternal.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MValidationExternal extends MetaObject { - - private static final MetadataType[] CHILDREN = { MetadataType.VALIDATION_EXTERNAL_TYPE }; - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String VALIDATIONEXTERNALNAME = "ValidationExternalName"; - public static final String SEARCHRESOURCE = "SearchResource"; - public static final String SEARCHCLASS = "SearchClass"; - public static final String VERSION = "Version"; - public static final String DATE = "Date"; - - public MValidationExternal() { - this(DEFAULT_PARSING); - } - - public MValidationExternal(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getValidationExternalName() { - return getStringAttribute(VALIDATIONEXTERNALNAME); - } - - public String getSearchResource() { - return getStringAttribute(SEARCHRESOURCE); - } - - public String getSearchClass() { - return getStringAttribute(SEARCHCLASS); - } - - public int getVersion() { - return getIntAttribute(VERSION); - } - - public String getDate() { - return getDateAttribute(DATE); - } - - public MValidationExternalType[] getMValidationExternalTypes() { - MValidationExternalType[] tmpl = new MValidationExternalType[0]; - return (MValidationExternalType[]) getChildren(MetadataType.VALIDATION_EXTERNAL_TYPE).toArray(tmpl); - } - - @Override - public MetadataType[] getChildTypes() { - return CHILDREN; - } - - @Override - protected String getIdAttr() { - return VALIDATIONEXTERNALNAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(VALIDATIONEXTERNALNAME, sAlphanum32); - attributeMap.put(SEARCHRESOURCE, sAlphanum32); - attributeMap.put(SEARCHCLASS, sAlphanum32); - attributeMap.put(VERSION, sAttrVersion); - attributeMap.put(DATE, sAttrDate); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternalType.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternalType.java deleted file mode 100644 index dc26da1..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationExternalType.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MValidationExternalType extends MetaObject { - - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String SEARCHFIELD = "SearchField"; - public static final String DISPLAYFIELD = "DisplayField"; - public static final String RESULTFIELDS = "ResultFields"; - - public MValidationExternalType() { - this(DEFAULT_PARSING); - } - - public MValidationExternalType(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getSearchField() { - return getStringAttribute(SEARCHFIELD); - } - - public String getDisplayField() { - return getStringAttribute(DISPLAYFIELD); - } - - public String getResultFields() { - return getStringAttribute(RESULTFIELDS); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return null; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(SEARCHFIELD, sPlaintext512); - attributeMap.put(DISPLAYFIELD, sPlaintext512); - attributeMap.put(RESULTFIELDS, sPlaintext1024); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookup.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookup.java deleted file mode 100644 index 118dca1..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookup.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -//import java.util.Date; -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MValidationLookup extends MetaObject { - - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String VALIDATIONLOOKUPNAME = "ValidationLookupName"; - public static final String PARENT1FIELD = "Parent1Field"; - public static final String PARENT2FIELD = "Parent2Field"; - public static final String VERSION = "Version"; - public static final String DATE = "Date"; - private static final MetadataType[] sChildren = { MetadataType.VALIDATION_LOOKUP_TYPE }; - - public MValidationLookup() { - this(DEFAULT_PARSING); - } - - public MValidationLookup(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getValidationLookupName() { - return getStringAttribute(VALIDATIONLOOKUPNAME); - } - - public String getParent1Field() { - return getStringAttribute(PARENT1FIELD); - } - - public String getParent2Field() { - return getStringAttribute(PARENT2FIELD); - } - - public int getVersion() { - return getIntAttribute(VERSION); - } - - public String getDate() { - return getDateAttribute(DATE); - } - - @Override - public MetadataType[] getChildTypes() { - return sChildren; - } - - @Override - protected String getIdAttr() { - return VALIDATIONLOOKUPNAME; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(VALIDATIONLOOKUPNAME, sAlphanum32); - attributeMap.put(PARENT1FIELD, sAlphanum32); - attributeMap.put(PARENT2FIELD, sAlphanum32); - attributeMap.put(VERSION, sAttrVersion); - attributeMap.put(DATE, sAttrDate); - } - - public MValidationLookupType[] getMValidationLookupTypes() { - MValidationLookupType[] tmpl = new MValidationLookupType[0]; - return (MValidationLookupType[]) getChildren(MetadataType.VALIDATION_LOOKUP_TYPE).toArray(tmpl); - } - -} diff --git a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookupType.java b/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookupType.java deleted file mode 100644 index ff3e60f..0000000 --- a/src/main/java/com/ossez/usreio/tests/common/metadata/types/MValidationLookupType.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.ossez.usreio.tests.common.metadata.types; - -import java.util.Map; - -import com.ossez.usreio.tests.common.metadata.MetaObject; -import com.ossez.usreio.tests.common.metadata.MetadataType; - -public class MValidationLookupType extends MetaObject { - public static final String METADATAENTRYID = "MetadataEntryID"; - public static final String VALIDTEXT = "ValidText"; - public static final String PARENT1VALUE = "Parent1Value"; - public static final String PARENT2VALUE = "Parent2Value"; - - public MValidationLookupType() { - this(DEFAULT_PARSING); - } - - public MValidationLookupType(boolean strictParsing) { - super(strictParsing); - } - - public String getMetadataEntryID() { - return getStringAttribute(METADATAENTRYID); - } - - public String getValidText() { - return getStringAttribute(VALIDTEXT); - } - - public String getParent1Value() { - return getStringAttribute(PARENT1VALUE); - } - - public String getParent2Value() { - return getStringAttribute(PARENT2VALUE); - } - - @Override - public MetadataType[] getChildTypes() { - return sNoChildren; - } - - @Override - protected String getIdAttr() { - return null; - } - - @Override - protected void addAttributesToMap(Map attributeMap) { - attributeMap.put(METADATAENTRYID, sAttrMetadataEntryId); - attributeMap.put(VALIDTEXT, sAlphanum32); - attributeMap.put(PARENT1VALUE, sAlphanum32); - attributeMap.put(PARENT2VALUE, sAlphanum32); - } - -} diff --git a/src/main/java/com/ossez/usreio/util/SessionUtils.java b/src/main/java/com/ossez/usreio/util/SessionUtils.java deleted file mode 100644 index 2305a3f..0000000 --- a/src/main/java/com/ossez/usreio/util/SessionUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.ossez.usreio.util; - -import com.ossez.usreio.client.*; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * SessionUtils for RETS server session - * - * @author YuCheng Hu - */ -public final class SessionUtils { - private static final Logger logger = LoggerFactory.getLogger(SessionUtils.class); - - // Prevent the class from being constructed - private SessionUtils() { - - } - - /** - * Login to Server and return session Object - * - * @param retsLoginUrl - * @param retsUsername - * @param retsPassword - * @return - */ - public static RetsSession retsLogin(String retsLoginUrl, String retsUsername, String retsPassword, RetsVersion retsVersion) throws RetsException { - logger.debug("RETS Session Login URL: [{}]", retsLoginUrl); - - LoginResponse loginResponse = new LoginResponse(); - - //Create a RetsHttpClient (other constructors provide configuration i.e. timeout, gzip capability) - RetsHttpClient httpClient = new CommonsHttpClient(); - - // SET RETS VERSION - if (ObjectUtils.isEmpty(retsVersion)) - retsVersion = RetsVersion.DEFAULT; - - //Create a RetesSession with RetsHttpClient - RetsSession session = new RetsSession(retsLoginUrl, httpClient, retsVersion); - - //Set method as GET or POST - session.setMethod("POST"); - try { - //Login - loginResponse = session.login(retsUsername, retsPassword); - } catch (RetsException ex) { - throw ex; - } - - // SESSION NULL CHECK - if (!(!ObjectUtils.isEmpty(session) && StringUtils.isNotEmpty(loginResponse.getSessionId()))) { - session = null; - } - - logger.info("Session ID :[{}]", loginResponse.getSessionId()); - return session; - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/ConnectionTest.java b/src/test/java/com/ossez/usreio/tests/client/ConnectionTest.java deleted file mode 100644 index 446fa38..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/ConnectionTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.ossez.usreio.tests.client; - -import org.junit.jupiter.api.Test; -import com.ossez.usreio.client.retsapi.RETSConnection; -import com.ossez.usreio.client.retsapi.RETSLoginTransaction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -import java.io.InputStream; -import java.util.Properties; - -/** - * @author YuCheng - */ -public class ConnectionTest { - - private final static Logger logger = LoggerFactory.getLogger(ConnectionTest.class); - - /** - * Do RetsServerConnection Test - */ - @Test - public void testStaticVariableChange() { - - // BasicConfigurator.configure(); - - RETSConnection rc = new RETSConnection(); - RETSLoginTransaction trans = new RETSLoginTransaction(); - - try { - Properties props = new Properties(); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - InputStream inputStream = loader.getResourceAsStream("rets.properties"); - - props.load(inputStream); - - // 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("rets_server")); - trans.setUrl((String) props.getProperty("rets_server")); - trans.setUsername((String) props.getProperty("rets_username")); - trans.setPassword((String) props.getProperty("rets_password")); - } catch (Exception e) { - e.printStackTrace(); - } - - rc.execute(trans); - - } - -} diff --git a/src/test/java/com/ossez/usreio/tests/client/GetMetadataRequestTest.java b/src/test/java/com/ossez/usreio/tests/client/GetMetadataRequestTest.java deleted file mode 100644 index 3a2f735..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/GetMetadataRequestTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.ossez.usreio.tests.client; - -import com.ossez.usreio.client.GetMetadataRequest; -import com.ossez.usreio.client.InvalidArgumentException; -import com.ossez.usreio.client.RetsException; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class GetMetadataRequestTest extends RetsTestCase { - public void testGetMetadataRequestSimple() throws RetsException { - GetMetadataRequest request = new GetMetadataRequest("SYSTEM", "*"); - request.setUrl("http://rets.test:6103/getMetadata"); - assertFalse(request.isCompactFormat()); - assertTrue(request.isStandardXmlFormat()); -// assertNull(request.getStandardXmlVersion()); -// assertEquals("http://rets.test:6103/getMetadata", request.getUrl()); -// assertEquals("Format=STANDARD-XML&ID=*&Type=METADATA-SYSTEM", RetsUtil.urlDecode(request.getHttpParameters())); - } - - public void testGetMetadataRequestMultipleIds() throws RetsException { - GetMetadataRequest request = new GetMetadataRequest("UPDATE_TYPE", new String[] { "ActiveAgent", "ACTAGT", - "Change_ACTAGT" }); - request.setCompactFormat(); - - assertTrue(request.isCompactFormat()); - assertFalse(request.isStandardXmlFormat()); -// assertEquals("Format=COMPACT&ID=ActiveAgent:ACTAGT:Change_ACTAGT" + "&Type=METADATA-UPDATE_TYPE", RetsUtil -// .urlDecode(request.getHttpParameters())); - } - - public void testInvalidGetMetadataRequests() throws RetsException { - try { - // ID for METADATA-SYSTEM can only be 0 or * - new GetMetadataRequest("SYSTEM", "Blah"); -// fail("Should have thrown an InvalidArgumentException"); - } catch (InvalidArgumentException e) { - // Expected - } - - try { - // ID for METADATA-RESOURCE can only be 0 or * - new GetMetadataRequest("RESOURCE", "Blah"); -// fail("Should have thrown an InvalidArgumentException"); - } catch (InvalidArgumentException e) { - // Expected - } - - try { - // Must have at least 1 ID - new GetMetadataRequest("RESOURCE", new String[0]); -// fail("Should have thrown an InvalidArgumentException"); - } catch (InvalidArgumentException e) { - // Expected - } - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/GetMetadataResponseTest.java b/src/test/java/com/ossez/usreio/tests/client/GetMetadataResponseTest.java deleted file mode 100644 index d3491b4..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/GetMetadataResponseTest.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.ossez.usreio.tests.client; - -//import java.util.List; -//import java.util.ArrayList; -//import java.util.Iterator; -//import java.util.Map; - -public class GetMetadataResponseTest extends RetsTestCase { - public void testValidSystemMetadataResponse() { - // GetMetadataResponse response = new GetMetadataResponse( - // getResource("getMetadataResponse_system.xml")); - - // MetadataSegment[] segments = response.getMetadataSegments(); - // assertEquals(1, segments.length); - // MetadataSegment segment = segments[0]; - // assertEquals("SYSTEM", segment.getName()); - // assertEquals("01.00.001", segment.getAttribute("Version")); - // assertEquals("Tue, 27 May 2003 12:00:00 CDT", - // segment.getAttribute("Date")); - // assertEquals("CRT_RETS", segment.getSystemId()); - // assertEquals("Center for REALTOR Technology", - // segment.getSystemDescription()); - // assertEquals("The reference implementation of a RETS Server", - // segment.getSystemComments()); - // assertNull(segment.getColumns()); - // assertNull(segment.getData()); - } - - public void testSingleSegmentResponse() { - // GetMetadataResponse response = new GetMetadataResponse( - // getResource("getMetadataResponse_updateType.xml")); - // - // MetadataSegment[] segments = response.getMetadataSegments(); - // assertEquals(1, segments.length); - // MetadataSegment segment = segments[0]; - // assertEquals("UPDATE_TYPE", segment.getName()); - // - // assertEquals("ActiveAgent", segment.getAttribute("resource")); - // assertEquals("ACTAGT", segment.getAttribute("CLASS")); - // assertEquals("Change_ACTAGT", segment.getAttribute("update")); - // assertEquals("1.00.000", segment.getAttribute("VERSION")); - // assertEquals("Sat, 20 Mar 2002 12:03:38 GMT", - // segment.getAttribute("date")); - // - // // Try with the opposite case above to check case-insensitive - // // comparisons - // Map attributes = segment.getAttributes(); - // assertEquals(5, attributes.size()); - // assertEquals("ActiveAgent", attributes.get("RESOURCE")); - // assertEquals("ACTAGT", attributes.get("class")); - // assertEquals("Change_ACTAGT", attributes.get("UPDATE")); - // assertEquals("1.00.000", attributes.get("version")); - // assertEquals("Sat, 20 Mar 2002 12:03:38 GMT", - // attributes.get("DATE")); - // - // assertNull(segment.getSystemId()); - // assertNull(segment.getSystemDescription()); - // assertNull(segment.getSystemComments()); - // - // List columns = segment.getColumns(); - // assertNotNull("columns not null", columns); - // assertEquals("columns.size", 8, columns.size()); - // - // List expectedColumns = new ArrayList(); - // expectedColumns.add("SystemName"); - // expectedColumns.add("Sequence"); - // expectedColumns.add("Attributes"); - // expectedColumns.add("Default"); - // expectedColumns.add("ValidationExpressionID"); - // expectedColumns.add("UpdateHelpID"); - // expectedColumns.add("ValidationLookupName"); - // expectedColumns.add("ValidationExternalName"); - // assertEquals("columns", expectedColumns, columns); - // - // List data = segment.getData(); - // assertNotNull(data); - // assertEquals(2, data.size()); - // Iterator i = data.iterator(); - // - // assertTrue(i.hasNext()); - // List dataRow = (List) i.next(); - // assertEquals(8, dataRow.size()); - // List expectedDataRow = new ArrayList(); - // expectedDataRow.add("AGENT_ID"); - // expectedDataRow.add("1"); - // expectedDataRow.add("1"); - // expectedDataRow.add("0"); - // expectedDataRow.add(null); - // expectedDataRow.add(null); - // expectedDataRow.add(null); - // expectedDataRow.add(null); - // assertEquals(expectedDataRow, dataRow); - // - // assertTrue(i.hasNext()); - // dataRow = (List) i.next(); - // assertEquals(8, dataRow.size()); - // expectedDataRow = new ArrayList(); - // expectedDataRow.add("OFFICE_ID"); - // expectedDataRow.add("2"); - // expectedDataRow.add("2"); - // expectedDataRow.add("0"); - // expectedDataRow.add(null); - // expectedDataRow.add(null); - // expectedDataRow.add(null); - // expectedDataRow.add(null); - // assertEquals(expectedDataRow, dataRow); - } - - public void testMultipleSegmentResponse() { - // GetMetadataResponse response = new GetMetadataResponse( - // getResource("getMetadataResponse_lookupZero.xml")); - // - // MetadataSegment[] segments = response.getMetadataSegments(); - // assertEquals(2, segments.length); - // - // // Check first segment - // MetadataSegment segment = segments[0]; - // assertEquals("LOOKUP", segment.getName()); - // assertEquals("Property", segment.getAttribute("Resource")); - // List columns = segment.getColumns(); - // assertNotNull(columns); - // assertEquals(4, columns.size()); - // - // List expectedColumns = new ArrayList(); - // expectedColumns.add("LookupName"); - // expectedColumns.add("VisibleName"); - // expectedColumns.add("Version"); - // expectedColumns.add("Date"); - // assertEquals("columns", expectedColumns, columns); - // - // List data = segment.getData(); - // assertNotNull(data); - // assertEquals(9, data.size()); - // - // // Check second segment - // segment = segments[1]; - // assertEquals("LOOKUP", segment.getName()); - // assertEquals("Agent", segment.getAttribute("Resource")); - // columns = segment.getColumns(); - // assertNotNull(columns); - // assertEquals(4, columns.size()); - // assertEquals("columns", expectedColumns, columns); - // - // data = segment.getData(); - // assertNotNull(data); - // assertEquals(1, data.size()); - } - - public void testNoRecordsMetadataResponse() { - // GetMetadataResponse response = new GetMetadataResponse( - // getResource("getMetadataResponse_noRecords.xml")); - // - // MetadataSegment[] segments = response.getMetadataSegments(); - // assertEquals(0, segments.length); - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/GetObjectResponseIteratorTest.java b/src/test/java/com/ossez/usreio/tests/client/GetObjectResponseIteratorTest.java deleted file mode 100644 index b06f01f..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/GetObjectResponseIteratorTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import com.ossez.usreio.client.GetObjectIterator; -import com.ossez.usreio.client.GetObjectResponse; -import com.ossez.usreio.client.SingleObjectResponse; -import junit.framework.TestCase; - -public class GetObjectResponseIteratorTest extends TestCase { - private static final String BOUNDARY = "jack"; - - private static final String BINARY_BLOB_1 = "1)dcg fa8 5 uiwjskdgsdfkg hdsfa bdf" + " erkfjhwfewuhuh" - + "B\r\n\r\n"; - - private static final String BINARY_BLOB_2 = "2)dcg fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + "\tAAAAAAAAAAAAhdsfa bdf"; - - private static final String BINARY_BLOB_3 = "3)dcg fAAAAAAAAAAAAAAAAA\t\\!" - + "\r\n\r\nAAAAAAAAAAAAAAAAAAAAAAAAAhdsfa bdf"; - - private static final String BINARY_BLOB_4 = "fgsdgsdfg"; - - private static final String BINARY_BLOB_5 = "4)dcg fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhdsfa bdf"; - - public static final byte[] MULTIPART_RESPONSE_BODY = ("--" + BOUNDARY + "\r\n" + "Content-Type: text\r\n" - + "Content-ID: one\r\n" + "Object-ID: 1\r\n" + "\r\n" + BINARY_BLOB_1 + "\r\n--" + BOUNDARY + "\r\n" - + "Content-Type: gray-matter\r\n" + "Content-ID: two\r\n" + "Object-ID: 2\r\n" + "\r\n" + BINARY_BLOB_2 - + "\r\n--" + BOUNDARY + "\r\n" + "Content-Type: blue-matter\r\n" + "Content-ID: three\r\n" - + "Object-ID: 3\r\n" + "\r\n" + BINARY_BLOB_3 + "\r\n--" + BOUNDARY + "\r\n" - + "Content-Type: green-matter\r\n" + "Content-ID: four\r\n" + "Object-ID: 4\r\n" + "\r\n" + BINARY_BLOB_4 - + "\r\n--" + BOUNDARY + "\r\n" + "Content-Type: yellow-matter-custard\r\n" + "Content-ID: five\r\n" - + "Object-ID: 5\r\n" + "\r\n" + BINARY_BLOB_5 + "\r\n--" + BOUNDARY + "--").getBytes(); - - public void testIterationMultipart() throws Exception { - GetObjectIterator getObjectIterator = null; - - Map headers = new HashMap(); - headers.put("Content-Type", "multipart/parallel; boundary=\"" + BOUNDARY + "\""); - headers.put("MIME-Version", "1.0"); - GetObjectResponse getObjectResponse = new GetObjectResponse(headers, new ByteArrayInputStream(MULTIPART_RESPONSE_BODY)); - getObjectIterator = getObjectResponse.iterator(); - - SingleObjectResponse firstResponse = getObjectIterator.next(); - assertEquals("text", firstResponse.getType()); - assertEquals("one", firstResponse.getContentID()); - assertEquals("1", firstResponse.getObjectID()); - assertEquals(BINARY_BLOB_1, new String(readOut(firstResponse.getInputStream(), 1024))); - - assertTrue(getObjectIterator.hasNext()); - assertTrue(getObjectIterator.hasNext()); - - SingleObjectResponse secondResponse = getObjectIterator.next(); - assertEquals("gray-matter", secondResponse.getType()); - assertEquals("two", secondResponse.getContentID()); - assertEquals("2", secondResponse.getObjectID()); - assertEquals(BINARY_BLOB_2, new String(readOut(secondResponse.getInputStream(), 1024))); - - getObjectIterator.next(); - getObjectIterator.next(); - getObjectIterator.next(); - - assertFalse(getObjectIterator.hasNext()); - assertFalse(getObjectIterator.hasNext()); - } - - public void testIterationNonMultipart() throws Exception { - GetObjectIterator getObjectIterator = null; - - Map headers = new HashMap(); - headers.put("Content-Type", "image/jpeg"); - headers.put("MIME-Version", "1.0"); - headers.put("Content-ID", "one"); - headers.put("Object-ID", "1"); - GetObjectResponse getObjectResponse = new GetObjectResponse(headers, new ByteArrayInputStream(BINARY_BLOB_1 - .getBytes())); - - getObjectIterator = getObjectResponse.iterator(); - - assertTrue(getObjectIterator.hasNext()); - assertTrue(getObjectIterator.hasNext()); - - SingleObjectResponse firstResponse = getObjectIterator.next(); - - assertEquals("image/jpeg", firstResponse.getType()); - assertEquals("one", firstResponse.getContentID()); - assertEquals("1", firstResponse.getObjectID()); - assertEquals(BINARY_BLOB_1, new String(readOut(firstResponse.getInputStream(), 1024))); - - assertFalse(getObjectIterator.hasNext()); - assertFalse(getObjectIterator.hasNext()); - } - - /* - * TODO: Fix these tests. - * - public void testMissingObjects() throws Exception { - Map headers = new HashMap(); - String BUG_BOUNDARY = "50eb24a2.9354.35f3.be11.9cea9411a260"; - headers.put("Content-Type", "mutipart/parallel; boundary=\"" + BUG_BOUNDARY + "\""); - headers.put("MIME-Version", "1.0"); - - InputStream BUG_MULTIPART_RESPONSE_BODY = this.getClass().getResourceAsStream("objects-missing.multipart"); - - GetObjectResponse getObjectResponse = new GetObjectResponse(headers, BUG_MULTIPART_RESPONSE_BODY); - GetObjectIterator bugObjectIterator = GetObjectResponseIterator.createIterator(getObjectResponse, 10000); - - String[] expectedContentIds = new String[] { "111285", "10037", "100084", "13710", "58946", }; - for (int i = 0; bugObjectIterator.hasNext(); i++) { - SingleObjectResponse objectResponse = bugObjectIterator.next(); - String contentID = objectResponse.getContentID(); - assertEquals(expectedContentIds[i], contentID); - } - } - - public void testParsingObjects() throws Exception { - Map headers = new HashMap(); - String BUG_BOUNDARY = "simple boundary"; - headers.put("Content-Type", "mutipart/parallel; boundary=\"" + BUG_BOUNDARY + "\""); - headers.put("MIME-Version", "1.0"); - - InputStream BUG_MULTIPART_RESPONSE_BODY = this.getClass().getResourceAsStream("2237858_0.jpg"); - - GetObjectResponse getObjectResponse = new GetObjectResponse(headers, BUG_MULTIPART_RESPONSE_BODY); - GetObjectIterator bugObjectIterator = GetObjectResponseIterator.createIterator(getObjectResponse, 10000); - - String[] expectedContentIds = new String[] { "2237858", "2237858", "2237858", "2237858", "2236185", "2236185", - "2236185", "2236185", "2236210", "2236210", "2236210", "2236210", "2236210" }; - String[] expectedObjectIds = new String[] { "1", "2", "3", "4", "0", "1", "2", "3", "0", "1", "2", "3", "4", }; - int i = 0; - for (; bugObjectIterator.hasNext(); i++) { - SingleObjectResponse objectResponse = bugObjectIterator.next(); - - String contentID = objectResponse.getContentID(); - assertEquals(expectedContentIds[i], contentID); - - String objectID = objectResponse.getObjectID(); - assertEquals(expectedObjectIds[i], objectID); - - File tmp = File.createTempFile("embedded-image.", "." + contentID + "_" + objectID + ".jpg"); - byte[] image = GetObjectResponseIteratorTest.readOut(objectResponse.getInputStream(), 10000); - - FileOutputStream imageOut = new FileOutputStream(tmp); - try { - imageOut.write(image); - } finally { - imageOut.close(); - } - System.out.println("embedded image extracted to: " + tmp); - } - assertEquals("Objects were swallowed.", expectedContentIds.length, i); - } - */ - - /** - * Some RETS servers send headers like "Content-type" - */ - public void testCaseInsensitiveHeaders() throws Exception { - GetObjectIterator getObjectIterator = null; - - Map headers = new HashMap(); - headers.put("Content-type", "image/jpeg"); - headers.put("MIME-version", "1.0"); - headers.put("content-id", "one"); - headers.put("Object-id", "1"); - GetObjectResponse getObjectResponse = new GetObjectResponse(headers, new ByteArrayInputStream(BINARY_BLOB_1.getBytes())); - - getObjectIterator = getObjectResponse.iterator(); - - assertTrue(getObjectIterator.hasNext()); - SingleObjectResponse firstResponse = getObjectIterator.next(); - - assertEquals("image/jpeg", firstResponse.getType()); - assertEquals("one", firstResponse.getContentID()); - assertEquals("1", firstResponse.getObjectID()); - assertEquals(BINARY_BLOB_1, new String(readOut(firstResponse.getInputStream(), 1024))); - } - - public static byte[] readOut(InputStream in, int bufferSize) throws IOException { - byte[] temp = new byte[bufferSize]; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - do { - bufferSize = in.read(temp, 0, bufferSize); - if (bufferSize > 0) - baos.write(temp, 0, bufferSize); - } while (bufferSize != -1); - return baos.toByteArray(); - } - -} diff --git a/src/test/java/com/ossez/usreio/tests/client/IOFailReader.java b/src/test/java/com/ossez/usreio/tests/client/IOFailReader.java deleted file mode 100644 index 6cf47c3..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/IOFailReader.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.io.FilterReader; -import java.io.IOException; -import java.io.Reader; - -public class IOFailReader extends FilterReader { - - protected IOFailReader(Reader reader) { - super(reader); - } - - public void setFailRead(boolean failRead) { - this.mFailRead = failRead; - } - - @Override - public int read() throws IOException { - checkFailRead(); - return super.read(); - } - - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - checkFailRead(); - return super.read(cbuf, off, len); - } - - private void checkFailRead() throws IOException { - if (this.mFailRead) - throw new IOException("Simulated IOException"); - } - - private boolean mFailRead; -} diff --git a/src/test/java/com/ossez/usreio/tests/client/LoginRequestTest.java b/src/test/java/com/ossez/usreio/tests/client/LoginRequestTest.java deleted file mode 100644 index 19bc732..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/LoginRequestTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.ossez.usreio.tests.client; - -import com.ossez.usreio.client.LoginRequest; - -public class LoginRequestTest extends RetsTestCase { - public void testGetUrl() { - LoginRequest req = new LoginRequest(); - req.setUrl("http://testurl:6103/login"); -// assertEquals("http://testurl:6103/login", req.getUrl()); - } - - public void testSetBrokerCode() { - LoginRequest req = new LoginRequest(); - req.setUrl("http://testurl:6103/login"); - req.setBrokerCode(null, "branch"); -// assertEquals("http://testurl:6103/login", req.getUrl()); - req.setBrokerCode("broker", null); - // query parameters are separate now because of get/post -// assertEquals("http://testurl:6103/login", req.getUrl()); -// assertEquals("BrokerCode=broker", req.getHttpParameters()); - req.setBrokerCode("broker", "branch"); -// assertEquals("BrokerCode=broker,branch", RetsUtil.urlDecode(req.getHttpParameters())); - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/LoginResponseTest.java b/src/test/java/com/ossez/usreio/tests/client/LoginResponseTest.java deleted file mode 100644 index 7cb0092..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/LoginResponseTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.ossez.usreio.tests.client; - - -import com.ossez.usreio.client.CapabilityUrls; -import com.ossez.usreio.client.LoginResponse; -import com.ossez.usreio.client.RetsException; -import com.ossez.usreio.client.RetsVersion; -import org.junit.Test; - -public class LoginResponseTest extends RetsTestCase { - /** - * @throws RetsException - */ - @Test - public void testValidLoginResponse17() throws RetsException { - LoginResponse response = new LoginResponse(); -// response.parse(getResource("login_response_valid_1.7.xml"), RetsVersion.RETS_17); -// assertEquals("Checking broker", "4935,4935", response.getBroker()); -// assertEquals("Checking member name", "BHHS Verani IDX RETS User", response.getMemberName()); -// assertEquals("Checking metadata version", "19.9.17332", response.getMetadataVersion()); -// assertEquals("Checking min metadata version", null, response.getMinMetadataVersion()); -// assertEquals("Checking user information", "test,1,21,279117", response.getUserInformation()); -//// assertNull("Checking office list", response.getOfficeList()); -// assertEquals("Checking balance", null, response.getBalance()); -// assertEquals("Checking timeout", 7200, response.getSessionTimeout()); -//// assertNull("Checking password expiration", response.getPasswordExpiration()); - - CapabilityUrls urls = response.getCapabilityUrls(); -// assertEquals(null, urls.getActionUrl()); -// assertEquals(null, urls.getChangePasswordUrl()); -// assertEquals("http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/getobject", urls.getGetObjectUrl()); -// assertEquals("http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/login", urls.getLoginUrl()); -// assertNull(urls.getLoginCompleteUrl()); -// assertEquals("http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/logout", urls.getLogoutUrl()); -// assertEquals("http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/search", urls.getSearchUrl()); -// assertEquals("http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/getmetadata", urls.getGetMetadataUrl()); -// assertNull(urls.getUpdateUrl()); - } - - - /** - * @throws RetsException - */ - public void testValidLoginResponse15() throws RetsException { - LoginResponse response = new LoginResponse(); -// response.parse(getResource("login_response_valid_1.5.xml"), RetsVersion.RETS_15); -// assertEquals("Checking broker", "B123, BO987", response.getBroker()); -// assertEquals("Checking member name", "Joe T. Schmoe", response.getMemberName()); -// assertEquals("Checking metadata version", "1.00.000", response.getMetadataVersion()); -// assertEquals("Checking min metadata version", "1.00.000", response.getMinMetadataVersion()); -// assertEquals("Checking user information", "A123,5678,1,A123", response.getUserInformation()); -// assertNull("Checking office list", response.getOfficeList()); -// assertEquals("Checking balance", "44.21", response.getBalance()); -// assertEquals("Checking timeout", 60, response.getSessionTimeout()); -// assertNull("Checking password expiration", response.getPasswordExpiration()); -// -// CapabilityUrls urls = response.getCapabilityUrls(); -// assertEquals("http://rets.test:6103/get", urls.getActionUrl()); -// assertEquals("http://rets.test:6103/changePassword", urls.getChangePasswordUrl()); -// assertEquals("http://rets.test:6103/getObjectEx", urls.getGetObjectUrl()); -// assertEquals("http://rets.test:6103/login", urls.getLoginUrl()); -// assertNull(urls.getLoginCompleteUrl()); -// assertEquals("http://rets.test:6103/logout", urls.getLogoutUrl()); -// assertEquals("http://rets.test:6103/search", urls.getSearchUrl()); -// assertEquals("http://rets.test:6103/getMetadata", urls.getGetMetadataUrl()); -// assertNull(urls.getUpdateUrl()); - } - - /** - * @throws RetsException - */ - public void testValidLoginResponse10() throws RetsException { - LoginResponse response = new LoginResponse(); -// response.parse(getResource("login_response_valid_1.0.xml"), RetsVersion.RETS_10); -// assertEquals("Checking broker", "B123, BO987", response.getBroker()); -// assertEquals("Checking member name", "Joe T. Schmoe", response.getMemberName()); -// assertEquals("Checking metadata version", "1.00.000", response.getMetadataVersion()); -// assertEquals("Checking min metadata version", "1.00.000", response.getMinMetadataVersion()); -// assertEquals("Checking user information", "A123,5678,1,A123", response.getUserInformation()); -// assertNull("Checking office list", response.getOfficeList()); -// assertEquals("Checking balance", "44.21", response.getBalance()); -// assertEquals("Checking timeout", 60, response.getSessionTimeout()); -// assertNull("Checking password expiration", response.getPasswordExpiration()); -// -// CapabilityUrls urls = response.getCapabilityUrls(); -// assertEquals("http://rets.test:6103/get", urls.getActionUrl()); -// assertEquals("http://rets.test:6103/changePassword", urls.getChangePasswordUrl()); -// assertEquals("http://rets.test:6103/getObjectEx", urls.getGetObjectUrl()); -// assertEquals("http://rets.test:6103/login", urls.getLoginUrl()); -// assertNull(urls.getLoginCompleteUrl()); -// assertEquals("http://rets.test:6103/logout", urls.getLogoutUrl()); -// assertEquals("http://rets.test:6103/search", urls.getSearchUrl()); -// assertEquals("http://rets.test:6103/getMetadata", urls.getGetMetadataUrl()); -// assertNull(urls.getUpdateUrl()); - } - - public void testLowerCaseKeys() throws RetsException { - LoginResponse response = new LoginResponse(); -// response.parse(getResource("login_lower_case.xml"), RetsVersion.RETS_15); -// assertEquals("Checking broker", "B123, BO987", response.getBroker()); -// assertEquals("Checking member name", "Joe T. Schmoe", response.getMemberName()); -// assertEquals("Checking metadata version", "1.00.000", response.getMetadataVersion()); -// assertEquals("Checking min metadata version", "1.00.000", response.getMinMetadataVersion()); -// assertEquals("Checking user information", "A123,5678,1,A123", response.getUserInformation()); -// assertNull("Checking office list", response.getOfficeList()); -// assertEquals("Checking balance", "44.21", response.getBalance()); -// assertEquals("Checking timeout", 60, response.getSessionTimeout()); -// assertNull("Checking password expiration", response.getPasswordExpiration()); -// -// CapabilityUrls urls = response.getCapabilityUrls(); -// assertEquals("http://rets.test:6103/get", urls.getActionUrl()); -// assertEquals("http://rets.test:6103/changePassword", urls.getChangePasswordUrl()); -// assertEquals("http://rets.test:6103/getObjectEx", urls.getGetObjectUrl()); -// assertEquals("http://rets.test:6103/login", urls.getLoginUrl()); -// assertNull(urls.getLoginCompleteUrl()); -// assertEquals("http://rets.test:6103/logout", urls.getLogoutUrl()); -// assertEquals("http://rets.test:6103/search", urls.getSearchUrl()); -// assertEquals("http://rets.test:6103/getMetadata", urls.getGetMetadataUrl()); -// assertNull(urls.getUpdateUrl()); - } - - public void testStrictLowerCaseKeys() { - LoginResponse response = new LoginResponse(); - response.setStrict(true); - try { - response.parse(getResource("login_lower_case.xml"), RetsVersion.RETS_15); - - } catch (RetsException e) { - // Expected -// fail("Should throw exception"); - } - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/LogoutResponseTest.java b/src/test/java/com/ossez/usreio/tests/client/LogoutResponseTest.java deleted file mode 100644 index 24860e7..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/LogoutResponseTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.ossez.usreio.tests.client; - -import com.ossez.usreio.client.LogoutResponse; -import com.ossez.usreio.client.RetsException; -import com.ossez.usreio.client.RetsVersion; - -public class LogoutResponseTest extends RetsTestCase { - /* - * TODO: FIX THESE - * - public void testValidLogoutResponse10() throws RetsException { - LogoutResponse response = new LogoutResponse(); - response.parse(getResource("logout_valid10.xml"), RetsVersion.RETS_10); - assertEquals("1000", response.getSeconds()); - assertEquals("$20.00", response.getBillingInfo()); - assertEquals("Good Bye", response.getLogoutMessage()); - } - - public void testValidLogoutResponse() throws RetsException { - LogoutResponse response = new LogoutResponse(); - response.parse(getResource("logout_valid15.xml"), RetsVersion.RETS_15); - assertEquals("1000", response.getSeconds()); - assertEquals("$20.00", response.getBillingInfo()); - assertEquals("Good Bye", response.getLogoutMessage()); - } - - public void testLowerCaseKeys() throws RetsException { - LogoutResponse response = new LogoutResponse(); - response.parse(getResource("logout_lower_case.xml"), RetsVersion.RETS_15); - assertEquals("1000", response.getSeconds()); - assertEquals("$20.00", response.getBillingInfo()); - assertEquals("Good Bye", response.getLogoutMessage()); - } - */ - public void testStrictLowerCaseKeys() { - try { - LogoutResponse response = new LogoutResponse(); - response.setStrict(true); - response.parse(getResource("logout_lower_case.xml"), RetsVersion.RETS_15); - - } catch (RetsException e) { - // Expected -// fail("Should have thrown exception"); - } - } - /* - * TODO: FIX THIS. - * - public void testLogoutNoEquals() throws RetsException { - LogoutResponse response = new LogoutResponse(); - response.parse(getResource("logout_no_equals.xml"), RetsVersion.RETS_15); - assertNull(response.getSeconds()); - assertNull(response.getBillingInfo()); - assertNull(response.getLogoutMessage()); - } - */ -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsGetObjectExample.java b/src/test/java/com/ossez/usreio/tests/client/RetsGetObjectExample.java deleted file mode 100644 index b3aa08c..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsGetObjectExample.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import com.ossez.usreio.client.CommonsHttpClient; -import com.ossez.usreio.client.GetObjectRequest; -import com.ossez.usreio.client.RetsException; -import com.ossez.usreio.client.RetsHttpClient; -import com.ossez.usreio.client.RetsSession; -import com.ossez.usreio.client.RetsVersion; -import com.ossez.usreio.client.SingleObjectResponse; - - -/** - * Simple Example performing a GetObject and iterating the results - * - */ -public class RetsGetObjectExample { - - - public static void main(String[] args) throws MalformedURLException { - - //Create a RetsHttpClient (other constructors provide configuration i.e. timeout, gzip capability) - RetsHttpClient httpClient = new CommonsHttpClient(); - RetsVersion retsVersion = RetsVersion.RETS_1_7_2; - String loginUrl = "http://theurloftheretsserver.com"; - - //Create a RetesSession with RetsHttpClient - RetsSession session = new RetsSession(loginUrl, httpClient, retsVersion); - - String username = "username"; - String password = "password"; - try { - //Login - session.login(username, password); - } catch (RetsException e) { - e.printStackTrace(); - } - - String sResource = "Property"; - String objType = "Photo"; - String seqNum = "*"; // * denotes get all pictures associated with id (from Rets Spec) - List idsList = Arrays.asList("331988","152305","243374"); - try { - //Create a GetObjectRequeset - GetObjectRequest req = new GetObjectRequest(sResource, objType); - - //Add the list of ids to request on (ids can be determined from records) - Iterator idsIter = idsList.iterator(); - while(idsIter.hasNext()) { - req.addObject(idsIter.next(), seqNum); - } - - //Execute the retrieval of objects - Iterator singleObjectResponseIter = session.getObject(req).iterator(); - - //Iterate over each Object - while (singleObjectResponseIter.hasNext()) { - SingleObjectResponse sor = (SingleObjectResponse)singleObjectResponseIter.next(); - - //Retrieve in info and print - String type = sor.getType(); - String contentID = sor.getContentID(); - String objectID = sor.getObjectID(); - String description = sor.getDescription(); - String location = sor.getLocation(); - InputStream is = sor.getInputStream(); - - System.out.print("type:" + type); - System.out.print(" ,contentID:" + contentID); - System.out.print(" ,objectID:" + objectID); - System.out.println(" ,description:" + description); - System.out.println("location:" + location); - - //Download object - try { - String dest = "/path/of/dowload/loaction"; - int size = is.available(); - String filename = dest + contentID +"-" + objectID + "." + type; - OutputStream out = new FileOutputStream(new File(filename)); - int read = 0; - byte[] bytes = new byte[1024]; - - while ((read = is.read(bytes)) != -1) { - - out.write(bytes, 0, read); - } - - is.close(); - out.flush(); - out.close(); - - System.out.println("New file with size " + size + " created: " + filename); - } catch (IOException e) { - System.out.println(e.getMessage()); - } - - } - - } catch (RetsException e) { - e.printStackTrace(); - } - finally { - if(session != null) { - try { - session.logout(); - } - catch (RetsException e) { - e.printStackTrace(); - } - } - } - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsGetObjectURLExample.java b/src/test/java/com/ossez/usreio/tests/client/RetsGetObjectURLExample.java deleted file mode 100644 index 5c1739f..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsGetObjectURLExample.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.net.MalformedURLException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import com.ossez.usreio.client.CommonsHttpClient; -import com.ossez.usreio.client.GetObjectRequest; -import com.ossez.usreio.client.RetsException; -import com.ossez.usreio.client.RetsHttpClient; -import com.ossez.usreio.client.RetsSession; -import com.ossez.usreio.client.RetsVersion; -import com.ossez.usreio.client.SingleObjectResponse; - - -/** - * Simple Example performing a GetObject and iterating the results - * - */ -public class RetsGetObjectURLExample { - - - public static void main(String[] args) throws MalformedURLException { - - //Create a RetsHttpClient (other constructors provide configuration i.e. timeout, gzip capability) - RetsHttpClient httpClient = new CommonsHttpClient(); - RetsVersion retsVersion = RetsVersion.RETS_1_7_2; - String loginUrl = "http://theurloftheretsserver.com"; - - //Create a RetesSession with RetsHttpClient - RetsSession session = new RetsSession(loginUrl, httpClient, retsVersion); - - String username = "username"; - String password = "password"; - try { - //Login - session.login(username, password); - } catch (RetsException e) { - e.printStackTrace(); - } - - String sResource = "Property"; - String objType = "Photo"; - String seqNum = "*"; // * denotes get all pictures associated with id (from Rets Spec) - boolean locationOnly = true; //InputStream not included in response for faster transmission - - List idsList = Arrays.asList("331988","152305","243374"); - try { - //Create a GetObjectRequeset - GetObjectRequest req = new GetObjectRequest(sResource, objType); - req.setLocationOnly(locationOnly); - //Add the list of ids to request on (ids can be determined from records) - Iterator idsIter = idsList.iterator(); - while(idsIter.hasNext()) { - req.addObject(idsIter.next(), seqNum); - } - - //Execute the retrieval of objects - Iterator singleObjectResponseIter = session.getObject(req).iterator(); - - //Iterate over each Object - while (singleObjectResponseIter.hasNext()) { - SingleObjectResponse sor = (SingleObjectResponse)singleObjectResponseIter.next(); - - //Retrieve in info and print - String type = sor.getType(); - String contentID = sor.getContentID(); - String objectID = sor.getObjectID(); - String description = sor.getDescription(); - String location = sor.getLocation(); - - System.out.print("type:" + type); - System.out.print(" ,contentID:" + contentID); - System.out.print(" ,objectID:" + objectID); - System.out.println(" ,description:" + description); - System.out.println("location:" + location); //location holds the URL string - } - - } catch (RetsException e) { - e.printStackTrace(); - } - finally { - if(session != null) { - try { - session.logout(); - } - catch (RetsException e) { - e.printStackTrace(); - } - } - } - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsMetadataTest.java b/src/test/java/com/ossez/usreio/tests/client/RetsMetadataTest.java deleted file mode 100644 index e8d9833..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsMetadataTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.io.InputStream; -import java.net.MalformedURLException; -import java.util.Properties; - -import com.ossez.usreio.client.retsapi.RETSConnection; -import com.ossez.usreio.client.retsapi.RETSGetMetadataTransaction; -import com.ossez.usreio.tests.common.metadata.types.MClass; -import com.ossez.usreio.tests.common.metadata.types.MResource; -import com.ossez.usreio.tests.common.metadata.types.MSystem; -import com.ossez.usreio.client.*; -import com.ossez.usreio.util.SessionUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; - -/** - * Simple Example performing a GetMetadata and iterating of the results - * - * @author YuCheng Hu - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class RetsMetadataTest extends RetsTestCase { - - @Test - public void testGetRetsMetadata() { - RetsSession session = null; - try { - - session = SessionUtils.retsLogin(retsLoginUrl, retsUsername, retsPassword, RetsVersion.RETS_1_7_2); - - MSystem system = session.getMetadata().getSystem(); - System.out.println( - "SYSTEM: " + system.getSystemID() + - " - " + system.getSystemDescription()); - - for (MResource resource : system.getMResources()) { - - System.out.println( - " RESOURCE: " + resource.getResourceID()); - - for (MClass classification : resource.getMClasses()) { - System.out.println( - " CLASS: " + classification.getClassName() + - " - " + classification.getDescription()); - } - } - } catch (RetsException e) { - e.printStackTrace(); - } finally { - if (session != null) { - try { - session.logout(); - } catch (RetsException e) { - e.printStackTrace(); - } - } - } - } - - /** - * Do RetsServerConnection Test - */ - @Test - public void testStaticVariableChange() { - - // BasicConfigurator.configure(); - - RETSConnection rc = new RETSConnection(); -// RETSLoginTransaction trans = new RETSLoginTransaction(); - RETSGetMetadataTransaction trans = new RETSGetMetadataTransaction(); - - - try { - Properties props = new Properties(); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - InputStream inputStream = loader.getResourceAsStream("rets.properties"); - - props.load(inputStream); - - // 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("rets_server")); -// trans.setUrl((String) props.getProperty("rets_server")); -// trans.setUsername((String) props.getProperty("rets_username")); -// trans.setPassword((String) props.getProperty("rets_password")); - } catch (Exception e) { - e.printStackTrace(); - } - rc.execute(trans); -// rc.execute(transaction); - -// transaction.getVersion(); - - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsSearchExample.java b/src/test/java/com/ossez/usreio/tests/client/RetsSearchExample.java deleted file mode 100644 index 8b7693f..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsSearchExample.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.net.MalformedURLException; - -import org.apache.commons.lang3.StringUtils; -import com.ossez.usreio.client.CommonsHttpClient; -import com.ossez.usreio.client.RetsException; -import com.ossez.usreio.client.RetsHttpClient; -import com.ossez.usreio.client.RetsSession; -import com.ossez.usreio.client.RetsVersion; -import com.ossez.usreio.client.SearchRequest; -import com.ossez.usreio.client.SearchResultImpl; - -/** - * Simple Example performing a search and iterating over the results - * - */ -public class RetsSearchExample { - - public static void main(String[] args) throws MalformedURLException { - - //Create a RetsHttpClient (other constructors provide configuration i.e. timeout, gzip capability) - RetsHttpClient httpClient = new CommonsHttpClient(); - RetsVersion retsVersion = RetsVersion.RETS_1_7_2; - String loginUrl = "http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/login"; - - //Create a RetesSession with RetsHttpClient - RetsSession session = new RetsSession(loginUrl, httpClient, retsVersion); - - String username = "prurets1"; - String password = "boyd070110"; - - //Set method as GET or POST - session.setMethod("POST"); - try { - //Login - session.login(username, password); - } catch (RetsException e) { - e.printStackTrace(); - } - - String sQuery = "(Member_num=.ANY.)"; - String sResource = "Property"; - String sClass = "Residential"; - - //Create a SearchRequest - SearchRequest request = new SearchRequest(sResource, sClass, sQuery); - - //Select only available fields - String select ="field1,field2,field3,field4,field5"; - request.setSelect(select); - - //Set request to retrive count if desired - request.setCountFirst(); - - SearchResultImpl response; - try { - //Execute the search - response= (SearchResultImpl) session.search(request); - - //Print out count and columns - int count = response.getCount(); - System.out.println("COUNT: " + count); - System.out.println("COLUMNS: " + StringUtils.join(response.getColumns(), "\t")); - - //Iterate over, print records - for (int row = 0; row < response.getRowCount(); row++){ - System.out.println("ROW"+ row +": " + StringUtils.join(response.getRow(row), "\t")); - } - } catch (RetsException e) { - e.printStackTrace(); - } - finally { - if(session != null) { - try { - session.logout(); - } - catch(RetsException e) { - e.printStackTrace(); - } - } - } - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsSessionTest.java b/src/test/java/com/ossez/usreio/tests/client/RetsSessionTest.java deleted file mode 100644 index be9839a..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsSessionTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.ossez.usreio.tests.client; - -import com.ossez.usreio.client.RetsException; -import com.ossez.usreio.client.RetsSession; -import com.ossez.usreio.client.RetsVersion; -import com.ossez.usreio.util.SessionUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * Test for RETS session - * - * @author YuCheng Hu - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class RetsSessionTest extends RetsTestCase { - private final Logger logger = LoggerFactory.getLogger(RetsSessionTest.class); - - - /** - * Test Login should return SessionID from server - */ - @Test - public void testLogin() { - logger.debug("Test Rets Session Login by URL: [{}]", retsLoginUrl); - - try { - RetsSession session = SessionUtils.retsLogin(retsLoginUrl, retsUsername, retsPassword, RetsVersion.RETS_1_7_2); - assertNotNull(session.getSessionId()); - } catch (RetsException ex) { - logger.debug("Session Login Error", ex); - } - - - } - - /** - * TEST Logout - */ - @Test - public void testLogout() { - logger.debug("RETS Session Login URL: [{}]", retsLoginUrl); - RetsSession session = null; - - try { - session = SessionUtils.retsLogin(retsLoginUrl, retsUsername, retsPassword, RetsVersion.RETS_1_7_2); - assertNotNull(session.getSessionId()); - } catch (RetsException ex) { - logger.debug("Session Login Error", ex); - } - - - // If Session is not Empty then Logout - if (ObjectUtils.isNotEmpty(session)) { - try { - session.logout(); - } catch (RetsException e) { - logger.error("Logout Error: ", e); - } - } - - } - -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsTestCase.java b/src/test/java/com/ossez/usreio/tests/client/RetsTestCase.java deleted file mode 100644 index 0c001f4..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsTestCase.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.ossez.usreio.tests.client; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.TestInstance; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.Properties; - - -/** - * @author YuCheng Hu - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public abstract class RetsTestCase { - - public Properties props = new Properties(); - public String retsLoginUrl; - public String retsUsername; - public String retsPassword; - - @BeforeAll - public void setUp() throws IOException { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - props.load(loader.getResourceAsStream("rets.properties")); - - retsLoginUrl = props.getProperty("rets_server"); - retsUsername = props.getProperty("rets_username"); - retsPassword = props.getProperty("rets_password"); - } - - /** - * Get Resource from file - * - * @param name - * @return - */ - protected static InputStream getResource(String name) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return cl.getResourceAsStream(name); - } - - /** - * @param urlStr - * @return - */ - protected static InputStream getResourceFromURL(String urlStr) { - - try { -// in = new URL( "" ).openStream(); - URL oracle = new URL("https://cdn.ossez.com/reso/rets-1x/login/login_response_valid_1.0.xml"); - BufferedReader in = new BufferedReader( - new InputStreamReader(oracle.openStream())); - - String inputLine; - while ((inputLine = in.readLine()) != null) - System.out.println(inputLine); - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - return null - ; - } - - public void assertEquals(String message, Object[] expected, Object[] actual) { - boolean success; - if (expected.length == actual.length) { - success = true; - for (int i = 0; i < expected.length; i++) { - success = true; - if (!expected[i].equals(actual[i])) { - success = false; - break; - } - } - } else { - success = false; - } - - } - -} diff --git a/src/test/java/com/ossez/usreio/tests/client/RetsVersionTest.java b/src/test/java/com/ossez/usreio/tests/client/RetsVersionTest.java deleted file mode 100644 index d139751..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/RetsVersionTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.ossez.usreio.tests.client; - -public class RetsVersionTest extends RetsTestCase { - - @SuppressWarnings("deprecation") - public void testEquals() { -// assertEquals("Checking 1.0", RetsVersion.RETS_10, new RetsVersion(1, 0)); -// -// assertEquals("Checking 1.5", RetsVersion.RETS_15, new RetsVersion(1, 5)); -// -// assertEquals("Checking 1.7", RetsVersion.RETS_17, new RetsVersion(1, 7)); -// -// assertEquals("Checking 1.7.2", RetsVersion.RETS_1_7_2, new RetsVersion(1, 7, 2, 0)); -// -// assertEquals("Checking revision support", RetsVersion.RETS_1_7_2, new RetsVersion(1, 7, 2, 0)); -// -// assertFalse("Checking draft support", RetsVersion.RETS_15.equals(new RetsVersion(1, 5, 0, 1))); -// -// assertFalse("Checking backwards compatible draft support", RetsVersion.RETS_15.equals(new RetsVersion(1, 5, 1))); - } - - @SuppressWarnings("deprecation") - public void testToString() { -// assertEquals("Checking toString() 1.0", "RETS/1.0", RetsVersion.RETS_10.toString()); -// assertEquals("Checking toString() 1.5", "RETS/1.5", RetsVersion.RETS_15.toString()); -// assertEquals("Checking toString() 1.7", "RETS/1.7", RetsVersion.RETS_17.toString()); -// assertEquals("Checking toString() 1.7.2", "RETS/1.7.2", RetsVersion.RETS_1_7_2.toString()); -// assertEquals("Checking toString() backward compatible draft without revision", "RETS/1.5d1", new RetsVersion(1, 5, 1).toString()); -// assertEquals("Checking toString() revision with draft", "RETS/1.7.2d1", new RetsVersion(1, 7, 2, 1).toString()); - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/SearchResultHandlerTest.java b/src/test/java/com/ossez/usreio/tests/client/SearchResultHandlerTest.java deleted file mode 100644 index d6a0485..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/SearchResultHandlerTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.client; - -import java.io.StringReader; - -import com.ossez.usreio.client.*; -import org.xml.sax.InputSource; - -/** - * TODO refactor this and the StreamingSearchResultsProcessorTest. - * - * dbt is lame and hasn't overridden the default - * javadoc string. - */ -public class SearchResultHandlerTest extends RetsTestCase { - SearchResult runSearchTest(String input) throws RetsException { - return runSearchTest(input, InvalidReplyCodeHandler.FAIL); - } - - SearchResult runSearchTest(String input, InvalidReplyCodeHandler invalidReplyCodeHandler) throws RetsException { - SearchResultImpl res = new SearchResultImpl(); - SearchResultHandler h = new SearchResultHandler(res, invalidReplyCodeHandler, CompactRowPolicy.DEFAULT); - InputSource source = new InputSource(new StringReader(input)); - h.parse(source); - return res; - } - - public void testSmallResult() throws RetsException { -// SearchResult result = runSearchTest(GOOD_SMALL_TEST); -// assertTrue("search not complete", result.isComplete()); -// String[] columns = result.getColumns(); -// assertNotNull(columns); -// assertEquals("column headers count wrong", 1, columns.length); -// assertEquals("bad column header", "Column1", columns[0]); -// assertEquals("wrong row count", 1, result.getCount()); -// String[] row = result.getRow(0); -// assertEquals("wrong row width", 1, row.length); -// assertEquals("wrong row data", "Data1", row[0]); -// assertFalse("max rows wrong", result.isMaxrows()); - } - - public void testAllTags() throws RetsException { -// SearchResult result = runSearchTest(ALL_TAGS_TEST); -// assertTrue("search not complete", result.isComplete()); -// assertEquals("extended count wrong", 100, result.getCount()); -// assertTrue("max rows not set", result.isMaxrows()); -// String[] row = result.getRow(0); -// assertNotNull("row 0 is null", row); -// assertEquals("wrong number of row[0] elements", 1, row.length); -// assertEquals("wrong row[0] data", "Data1", row[0]); -// row = result.getRow(1); -// assertNotNull("row 1 is null", row); -// assertEquals("wrong number of row[1] elements", 1, row.length); -// assertEquals("wrong row[1] data", "Data2", row[0]); - } - - public void testReplyCode20208() throws RetsException { -// SearchResult result = runSearchTest(MAXROWS_REPLYCODE); -// assertTrue("search not complete", result.isComplete()); -// assertEquals("extended count wrong", 100, result.getCount()); -// assertTrue("max rows not set", result.isMaxrows()); -// String[] row = result.getRow(0); -// assertNotNull("row 0 is null", row); -// assertEquals("wrong number of row[0] elements", 1, row.length); -// assertEquals("wrong row[0] data", "Data1", row[0]); -// row = result.getRow(1); -// assertNotNull("row 1 is null", row); -// assertEquals("wrong number of row[1] elements", 1, row.length); -// assertEquals("wrong row[1] data", "Data2", row[0]); - } - - public void testReplyCode20201WithColumns() throws RetsException { - SearchResult result = runSearchTest(EMPTY_REPLYCODE_WITH_COLUMNS_TAG); -// assertFalse("iterator should be empty", result.iterator().hasNext()); - } - - public void testReplyCode20201WithoutColumns() throws RetsException { - SearchResult result = runSearchTest(EMPTY_REPLYCODE); -// assertFalse("iterator should be empty", result.iterator().hasNext()); - } - - public void testEarlyException() throws RetsException { - try { - runSearchTest(EARLY_ERROR_TEST); -// fail("Expected an InvalidReplyCodeException"); - } catch (InvalidReplyCodeException e) { - // "success" - } - } - - public void testLateException() throws RetsException { - try { - runSearchTest(LATE_ERROR_TEST); -// fail("Expected an Invalid ReplyCodeException"); - } catch (InvalidReplyCodeException e) { - // "success" - } - } - - public void testEarlyExceptionWithTrap() throws RetsException { - try { - runSearchTest(EARLY_ERROR_TEST, new TestInvalidReplyCodeHandler()); -// fail("Expected an InvalidReplyCodeException"); - } catch (InvalidReplyCodeException e) { - // "success" - } - } - - public void testLateExceptionWithTrap() throws RetsException { - TestInvalidReplyCodeHandler testInvalidReplyCodeHandler = new TestInvalidReplyCodeHandler(); - runSearchTest(LATE_ERROR_TEST, testInvalidReplyCodeHandler); -// assertEquals(LATE_ERROR_CODE, testInvalidReplyCodeHandler.getReplyCode()); - } - - public static final String CRLF = "\r\n"; - - public static final String GOOD_SMALL_TEST = "" + CRLF - + "" + CRLF + "\tColumn1\t" + CRLF + "\tData1\t" - + CRLF + "" + CRLF; - - public static final String ALL_TAGS_TEST = "" + CRLF - + "" + CRLF + "" + CRLF + "\tColumn1\t" - + CRLF + "\tData1\t" + CRLF + "\tData2\t" + CRLF + "" + "" - + CRLF; - - public static final String EARLY_ERROR_TEST = "" - + CRLF + "" + CRLF; - - public static final int LATE_ERROR_CODE = 20203; - - public static final String LATE_ERROR_TEST = "" + CRLF - + "" + CRLF + "" + CRLF + "\tColumn1\t" - + CRLF + "\tData1\t" + CRLF + "\tData2\t" + CRLF + "" + "" + CRLF; - - public static final String MAXROWS_REPLYCODE = "" + CRLF - + "" + CRLF + "" + CRLF + "\tColumn1\t" - + CRLF + "\tData1\t" + CRLF + "\tData2\t" + CRLF + "" + "" - + CRLF; - - public static final String EMPTY_REPLYCODE = "" - + CRLF + "" + CRLF; - - public static final String EMPTY_REPLYCODE_WITH_COLUMNS_TAG = "" + CRLF + "" + CRLF - + "\tColumn1\t" + CRLF + "" + CRLF; -} diff --git a/src/test/java/com/ossez/usreio/tests/client/SearchResultImplTest.java b/src/test/java/com/ossez/usreio/tests/client/SearchResultImplTest.java deleted file mode 100644 index 2c26a41..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/SearchResultImplTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * cart: CRT's Awesome RETS Tool - * - * Author: David Terrell - * Copyright (c) 2003, The National Association of REALTORS - * Distributed under a BSD-style license. See LICENSE.TXT for details. - */ -package com.ossez.usreio.tests.client; - -import com.ossez.usreio.client.SearchResultImpl; - -import java.util.NoSuchElementException; - -/** - * dbt is lame and hasn't overridden the default - * javadoc string. - */ -public class SearchResultImplTest extends RetsTestCase { - public void testSearchResult() { - String[] cols = { "Column1", "Column2" }; - String[] row1 = { "Data1x1", "Data1x2" }; - String[] row2 = { "Data2x1", "Data2x2" }; - String[] row2alt = { "", "" }; - row2alt[0] = row2[0]; - row2alt[1] = row2[1]; - SearchResultImpl result = new SearchResultImpl(); - result.setCount(5); - result.setColumns(cols); - result.addRow(row1); - result.addRow(row2); - result.setMaxrows(); - result.setComplete(); -// assertEquals("setCount wrong", result.getCount(), 5); -// assertTrue("isComplete not set", result.isComplete()); -// assertTrue("isMaxrows not set", result.isMaxrows()); -// assertEquals("columns mangled", cols, result.getColumns()); -// assertEquals("row 1 mangled", row1, result.getRow(0)); -// assertEquals("row 2 mangled", row2alt, result.getRow(1)); - try { - result.getRow(2); -// fail("getting invalid row 2 should have thrown " + "NoSuchElementException"); - } catch (NoSuchElementException e) { - // "success" - } - } - - public void testMinimumSearchResult() { - String[] cols = { "col1" }; - String[] row = { "row1" }; - SearchResultImpl result = new SearchResultImpl(); - result.setColumns(cols); - result.addRow(row); - result.setComplete(); -// assertEquals("row count wrong", 1, result.getCount()); -// assertTrue("isComplete wrong", result.isComplete()); -// assertFalse("isMaxrows wrong", result.isMaxrows()); - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/SingleObjectResponseTest.java b/src/test/java/com/ossez/usreio/tests/client/SingleObjectResponseTest.java deleted file mode 100644 index ade2716..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/SingleObjectResponseTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.util.HashMap; -import java.util.Map; - -import com.ossez.usreio.client.SingleObjectResponse; -import junit.framework.TestCase; - -public class SingleObjectResponseTest extends TestCase { - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - public void testCaseInsensitiveHeaders() throws Exception { - Map headers = new HashMap(); - headers.put("Content-type", "1"); - headers.put("location", "2"); - headers.put("Object-Id", "3"); - headers.put("content-id", "4"); - headers.put("CONTENT-DESCRIPTION", "5"); - - SingleObjectResponse res = new SingleObjectResponse(headers, null); - assertEquals("1", res.getType()); - assertEquals("2", res.getLocation()); - assertEquals("3", res.getObjectID()); - assertEquals("4", res.getContentID()); - assertEquals("5", res.getDescription()); - } - -} diff --git a/src/test/java/com/ossez/usreio/tests/client/StreamingSearchResultProcessorTest.java b/src/test/java/com/ossez/usreio/tests/client/StreamingSearchResultProcessorTest.java deleted file mode 100644 index eff53de..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/StreamingSearchResultProcessorTest.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.ossez.usreio.tests.client; - -import java.io.Reader; -import java.io.StringReader; - -import com.ossez.usreio.client.*; -import junit.framework.TestCase; - -public class StreamingSearchResultProcessorTest extends TestCase { - protected SearchResultProcessor createProcessor(InvalidReplyCodeHandler invalidReplyCodeHandler) { - StreamingSearchResultProcessor streamingSearchResultProcessor = new StreamingSearchResultProcessor(1, 0); - if (invalidReplyCodeHandler != null) - streamingSearchResultProcessor.setInvalidRelyCodeHandler(invalidReplyCodeHandler); - return streamingSearchResultProcessor; - } - - SearchResultSet runSearchTest(String input) throws RetsException { - return runSearchTest(input, InvalidReplyCodeHandler.FAIL); - } - - SearchResultSet runSearchTest(String input, InvalidReplyCodeHandler invalidReplyCodeHandler) throws RetsException { - SearchResultProcessor processor = createProcessor(invalidReplyCodeHandler); - Reader source = new StringReader(input); - return processor.parse(source); - } - - public void testSmallResult() throws RetsException { - SearchResultSet result = runSearchTest(SearchResultHandlerTest.GOOD_SMALL_TEST); - String[] columns = result.getColumns(); - assertNotNull(columns); - assertEquals("column headers count wrong", 1, columns.length); - assertEquals("bad column header", "Column1", columns[0]); - - if (result.getCount() != -1) - assertEquals("wrong row count", 1, result.getCount()); - - assertTrue("iterator should have more", result.hasNext()); - String[] row = result.next(); - - assertEquals("wrong row width", 1, row.length); - assertEquals("wrong row data", "Data1", row[0]); - - assertFalse("rows should be exhausted", result.hasNext()); - assertFalse("max rows wrong", result.isMaxrows()); - assertTrue("search not complete", result.isComplete()); - } - - public void testEarlyCallToIsMaxRows() throws RetsException { - SearchResultSet result = runSearchTest(SearchResultHandlerTest.ALL_TAGS_TEST); - try { - result.isMaxrows(); - fail("Should throw illegal state exception"); - } catch (IllegalStateException e) { - // "success" - } - } - - public void testAllTags() throws RetsException { - SearchResultSet result = runSearchTest(SearchResultHandlerTest.ALL_TAGS_TEST); - assertEquals("extended count wrong", 100, result.getCount()); - - assertTrue("iterator should have more", result.hasNext()); - String[] row = result.next(); - assertNotNull("row 0 is null", row); - assertEquals("wrong number of row[0] elements", 1, row.length); - assertEquals("wrong row[0] data", "Data1", row[0]); - - assertTrue("iterator should have more", result.hasNext()); - row = result.next(); - assertNotNull("row 1 is null", row); - assertEquals("wrong number of row[1] elements", 1, row.length); - assertEquals("wrong row[1] data", "Data2", row[0]); - - assertFalse("rows should be exhausted", result.hasNext()); - assertTrue("search not complete", result.isComplete()); - assertTrue("max rows not set", result.isMaxrows()); - } - - public void testReplyCode20208() throws RetsException { - SearchResultSet result = runSearchTest(SearchResultHandlerTest.MAXROWS_REPLYCODE); - assertEquals("extended count wrong", 100, result.getCount()); - - assertTrue("iterator should have more", result.hasNext()); - String[] row = result.next(); - assertNotNull("row 0 is null", row); - assertEquals("wrong number of row[0] elements", 1, row.length); - assertEquals("wrong row[0] data", "Data1", row[0]); - - assertTrue("iterator should have more", result.hasNext()); - row = result.next(); - assertNotNull("row 1 is null", row); - assertEquals("wrong number of row[1] elements", 1, row.length); - assertEquals("wrong row[1] data", "Data2", row[0]); - - assertFalse("rows should be exhausted", result.hasNext()); - assertTrue("search not complete", result.isComplete()); - assertTrue("max rows not set", result.isMaxrows()); - } - - public void testReplyCode20201WithColumns() throws RetsException { - SearchResultSet result = runSearchTest(SearchResultHandlerTest.EMPTY_REPLYCODE_WITH_COLUMNS_TAG); - assertFalse("iterator should be empty", result.hasNext()); - } - - public void testReplyCode20201WithoutColumns() throws RetsException { - SearchResultSet result = runSearchTest(SearchResultHandlerTest.EMPTY_REPLYCODE); - assertFalse("iterator should be empty", result.hasNext()); - } - - public void testEarlyException() throws RetsException { - try { - // Test now checks that the error is thrown at process - // or during the evaluation of the data rows, since the - // result may be lazily evaluated (streaming) - SearchResultSet result = runSearchTest(SearchResultHandlerTest.EARLY_ERROR_TEST); - while (result.hasNext()) - result.next(); - fail("Expected an Invalid ReplyCodeException"); - } catch (InvalidReplyCodeException e) { - // "success" - } - } - - public void testLateException() throws RetsException { - try { - // Test now checks that the error is thrown at process - // or during the evaluation of the data rows, since the - // result may be lazily evaluated (streaming) - SearchResultSet result = runSearchTest(SearchResultHandlerTest.LATE_ERROR_TEST); - while (result.hasNext()) - result.next(); - fail("Expected an Invalid ReplyCodeException"); - } catch (InvalidReplyCodeException e) { - // "success" - } - } - - public void testEarlyExceptionWithTrap() throws RetsException { - try { - // Test now checks that the error is thrown at process - // or during the evaluation of the data rows, since the - // result may be lazily evaluated (streaming) - SearchResultSet result = runSearchTest(SearchResultHandlerTest.EARLY_ERROR_TEST, - new TestInvalidReplyCodeHandler()); - while (result.hasNext()) - result.next(); - fail("Expected an Invalid ReplyCodeException"); - } catch (InvalidReplyCodeException e) { - // "success" - } - } - - public void testLateExceptionWithTrap() throws RetsException { - // Test now checks that the error is thrown at process - // or during the evaluation of the data rows, since the - // result may be lazily evaluated (streaming) - TestInvalidReplyCodeHandler testInvalidReplyCodeHandler = new TestInvalidReplyCodeHandler(); - SearchResultSet result = runSearchTest(SearchResultHandlerTest.LATE_ERROR_TEST, testInvalidReplyCodeHandler); - while (result.hasNext()) - result.next(); - - assertEquals(SearchResultHandlerTest.LATE_ERROR_CODE, testInvalidReplyCodeHandler.getReplyCode()); - } - - public void testTimeout() throws Exception { - int timeout = 100; - SearchResultProcessor processor = new StreamingSearchResultProcessor(1, timeout); - Reader source = new StringReader(SearchResultHandlerTest.ALL_TAGS_TEST); - SearchResultSet result = processor.parse(source); - - try { - // attempt to force timeout to occur - Thread.sleep(timeout * 10); - // hasNext should fail b/c timeout - // will have occurred - result.hasNext(); - fail("Should fail since timeout should have been reached"); - } catch (RetsException e) { - // success - } - } - - public void testIONotEatenException() throws RetsException { - SearchResultProcessor processor = new StreamingSearchResultProcessor(100); - - IOFailReader ioExceptionStream = new IOFailReader(new StringReader(SearchResultHandlerTest.ALL_TAGS_TEST)); - ioExceptionStream.setFailRead(true); - - SearchResultSet resultSet = processor.parse(ioExceptionStream); - - try { - while (resultSet.hasNext()) - resultSet.next(); - fail("Expection an IOException to be thrown during stream reading."); - } catch (RetsException e) { - e.printStackTrace(); - assertNotNull(e); - } - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/TestInvalidReplyCodeHandler.java b/src/test/java/com/ossez/usreio/tests/client/TestInvalidReplyCodeHandler.java deleted file mode 100644 index 3406db5..0000000 --- a/src/test/java/com/ossez/usreio/tests/client/TestInvalidReplyCodeHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ossez.usreio.tests.client; - -import com.ossez.usreio.client.InvalidReplyCodeException; -import com.ossez.usreio.client.InvalidReplyCodeHandler; - -final class TestInvalidReplyCodeHandler implements InvalidReplyCodeHandler { - private int replyCode; - - public void invalidRetsReplyCode(int code) throws InvalidReplyCodeException { - throw new InvalidReplyCodeException(code); - } - - public void invalidRetsStatusReplyCode(int code) throws InvalidReplyCodeException { - this.replyCode = code; - } - - public int getReplyCode() { - return this.replyCode; - } -} diff --git a/src/test/java/com/ossez/usreio/tests/client/objects-missing.multipart b/src/test/java/com/ossez/usreio/tests/client/objects-missing.multipart deleted file mode 100644 index d3baaf3771a8b646507c5a7290fd88cfbf55bdc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 105875 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 N|D+;Zw@y^({{Wx - - LookupName VisibleName Version Date - E_SCHOOL Elementary School District 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - H_SCHOOL High School District 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - AR Area 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - IFT Interior Features 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - VEW View 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - LISTING_TYPE Listing Type 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - LISTING_STATUS Listing Status 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - M_SCHOOL Middle School District 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - EFT Exerior Features 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - - - LookupName VisibleName Version Date - DESIGNATIONS DESIGNATIONS 1.00.000 Sat, 20 Mar 2002 12:03:38 GMT - - diff --git a/src/test/resources/getMetadataResponse_noRecords.xml b/src/test/resources/getMetadataResponse_noRecords.xml deleted file mode 100644 index 6596f33..0000000 --- a/src/test/resources/getMetadataResponse_noRecords.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/test/resources/getMetadataResponse_system.xml b/src/test/resources/getMetadataResponse_system.xml deleted file mode 100644 index 5ad7b2b..0000000 --- a/src/test/resources/getMetadataResponse_system.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - -The reference implementation of a RETS Server - - diff --git a/src/test/resources/getMetadataResponse_updateType.xml b/src/test/resources/getMetadataResponse_updateType.xml deleted file mode 100644 index 346b9c8..0000000 --- a/src/test/resources/getMetadataResponse_updateType.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - SystemName Sequence Attributes Default ValidationExpressionID UpdateHelpID ValidationLookupName ValidationExternalName - AGENT_ID 1 1 0 - OFFICE_ID 2 2 0 - - diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml deleted file mode 100644 index ff28157..0000000 --- a/src/test/resources/log4j2.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - /home/logs/reoc/services/ - - - - - - - - - - - - - - - - - - %-d{yyyy/MM/dd HH:mm:ss} %-5p [%c] - %m%n - - - - - - - - - - - - - - - %-d{yyyy/MM/dd HH:mm:ss} %-5p [%c] - %m%n - - - - - - - - - - - - - - - %-d{yyyy/MM/dd HH:mm:ss} %-5p [%c] - %m%n - - - - - - - - - - - - - - - %-d{yyyy/MM/dd HH:mm:ss} %-5p [%c] - %m%n - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/login_lower_case.xml b/src/test/resources/login_lower_case.xml deleted file mode 100644 index c73ad68..0000000 --- a/src/test/resources/login_lower_case.xml +++ /dev/null @@ -1,17 +0,0 @@ - - -broker = B123, BO987 -membername = Joe T. Schmoe -metadataversion = 1.00.000 -minmetadataversion = 1.00.000 -user = A123,5678,1,A123 -login = http://rets.test:6103/login -logout = http://rets.test:6103/logout -search = http://rets.test:6103/search -getmetadata = http://rets.test:6103/getMetadata -changepassword = http://rets.test:6103/changePassword -getobject = http://rets.test:6103/getObjectEx -action = http://rets.test:6103/get -balance = 44.21 -timeoutseconds = 60 - diff --git a/src/test/resources/login_response_valid_1.0.xml b/src/test/resources/login_response_valid_1.0.xml deleted file mode 100644 index bb2cb2e..0000000 --- a/src/test/resources/login_response_valid_1.0.xml +++ /dev/null @@ -1,16 +0,0 @@ - - Broker = B123, BO987 - MemberName = Joe T. Schmoe - MetadataVersion = 1.00.000 - MinMetadataVersion = 1.00.000 - User = A123,5678,1,A123 - Login = http://rets.test:6103/login - Logout = http://rets.test:6103/logout - Search = http://rets.test:6103/search - GetMetadata = http://rets.test:6103/getMetadata - ChangePassword = http://rets.test:6103/changePassword - GetObject = http://rets.test:6103/getObjectEx - Action = http://rets.test:6103/get - Balance = 44.21 - TimeoutSeconds = 60 - diff --git a/src/test/resources/login_response_valid_1.5.xml b/src/test/resources/login_response_valid_1.5.xml deleted file mode 100644 index 200cbdd..0000000 --- a/src/test/resources/login_response_valid_1.5.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - Broker = B123, BO987 - MemberName = Joe T. Schmoe - MetadataVersion = 1.00.000 - MinMetadataVersion = 1.00.000 - User = A123,5678,1,A123 - Login = http://rets.test:6103/login - Logout = http://rets.test:6103/logout - Search = http://rets.test:6103/search - GetMetadata = http://rets.test:6103/getMetadata - ChangePassword = http://rets.test:6103/changePassword - GetObject = http://rets.test:6103/getObjectEx - Action = http://rets.test:6103/get - Balance = 44.21 - TimeoutSeconds = 60 - - diff --git a/src/test/resources/login_response_valid_1.7.xml b/src/test/resources/login_response_valid_1.7.xml deleted file mode 100644 index 41b86e9..0000000 --- a/src/test/resources/login_response_valid_1.7.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - MemberName=BHHS Verani IDX RETS User - User=test,1,21,279117 - Broker=4935,4935 - MetadataVersion=19.9.17332 - MetadataTimestamp=2019-09-12T00:52:11.2Z - MinMetadataTimestamp=2019-09-12T00:52:11.2Z - TimeoutSeconds=7200 - GetMetadata=/rets/fnisrets.aspx/NEREN/getmetadata - GetObject=/rets/fnisrets.aspx/NEREN/getobject - Login=http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/login - Logout=/rets/fnisrets.aspx/NEREN/logout - Search=/rets/fnisrets.aspx/NEREN/search - - diff --git a/src/test/resources/login_response_valid_1.8.xml b/src/test/resources/login_response_valid_1.8.xml deleted file mode 100644 index 41b86e9..0000000 --- a/src/test/resources/login_response_valid_1.8.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - MemberName=BHHS Verani IDX RETS User - User=test,1,21,279117 - Broker=4935,4935 - MetadataVersion=19.9.17332 - MetadataTimestamp=2019-09-12T00:52:11.2Z - MinMetadataTimestamp=2019-09-12T00:52:11.2Z - TimeoutSeconds=7200 - GetMetadata=/rets/fnisrets.aspx/NEREN/getmetadata - GetObject=/rets/fnisrets.aspx/NEREN/getobject - Login=http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/login - Logout=/rets/fnisrets.aspx/NEREN/logout - Search=/rets/fnisrets.aspx/NEREN/search - - diff --git a/src/test/resources/login_response_valid_1.9.xml b/src/test/resources/login_response_valid_1.9.xml deleted file mode 100644 index 41b86e9..0000000 --- a/src/test/resources/login_response_valid_1.9.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - MemberName=BHHS Verani IDX RETS User - User=test,1,21,279117 - Broker=4935,4935 - MetadataVersion=19.9.17332 - MetadataTimestamp=2019-09-12T00:52:11.2Z - MinMetadataTimestamp=2019-09-12T00:52:11.2Z - TimeoutSeconds=7200 - GetMetadata=/rets/fnisrets.aspx/NEREN/getmetadata - GetObject=/rets/fnisrets.aspx/NEREN/getobject - Login=http://neren.rets.paragonrels.com/rets/fnisrets.aspx/NEREN/login - Logout=/rets/fnisrets.aspx/NEREN/logout - Search=/rets/fnisrets.aspx/NEREN/search - - diff --git a/src/test/resources/login_valid10.xml b/src/test/resources/login_valid10.xml deleted file mode 100644 index 3c1b834..0000000 --- a/src/test/resources/login_valid10.xml +++ /dev/null @@ -1,16 +0,0 @@ - -Broker = B123, BO987 -MemberName = Joe T. Schmoe -MetadataVersion = 1.00.000 -MinMetadataVersion = 1.00.000 -User = A123,5678,1,A123 -Login = http://rets.test:6103/login -Logout = http://rets.test:6103/logout -Search = http://rets.test:6103/search -GetMetadata = http://rets.test:6103/getMetadata -ChangePassword = http://rets.test:6103/changePassword -GetObject = http://rets.test:6103/getObjectEx -Action = http://rets.test:6103/get -Balance = 44.21 -TimeoutSeconds = 60 - diff --git a/src/test/resources/login_valid15.xml b/src/test/resources/login_valid15.xml deleted file mode 100644 index b2d3ea6..0000000 --- a/src/test/resources/login_valid15.xml +++ /dev/null @@ -1,17 +0,0 @@ - - -Broker = B123, BO987 -MemberName = Joe T. Schmoe -MetadataVersion = 1.00.000 -MinMetadataVersion = 1.00.000 -User = A123,5678,1,A123 -Login = http://rets.test:6103/login -Logout = http://rets.test:6103/logout -Search = http://rets.test:6103/search -GetMetadata = http://rets.test:6103/getMetadata -ChangePassword = http://rets.test:6103/changePassword -GetObject = http://rets.test:6103/getObjectEx -Action = http://rets.test:6103/get -Balance = 44.21 -TimeoutSeconds = 60 - diff --git a/src/test/resources/logout_lower_case.xml b/src/test/resources/logout_lower_case.xml deleted file mode 100644 index cacd066..0000000 --- a/src/test/resources/logout_lower_case.xml +++ /dev/null @@ -1,6 +0,0 @@ - - -connecttime = 1000 -billing = $20.00 -signoffmessage = Good Bye - diff --git a/src/test/resources/logout_no_equals.xml b/src/test/resources/logout_no_equals.xml deleted file mode 100644 index 765386f..0000000 --- a/src/test/resources/logout_no_equals.xml +++ /dev/null @@ -1,4 +0,0 @@ - - -Logged Out - diff --git a/src/test/resources/logout_valid10.xml b/src/test/resources/logout_valid10.xml deleted file mode 100644 index e3bf94a..0000000 --- a/src/test/resources/logout_valid10.xml +++ /dev/null @@ -1,5 +0,0 @@ - -ConnectTime = 1000 -Billing = $20.00 -SignOffMessage = Good Bye - diff --git a/src/test/resources/logout_valid15.xml b/src/test/resources/logout_valid15.xml deleted file mode 100644 index dd8864e..0000000 --- a/src/test/resources/logout_valid15.xml +++ /dev/null @@ -1,6 +0,0 @@ - - -ConnectTime = 1000 -Billing = $20.00 -SignOffMessage = Good Bye - diff --git a/src/test/resources/objects-missing.multipart b/src/test/resources/objects-missing.multipart deleted file mode 100644 index d3baaf3771a8b646507c5a7290fd88cfbf55bdc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 105875 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