From 54b1b6099de5285d2faae6d40c1d9a7e9c12c529 Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Mon, 24 Jun 2013 14:48:03 +0530 Subject: [PATCH 1/8] Removed apache dependency and moved apache Base64 classes to core module, changed servlet-api dependency to provided scope --- pom.xml | 8 +- .../com/paypal/core/codec/BinaryDecoder.java | 42 + .../com/paypal/core/codec/BinaryEncoder.java | 42 + .../com/paypal/core/codec/CharEncoding.java | 126 +++ .../java/com/paypal/core/codec/Decoder.java | 55 ++ .../paypal/core/codec/DecoderException.java | 89 ++ .../java/com/paypal/core/codec/Encoder.java | 46 ++ .../paypal/core/codec/EncoderException.java | 90 +++ .../com/paypal/core/codec/binary/Base64.java | 758 ++++++++++++++++++ .../paypal/core/codec/binary/BaseNCodec.java | 444 ++++++++++ .../paypal/core/codec/binary/StringUtils.java | 286 +++++++ .../core/rest/OAuthTokenCredential.java | 2 +- .../paypal/core/rest/RESTConfiguration.java | 2 +- .../com/paypal/sdk/util/OAuthSignature.java | 2 +- 14 files changed, 1983 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/paypal/core/codec/BinaryDecoder.java create mode 100644 src/main/java/com/paypal/core/codec/BinaryEncoder.java create mode 100644 src/main/java/com/paypal/core/codec/CharEncoding.java create mode 100644 src/main/java/com/paypal/core/codec/Decoder.java create mode 100644 src/main/java/com/paypal/core/codec/DecoderException.java create mode 100644 src/main/java/com/paypal/core/codec/Encoder.java create mode 100644 src/main/java/com/paypal/core/codec/EncoderException.java create mode 100644 src/main/java/com/paypal/core/codec/binary/Base64.java create mode 100644 src/main/java/com/paypal/core/codec/binary/BaseNCodec.java create mode 100644 src/main/java/com/paypal/core/codec/binary/StringUtils.java diff --git a/pom.xml b/pom.xml index 8c75559..18f02c8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.paypal.sdk paypal-core - 1.4.3 + 1.4.4 jar paypal-core PayPal Java SDK Core library base and common to PayPal SDKs. The paypal-core library is a dependency for all PayPal related Java SDKs @@ -28,11 +28,6 @@ UTF-8 - - commons-codec - commons-codec - 1.6 - org.testng testng @@ -43,6 +38,7 @@ javax.servlet servlet-api 2.5 + provided com.google.code.gson diff --git a/src/main/java/com/paypal/core/codec/BinaryDecoder.java b/src/main/java/com/paypal/core/codec/BinaryDecoder.java new file mode 100644 index 0000000..1a372b5 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/BinaryDecoder.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + * Defines common decoding methods for byte array decoders. + * + * @author Apache Software Foundation + * @version $Id: BinaryDecoder.java 1157192 2011-08-12 17:27:38Z ggregory $ + */ +public interface BinaryDecoder extends Decoder { + + /** + * Decodes a byte array and returns the results as a byte array. + * + * @param source A byte array which has been encoded with the + * appropriate encoder + * + * @return a byte array that contains decoded content + * + * @throws DecoderException A decoder exception is thrown + * if a Decoder encounters a failure condition during + * the decode process. + */ + byte[] decode(byte[] source) throws DecoderException; +} + diff --git a/src/main/java/com/paypal/core/codec/BinaryEncoder.java b/src/main/java/com/paypal/core/codec/BinaryEncoder.java new file mode 100644 index 0000000..d6b5b5b --- /dev/null +++ b/src/main/java/com/paypal/core/codec/BinaryEncoder.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + * Defines common encoding methods for byte array encoders. + * + * @author Apache Software Foundation + * @version $Id: BinaryEncoder.java 1157192 2011-08-12 17:27:38Z ggregory $ + */ +public interface BinaryEncoder extends Encoder { + + /** + * Encodes a byte array and return the encoded data + * as a byte array. + * + * @param source Data to be encoded + * + * @return A byte array containing the encoded data + * + * @throws EncoderException thrown if the Encoder + * encounters a failure condition during the + * encoding process. + */ + byte[] encode(byte[] source) throws EncoderException; +} + diff --git a/src/main/java/com/paypal/core/codec/CharEncoding.java b/src/main/java/com/paypal/core/codec/CharEncoding.java new file mode 100644 index 0000000..7e39576 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/CharEncoding.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + * Character encoding names required of every implementation of the Java platform. + * + * From the Java documentation Standard + * charsets: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

+ * + *
    + *
  • US-ASCII
    + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • + *
  • ISO-8859-1
    + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • + *
  • UTF-8
    + * Eight-bit Unicode Transformation Format.
  • + *
  • UTF-16BE
    + * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • + *
  • UTF-16LE
    + * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • + *
  • UTF-16
    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)
  • + *
+ * + * This perhaps would best belong in the [lang] project. Even if a similar interface is defined in [lang], it is not + * foreseen that [codec] would be made to depend on [lang]. + * + * @see Standard charsets + * @author Apache Software Foundation + * @since 1.4 + * @version $Id: CharEncoding.java 1170351 2011-09-13 21:09:09Z ggregory $ + */ +public class CharEncoding { + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + *

+ * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String US_ASCII = "US-ASCII"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16 = "UTF-16"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + *

+ * Eight-bit Unicode Transformation Format. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_8 = "UTF-8"; +} \ No newline at end of file diff --git a/src/main/java/com/paypal/core/codec/Decoder.java b/src/main/java/com/paypal/core/codec/Decoder.java new file mode 100644 index 0000000..3d9f6e9 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/Decoder.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + *

Provides the highest level of abstraction for Decoders. + * This is the sister interface of {@link Encoder}. All + * Decoders implement this common generic interface.

+ * + *

Allows a user to pass a generic Object to any Decoder + * implementation in the codec package.

+ * + *

One of the two interfaces at the center of the codec package.

+ * + * @author Apache Software Foundation + * @version $Id: Decoder.java 1157192 2011-08-12 17:27:38Z ggregory $ + */ +public interface Decoder { + + /** + * Decodes an "encoded" Object and returns a "decoded" + * Object. Note that the implementation of this + * interface will try to cast the Object parameter + * to the specific type expected by a particular Decoder + * implementation. If a {@link ClassCastException} occurs + * this decode method will throw a DecoderException. + * + * @param source the object to decode + * + * @return a 'decoded" object + * + * @throws DecoderException a decoder exception can + * be thrown for any number of reasons. Some good + * candidates are that the parameter passed to this + * method is null, a param cannot be cast to the + * appropriate type for a specific encoder. + */ + Object decode(Object source) throws DecoderException; +} + diff --git a/src/main/java/com/paypal/core/codec/DecoderException.java b/src/main/java/com/paypal/core/codec/DecoderException.java new file mode 100644 index 0000000..dea4703 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/DecoderException.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + * Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder} + * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. + * + * @author Apache Software Foundation + * @version $Id: DecoderException.java 1157192 2011-08-12 17:27:38Z ggregory $ + */ +public class DecoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public DecoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + */ + public DecoderException(String message) { + super(message); + } + + /** + * Constructsa new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/paypal/core/codec/Encoder.java b/src/main/java/com/paypal/core/codec/Encoder.java new file mode 100644 index 0000000..33ccb38 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/Encoder.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + *

Provides the highest level of abstraction for Encoders. + * This is the sister interface of {@link Decoder}. Every implementation of + * Encoder provides this common generic interface which allows a user to pass a + * generic Object to any Encoder implementation in the codec package.

+ * + * @author Apache Software Foundation + * @version $Id: Encoder.java 1170351 2011-09-13 21:09:09Z ggregory $ + */ +public interface Encoder { + + /** + * Encodes an "Object" and returns the encoded content + * as an Object. The Objects here may just be byte[] + * or Strings depending on the implementation used. + * + * @param source An object to encode + * + * @return An "encoded" Object + * + * @throws EncoderException an encoder exception is + * thrown if the encoder experiences a failure + * condition during the encoding process. + */ + Object encode(Object source) throws EncoderException; +} + diff --git a/src/main/java/com/paypal/core/codec/EncoderException.java b/src/main/java/com/paypal/core/codec/EncoderException.java new file mode 100644 index 0000000..7abaef6 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/EncoderException.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec; + +/** + * Thrown when there is a failure condition during the encoding process. This exception is thrown when an + * {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum, + * characters outside of the expected range. + * + * @author Apache Software Foundation + * @version $Id: EncoderException.java 1157192 2011-08-12 17:27:38Z ggregory $ + */ +public class EncoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public EncoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * a useful message relating to the encoder specific error. + */ + public EncoderException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/paypal/core/codec/binary/Base64.java b/src/main/java/com/paypal/core/codec/binary/Base64.java new file mode 100644 index 0000000..0d78577 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/binary/Base64.java @@ -0,0 +1,758 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec.binary; + +import java.math.BigInteger; + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

+ * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

+ *

+ * The class can be parameterized in the following manner with various constructors: + *

    + *
  • URL-safe mode: Default off.
  • + *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of + * 4 in the encoded data. + *
  • Line separator: Default is CRLF ("\r\n")
  • + *
+ *

+ *

+ * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

+ *

+ * This class is not thread-safe. Each thread should use its own instance. + *

+ * + * @see RFC 2045 + * @author Apache Software Foundation + * @since 1.0 + * @version $Revision: 1201529 $ + */ +public class Base64 extends BaseNCodec { + + /** + * BASE32 characters are 6 bits in length. + * They are formed by taking a block of 3 octets to form a 24-bit string, + * which is converted into 4 BASE64 characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 6; + private static final int BYTES_PER_UNENCODED_BLOCK = 3; + private static final int BYTES_PER_ENCODED_BLOCK = 4; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + *

+ * N.B. The next major release may break compatibility and make this field private. + *

+ * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + + /** + * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" + * equivalents as specified in Table 1 of RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / + * changed to - and _ to make the encoded Base64 results more URL-SAFE. + * This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in + * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 + * alphabet but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both + * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + /** + * Base64 uses 6-bit fields. + */ + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able + * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + // Only one decode table currently; keep for consistency with Base32 code + private final byte[] decodeTable = DECODE_TABLE; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Place holder for the bytes we're dealing with for our based logic. + * Bitwise operations store and extract the encoding or decoding from this variable. + */ + private int bitWorkArea; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ */ + public Base64() { + this(0); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ * + * @param urlSafe + * if true, URL-safe encoding is used. In most cases this should be set to + * false. + * @since 1.4 + */ + public Base64(boolean urlSafe) { + this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @since 1.4 + */ + public Base64(int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @throws IllegalArgumentException + * Thrown when the provided lineSeparator included some base64 characters. + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @param urlSafe + * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * @throws IllegalArgumentException + * The provided lineSeparator included some base64 characters. That's not going to work! + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, + lineLength, + lineSeparator == null ? 0 : lineSeparator.length); + // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 + // @see test case Base64Test.testConstructors() + if (lineSeparator != null) { + if (containsAlphabetOrPad(lineSeparator)) { + String sep = StringUtils.newStringUtf8(lineSeparator); + throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); + } + if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with + * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last + * remaining bytes (if not multiple of 3). + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in + * byte[] array of binary data to base64 encode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + @Override + void encode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + eof = true; + if (0 == modulus && lineLength == 0) { + return; // no leftovers to process and not using chunking + } + ensureBufferSize(encodeSize); + int savedPos = pos; + switch (modulus) { // 0-2 + case 1 : // 8 bits = 6 + 2 + buffer[pos++] = encodeTable[(bitWorkArea >> 2) & MASK_6BITS]; // top 6 bits + buffer[pos++] = encodeTable[(bitWorkArea << 4) & MASK_6BITS]; // remaining 2 + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + buffer[pos++] = PAD; + } + break; + + case 2 : // 16 bits = 6 + 6 + 4 + buffer[pos++] = encodeTable[(bitWorkArea >> 10) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 4) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + } + break; + } + currentLinePos += pos - savedPos; // keep track of current line position + // if currentPos == 0 we are at the start of a line, so don't add CRLF + if (lineLength > 0 && currentLinePos > 0) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(encodeSize); + modulus = (modulus+1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + bitWorkArea = (bitWorkArea << 8) + b; // BITS_PER_BYTE + if (0 == modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract + buffer[pos++] = encodeTable[(bitWorkArea >> 18) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 12) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 6) & MASK_6BITS]; + buffer[pos++] = encodeTable[bitWorkArea & MASK_6BITS]; + currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either. + *

+ *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are + * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, + * garbage-out philosophy: it will not check the provided data for validity. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in + * byte[] array of ascii data to base64 decode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + @Override + void decode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(decodeSize); + byte b = in[inPos++]; + if (b == PAD) { + // We're done. + eof = true; + break; + } else { + if (b >= 0 && b < DECODE_TABLE.length) { + int result = DECODE_TABLE[b]; + if (result >= 0) { + modulus = (modulus+1) % BYTES_PER_ENCODED_BLOCK; + bitWorkArea = (bitWorkArea << BITS_PER_ENCODED_BYTE) + result; + if (modulus == 0) { + buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) (bitWorkArea & MASK_8BITS); + } + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (eof && modulus != 0) { + ensureBufferSize(decodeSize); + + // We have some spare bits remaining + // Output all whole multiples of 8 bits and ignore the rest + switch (modulus) { + // case 1: // 6 bits - ignore entirely + // break; + case 2 : // 12 bits = 8 + 4 + bitWorkArea = bitWorkArea >> 4; // dump the extra 4 bits + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + case 3 : // 18 bits = 8 + 8 + 2 + bitWorkArea = bitWorkArea >> 2; // dump 2 bits + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + } + } + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. + */ + public static boolean isArrayByteBase64(byte[] arrayOctet) { + return isBase64(arrayOctet); + } + + /** + * Returns whether or not the octet is in the base 64 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the base 64 alphabet, false otherwise. + * @since 1.4 + */ + public static boolean isBase64(byte octet) { + return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param base64 + * String to test + * @return true if all characters in the String are valid characters in the Base64 alphabet or if + * the String is empty; false, otherwise + * @since 1.5 + */ + public static boolean isBase64(String base64) { + return isBase64(StringUtils.getBytesUtf8(base64)); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + * @since 1.5 + */ + public static boolean isBase64(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * NOTE: We changed the behaviour of this method from multi-line chunking (commons-codec-1.4) to + * single-line non-chunking (commons-codec-1.5). + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters. + * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). + */ + public static String encodeBase64String(byte[] binaryData) { + return StringUtils.newStringUtf8(encodeBase64(binaryData, false)); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(byte[] binaryData) { + return StringUtils.newStringUtf8(encodeBase64(binaryData, false, true)); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks + * + * @param binaryData + * binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * @param maxResultSize + * The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + // Create this so can use the super-class method + // Also ensures that the same roundings are performed by the ctor and the code + Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); + long len = b64.getEncodedLength(binaryData); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + + len + + ") than the specified maximum size of " + + maxResultSize); + } + + return b64.encode(binaryData); + } + + /** + * Decodes a Base64 String into octets + * + * @param base64String + * String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(String base64String) { + return new Base64().decode(base64String); + } + + /** + * Decodes Base64 data into octets + * + * @param base64Data + * Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + return new Base64().decode(base64Data); + } + + // Implementation of the Encoder Interface + + // Implementation of integer encoding used for crypto + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param pArray + * a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param bigInt + * a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException + * if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(BigInteger bigInt) { + if (bigInt == null) { + throw new NullPointerException("encodeInteger called with null parameter"); + } + return encodeBase64(toIntegerBytes(bigInt), false); + } + + /** + * Returns a byte-array representation of a BigInteger without sign bit. + * + * @param bigInt + * BigInteger to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Returns whether or not the octet is in the Base32 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the Base32 alphabet false otherwise. + */ + @Override + protected boolean isInAlphabet(byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } + +} diff --git a/src/main/java/com/paypal/core/codec/binary/BaseNCodec.java b/src/main/java/com/paypal/core/codec/binary/BaseNCodec.java new file mode 100644 index 0000000..553b382 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/binary/BaseNCodec.java @@ -0,0 +1,444 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec.binary; + +import com.paypal.core.codec.BinaryDecoder; +import com.paypal.core.codec.BinaryEncoder; +import com.paypal.core.codec.DecoderException; +import com.paypal.core.codec.EncoderException; + +/** + * Abstract superclass for Base-N encoders and decoders. + * + *

+ * This class is not thread-safe. + * Each thread should use its own instance. + *

+ */ +public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder { + + /** + * MIME chunk size per RFC 2045 section 6.8. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

+ * + * @see RFC 2045 section 6.8 + */ + public static final int MIME_CHUNK_SIZE = 76; + + /** + * PEM chunk size per RFC 1421 section 4.3.2.4. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

+ * + * @see RFC 1421 section 4.3.2.4 + */ + public static final int PEM_CHUNK_SIZE = 64; + + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + + /** + * Defines the default buffer size - currently {@value} + * - must be large enough for at least one encoded block+separator + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** Mask used to extract 8 bits, used in decoding bytes */ + protected static final int MASK_8BITS = 0xff; + + /** + * Byte used to pad output. + */ + protected static final byte PAD_DEFAULT = '='; // Allow static access to default + + protected final byte PAD = PAD_DEFAULT; // instance variable just in case it needs to vary later + + /** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */ + private final int unencodedBlockSize; + + /** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */ + private final int encodedBlockSize; + + /** + * Chunksize for encoding. Not used when decoding. + * A value of zero or less implies no chunking of the encoded data. + * Rounded down to nearest multiple of encodedBlockSize. + */ + protected final int lineLength; + + /** + * Size of chunk separator. Not used unless {@link #lineLength} > 0. + */ + private final int chunkSeparatorLength; + + /** + * Buffer for streaming. + */ + protected byte[] buffer; + + /** + * Position where next character should be written in the buffer. + */ + protected int pos; + + /** + * Position where next character should be read from the buffer. + */ + private int readPos; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, + * and must be thrown away. + */ + protected boolean eof; + + /** + * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to + * make sure each encoded line never goes beyond lineLength (if lineLength > 0). + */ + protected int currentLinePos; + + /** + * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. + * This variable helps track that. + */ + protected int modulus; + + /** + * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize} + * If chunkSeparatorLength is zero, then chunking is disabled. + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length lineLength + * @param chunkSeparatorLength the chunk separator length, if relevant + */ + protected BaseNCodec(int unencodedBlockSize, int encodedBlockSize, int lineLength, int chunkSeparatorLength){ + this.unencodedBlockSize = unencodedBlockSize; + this.encodedBlockSize = encodedBlockSize; + this.lineLength = (lineLength > 0 && chunkSeparatorLength > 0) ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; + this.chunkSeparatorLength = chunkSeparatorLength; + } + + /** + * Returns true if this object has buffered data for reading. + * + * @return true if there is data still available for reading. + */ + boolean hasData() { // package protected for access from I/O streams + return this.buffer != null; + } + + /** + * Returns the amount of buffered data available for reading. + * + * @return The amount of buffered data available for reading. + */ + int available() { // package protected for access from I/O streams + return buffer != null ? pos - readPos : 0; + } + + /** + * Get the default buffer size. Can be overridden. + * + * @return {@link #DEFAULT_BUFFER_SIZE} + */ + protected int getDefaultBufferSize() { + return DEFAULT_BUFFER_SIZE; + } + + /** Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. */ + private void resizeBuffer() { + if (buffer == null) { + buffer = new byte[getDefaultBufferSize()]; + pos = 0; + readPos = 0; + } else { + byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; + System.arraycopy(buffer, 0, b, 0, buffer.length); + buffer = b; + } + } + + /** + * Ensure that the buffer has room for size bytes + * + * @param size minimum spare space required + */ + protected void ensureBufferSize(int size){ + if ((buffer == null) || (buffer.length < pos + size)){ + resizeBuffer(); + } + } + + /** + * Extracts buffered data into the provided byte[] array, starting at position bPos, + * up to a maximum of bAvail bytes. Returns how many bytes were actually extracted. + * + * @param b + * byte[] array to extract the buffered data into. + * @param bPos + * position in byte[] array to start extraction at. + * @param bAvail + * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). + * @return The number of bytes successfully extracted into the provided byte[] array. + */ + int readResults(byte[] b, int bPos, int bAvail) { // package protected for access from I/O streams + if (buffer != null) { + int len = Math.min(available(), bAvail); + System.arraycopy(buffer, readPos, b, bPos, len); + readPos += len; + if (readPos >= pos) { + buffer = null; // so hasData() will return false, and this method can return -1 + } + return len; + } + return eof ? -1 : 0; + } + + /** + * Checks if a byte value is whitespace or not. + * Whitespace is taken to mean: space, tab, CR, LF + * @param byteToCheck + * the byte to check + * @return true if byte is whitespace, false otherwise + */ + protected static boolean isWhiteSpace(byte byteToCheck) { + switch (byteToCheck) { + case ' ' : + case '\n' : + case '\r' : + case '\t' : + return true; + default : + return false; + } + } + + /** + * Resets this object to its initial newly constructed state. + */ + private void reset() { + buffer = null; + pos = 0; + readPos = 0; + currentLinePos = 0; + modulus = 0; + eof = false; + } + + /** + * Encodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of the + * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[]. + * + * @param pObject + * Object to encode + * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied. + * @throws EncoderException + * if the parameter supplied is not of type byte[] + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof byte[])) { + throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]"); + } + return encode((byte[]) pObject); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A String containing only Base-N character data + */ + public String encodeToString(byte[] pArray) { + return StringUtils.newStringUtf8(encode(pArray)); + } + + /** + * Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of the + * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String. + * + * @param pObject + * Object to decode + * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String supplied. + * @throws DecoderException + * if the parameter supplied is not of type byte[] + */ + public Object decode(Object pObject) throws DecoderException { + if (pObject instanceof byte[]) { + return decode((byte[]) pObject); + } else if (pObject instanceof String) { + return decode((String) pObject); + } else { + throw new DecoderException("Parameter supplied to Base-N decode is not a byte[] or a String"); + } + } + + /** + * Decodes a String containing characters in the Base-N alphabet. + * + * @param pArray + * A String containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(String pArray) { + return decode(StringUtils.getBytesUtf8(pArray)); + } + + /** + * Decodes a byte[] containing characters in the Base-N alphabet. + * + * @param pArray + * A byte array containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + decode(pArray, 0, pArray.length); + decode(pArray, 0, -1); // Notify decoder of EOF. + byte[] result = new byte[pos]; + readResults(result, 0, result.length); + return result; + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A byte array containing only the basen alphabetic character data + */ + public byte[] encode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + encode(pArray, 0, pArray.length); + encode(pArray, 0, -1); // Notify encoder of EOF. + byte[] buf = new byte[pos - readPos]; + readResults(buf, 0, buf.length); + return buf; + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet. + * Uses UTF8 encoding. + * + * @param pArray a byte array containing binary data + * @return String containing only character data in the appropriate alphabet. + */ + public String encodeAsString(byte[] pArray){ + return StringUtils.newStringUtf8(encode(pArray)); + } + + abstract void encode(byte[] pArray, int i, int length); // package protected for access from I/O streams + + abstract void decode(byte[] pArray, int i, int length); // package protected for access from I/O streams + + /** + * Returns whether or not the octet is in the current alphabet. + * Does not allow whitespace or pad. + * + * @param value The value to test + * + * @return true if the value is defined in the current alphabet, false otherwise. + */ + protected abstract boolean isInAlphabet(byte value); + + /** + * Tests a given byte array to see if it contains only valid characters within the alphabet. + * The method optionally treats whitespace and pad as valid. + * + * @param arrayOctet byte array to test + * @param allowWSPad if true, then whitespace and PAD are also allowed + * + * @return true if all bytes are valid characters in the alphabet or if the byte array is empty; + * false, otherwise + */ + public boolean isInAlphabet(byte[] arrayOctet, boolean allowWSPad) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isInAlphabet(arrayOctet[i]) && + (!allowWSPad || (arrayOctet[i] != PAD) && !isWhiteSpace(arrayOctet[i]))) { + return false; + } + } + return true; + } + + /** + * Tests a given String to see if it contains only valid characters within the alphabet. + * The method treats whitespace and PAD as valid. + * + * @param basen String to test + * @return true if all characters in the String are valid characters in the alphabet or if + * the String is empty; false, otherwise + * @see #isInAlphabet(byte[], boolean) + */ + public boolean isInAlphabet(String basen) { + return isInAlphabet(StringUtils.getBytesUtf8(basen), true); + } + + /** + * Tests a given byte array to see if it contains any characters within the alphabet or PAD. + * + * Intended for use in checking line-ending arrays + * + * @param arrayOctet + * byte array to test + * @return true if any byte is a valid character in the alphabet or PAD; false otherwise + */ + protected boolean containsAlphabetOrPad(byte[] arrayOctet) { + if (arrayOctet == null) { + return false; + } + for (byte element : arrayOctet) { + if (PAD == element || isInAlphabet(element)) { + return true; + } + } + return false; + } + + /** + * Calculates the amount of space needed to encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * + * @return amount of space needed to encoded the supplied array. + * Returns a long since a max-len array will require > Integer.MAX_VALUE + */ + public long getEncodedLength(byte[] pArray) { + // Calculate non-chunked size - rounded up to allow for padding + // cast to long is needed to avoid possibility of overflow + long len = ((pArray.length + unencodedBlockSize-1) / unencodedBlockSize) * (long) encodedBlockSize; + if (lineLength > 0) { // We're using chunking + // Round up to nearest multiple + len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength; + } + return len; + } +} diff --git a/src/main/java/com/paypal/core/codec/binary/StringUtils.java b/src/main/java/com/paypal/core/codec/binary/StringUtils.java new file mode 100644 index 0000000..2765568 --- /dev/null +++ b/src/main/java/com/paypal/core/codec/binary/StringUtils.java @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.paypal.core.codec.binary; + +import java.io.UnsupportedEncodingException; + +import com.paypal.core.codec.CharEncoding; + +/** + * Converts String to and from bytes using the encodings required by the Java specification. These encodings are specified in Standard charsets + * + * @see CharEncoding + * @see Standard charsets + * @author Gary Gregory + * @version $Id: StringUtils.java 1170351 2011-09-13 21:09:09Z ggregory $ + * @since 1.4 + */ +public class StringUtils { + + /** + * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new + * byte array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesIso8859_1(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.ISO_8859_1); + } + + /** + * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUsAscii(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.US_ASCII); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Be(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16BE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Le(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16LE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf8(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_8); + } + + /** + * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte + * array. + *

+ * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param string + * the String to encode, may be null + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#getBytes(String) + */ + public static byte[] getBytesUnchecked(String string, String charsetName) { + if (string == null) { + return null; + } + try { + return string.getBytes(charsetName); + } catch (UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + private static IllegalStateException newIllegalStateException(String charsetName, UnsupportedEncodingException e) { + return new IllegalStateException(charsetName + ": " + e); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + *

+ * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param bytes + * The bytes to be decoded into characters, may be null + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return A new String decoded from the specified array of bytes using the given charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#String(byte[], String) + */ + public static String newString(byte[] bytes, String charsetName) { + if (bytes == null) { + return null; + } + try { + return new String(bytes, charsetName); + } catch (UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. + * + * @param bytes + * The bytes to be decoded into characters, may be null + * @return A new String decoded from the specified array of bytes using the ISO-8859-1 charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringIso8859_1(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.ISO_8859_1); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the US-ASCII charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUsAscii(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.US_ASCII); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16 charset + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16BE charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16Be(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16BE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16LE charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16Le(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16LE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-8 charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf8(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_8); + } + +} diff --git a/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java b/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java index a5c37aa..5e4c9b2 100644 --- a/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java +++ b/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; -import org.apache.commons.codec.binary.Base64; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -14,6 +13,7 @@ import com.paypal.core.HttpConfiguration; import com.paypal.core.HttpConnection; import com.paypal.core.SDKUtil; +import com.paypal.core.codec.binary.Base64; import com.paypal.core.credential.ICredential; public final class OAuthTokenCredential implements ICredential { diff --git a/src/main/java/com/paypal/core/rest/RESTConfiguration.java b/src/main/java/com/paypal/core/rest/RESTConfiguration.java index a61ca0a..118685d 100644 --- a/src/main/java/com/paypal/core/rest/RESTConfiguration.java +++ b/src/main/java/com/paypal/core/rest/RESTConfiguration.java @@ -8,11 +8,11 @@ import java.util.HashMap; import java.util.Map; -import org.apache.commons.codec.binary.Base64; import com.paypal.core.Constants; import com.paypal.core.HttpConfiguration; import com.paypal.core.SDKUtil; +import com.paypal.core.codec.binary.Base64; /** * RESTConfiguration helps {@link PayPalResource} with state dependent utility diff --git a/src/main/java/com/paypal/sdk/util/OAuthSignature.java b/src/main/java/com/paypal/sdk/util/OAuthSignature.java index 5f6440e..307e3b1 100644 --- a/src/main/java/com/paypal/sdk/util/OAuthSignature.java +++ b/src/main/java/com/paypal/sdk/util/OAuthSignature.java @@ -20,8 +20,8 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.codec.binary.Base64; +import com.paypal.core.codec.binary.Base64; import com.paypal.sdk.exceptions.OAuthException; public class OAuthSignature { From b993c3cd75dfbbfda29d99b9033ec4e6b53a0c95 Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Mon, 24 Jun 2013 19:19:51 +0530 Subject: [PATCH 2/8] Updated core for opensourcing --- src/main/java/com/paypal/core/Constants.java | 9 + .../core/rest/OAuthTokenCredential.java | 8 +- .../com/paypal/core/rest/PayPalResource.java | 107 +++++--- .../paypal/core/rest/RESTConfiguration.java | 243 ++++++------------ .../com/paypal/sdk/util/UserAgentHeader.java | 92 +++++++ .../core/rest/RESTConfigurationTestCase.java | 17 +- 6 files changed, 259 insertions(+), 217 deletions(-) create mode 100644 src/main/java/com/paypal/sdk/util/UserAgentHeader.java diff --git a/src/main/java/com/paypal/core/Constants.java b/src/main/java/com/paypal/core/Constants.java index 3db802d..933da41 100644 --- a/src/main/java/com/paypal/core/Constants.java +++ b/src/main/java/com/paypal/core/Constants.java @@ -54,6 +54,15 @@ private Constants() {} // PayPal Device IP Address Header public static final String PAYPAL_DEVICE_IPADDRESS_HEADER = "X-PAYPAL-DEVICE-IPADDRESS"; + + // User Agent Header + public static final String USER_AGENT_HEADER = "User-Agent"; + + // PayPal Request ID Header + public static final String PAYPAL_REQUEST_ID_HEADER = "PayPal-Request-Id"; + + // Authorization Header + public static final String AUTHORIZATION_HEADER = "Authorization"; // PayPal Sandbox Email Address for AA Header public static final String PAYPAL_SANDBOX_EMAIL_ADDRESS_HEADER = "X-PAYPAL-SANDBOX-EMAIL-ADDRESS"; diff --git a/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java b/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java index 5e4c9b2..06ed2eb 100644 --- a/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java +++ b/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; - import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.paypal.core.ConfigManager; @@ -15,6 +14,7 @@ import com.paypal.core.SDKUtil; import com.paypal.core.codec.binary.Base64; import com.paypal.core.credential.ICredential; +import com.paypal.sdk.util.UserAgentHeader; public final class OAuthTokenCredential implements ICredential { @@ -125,9 +125,11 @@ private String generateOAuthToken(String base64ClientID) httpConfiguration = getOAuthHttpConfiguration(); connection.createAndconfigureHttpConnection(httpConfiguration); Map headers = new HashMap(); - headers.put("Authorization", "Basic " + base64ClientID); + headers.put(Constants.AUTHORIZATION_HEADER, "Basic " + base64ClientID); headers.put(Constants.HTTP_ACCEPT_HEADER, "*/*"); - headers.put("User-Agent", RESTConfiguration.formUserAgentHeader()); + UserAgentHeader userAgentHeader = new UserAgentHeader( + PayPalResource.SDK_ID, PayPalResource.SDK_VERSION); + headers.putAll(userAgentHeader.getHeader()); String postRequest = "grant_type=client_credentials"; String jsonResponse = connection.execute("", postRequest, headers); JsonParser parser = new JsonParser(); diff --git a/src/main/java/com/paypal/core/rest/PayPalResource.java b/src/main/java/com/paypal/core/rest/PayPalResource.java index 29b814c..cc0da3d 100644 --- a/src/main/java/com/paypal/core/rest/PayPalResource.java +++ b/src/main/java/com/paypal/core/rest/PayPalResource.java @@ -9,8 +9,10 @@ import java.util.Map; import java.util.Properties; +import com.paypal.core.APICallPreHandler; import com.paypal.core.ConfigManager; import com.paypal.core.ConnectionManager; +import com.paypal.core.Constants; import com.paypal.core.HttpConfiguration; import com.paypal.core.HttpConnection; import com.paypal.core.LoggingManager; @@ -188,8 +190,8 @@ public static T configureAndExecute(APIContext apiContext, accessToken = apiContext.getAccessToken(); requestId = apiContext.getRequestId(); } - return configureAndExecute(cMap, accessToken, httpMethod, - resourcePath, null, payLoad, requestId, clazz); + return configureAndExecute(cMap, accessToken, httpMethod, resourcePath, + null, payLoad, requestId, clazz); } /** @@ -219,8 +221,8 @@ public static T configureAndExecute(APIContext apiContext, if (apiContext != null) { cMap = apiContext.getConfigurationMap(); } - return configureAndExecute(cMap, null, httpMethod, - resourcePath, headersMap, payLoad, null, clazz); + return configureAndExecute(cMap, null, httpMethod, resourcePath, + headersMap, payLoad, null, clazz); } private static T configureAndExecute( @@ -236,41 +238,35 @@ private static T configureAndExecute( if (!configInitialized) { initializeToDefault(); } - cMap = new HashMap( - PayPalResource.configurationMap); + cMap = new HashMap(PayPalResource.configurationMap); } - RESTConfiguration restConfiguration = createRESTConfiguration( - cMap, httpMethod, resourcePath, headersMap, + String contentType = (headersMap != null && headersMap + .containsKey(Constants.HTTP_CONTENT_TYPE_HEADER)) ? headersMap + .remove(Constants.HTTP_CONTENT_TYPE_HEADER) : null; + + APICallPreHandler apiCallPreHandler = createAPICallPreHandler( + cMap, payLoad, resourcePath, headersMap, accessToken, requestId); - t = execute(restConfiguration, payLoad, resourcePath, clazz); + HttpConfiguration httpConfiguration = createHttpConfiguration( + cMap, httpMethod, contentType, apiCallPreHandler); + t = execute(apiCallPreHandler, httpConfiguration, clazz); return t; } - /** - * Creates a {@link RESTConfiguration} based on configuration - * - * @param httpMethod - * {@link HttpMethod} - * @param resourcePath - * Resource URI - * @param accessToken - * Access Token - * @param requestId - * Request Id - * @return - */ - private static RESTConfiguration createRESTConfiguration( - Map configurationMap, HttpMethod httpMethod, + private static APICallPreHandler createAPICallPreHandler( + Map configurationMap, String payLoad, String resourcePath, Map headersMap, String accessToken, String requestId) { + APICallPreHandler apiCallPreHandler = null; RESTConfiguration restConfiguration = new RESTConfiguration( configurationMap, headersMap); - restConfiguration.setHttpMethod(httpMethod); restConfiguration.setResourcePath(resourcePath); restConfiguration.setRequestId(requestId); restConfiguration.setAuthorizationToken(accessToken); - return restConfiguration; + restConfiguration.setPayLoad(payLoad); + apiCallPreHandler = restConfiguration; + return apiCallPreHandler; } /** @@ -295,32 +291,27 @@ private static RESTConfiguration createRESTConfiguration( * @throws HttpErrorException * @throws InvalidResponseDataException */ - private static T execute(RESTConfiguration restConfiguration, - String payLoad, String resourcePath, Class clazz) + private static T execute(APICallPreHandler apiCallPreHandler, + HttpConfiguration httpConfiguration, Class clazz) throws PayPalRESTException { T t = null; ConnectionManager connectionManager; HttpConnection httpConnection; - HttpConfiguration httpConfig; Map headers; String responseString; try { // REST Headers - headers = restConfiguration.getHeaders(); - - // HTTPConfiguration Object - httpConfig = restConfiguration.getHttpConfigurations(); + headers = apiCallPreHandler.getHeaderMap(); // HttpConnection Initialization connectionManager = ConnectionManager.getInstance(); - httpConnection = connectionManager.getConnection(httpConfig); - httpConnection.createAndconfigureHttpConnection(httpConfig); + httpConnection = connectionManager.getConnection(httpConfiguration); + httpConnection.createAndconfigureHttpConnection(httpConfiguration); - LASTREQUEST.set(payLoad); - responseString = httpConnection.execute(restConfiguration - .getBaseURL().toURI().resolve(resourcePath).toString(), - payLoad, headers); + LASTREQUEST.set(apiCallPreHandler.getPayLoad()); + responseString = httpConnection.execute(null, + apiCallPreHandler.getPayLoad(), headers); LASTRESPONSE.set(responseString); if (clazz != null) { t = JSONFormatter.fromJSON(responseString, clazz); @@ -331,4 +322,42 @@ private static T execute(RESTConfiguration restConfiguration, return t; } + private static HttpConfiguration createHttpConfiguration( + Map configurationMap, HttpMethod httpMethod, + String contentType, APICallPreHandler apiCallPreHandler) { + HttpConfiguration httpConfiguration = new HttpConfiguration(); + httpConfiguration.setHttpMethod(httpMethod.toString()); + httpConfiguration.setEndPointUrl(apiCallPreHandler.getEndPoint()); + httpConfiguration.setContentType((contentType != null && contentType + .trim().length() > 0) ? (contentType) + : Constants.HTTP_CONTENT_TYPE_JSON); + httpConfiguration + .setGoogleAppEngine(Boolean.parseBoolean(configurationMap + .get(Constants.GOOGLE_APP_ENGINE))); + if (Boolean.parseBoolean(configurationMap + .get((Constants.USE_HTTP_PROXY)))) { + httpConfiguration.setProxyPort(Integer.parseInt(configurationMap + .get((Constants.HTTP_PROXY_PORT)))); + httpConfiguration.setProxyHost(configurationMap + .get((Constants.HTTP_PROXY_HOST))); + httpConfiguration.setProxyUserName(configurationMap + .get((Constants.HTTP_PROXY_USERNAME))); + httpConfiguration.setProxyPassword(configurationMap + .get((Constants.HTTP_PROXY_PASSWORD))); + } + httpConfiguration.setConnectionTimeout(Integer + .parseInt(configurationMap + .get(Constants.HTTP_CONNECTION_TIMEOUT))); + httpConfiguration.setMaxRetry(Integer.parseInt(configurationMap + .get(Constants.HTTP_CONNECTION_RETRY))); + httpConfiguration.setReadTimeout(Integer.parseInt(configurationMap + .get(Constants.HTTP_CONNECTION_READ_TIMEOUT))); + httpConfiguration.setMaxHttpConnection(Integer + .parseInt(configurationMap + .get(Constants.HTTP_CONNECTION_MAX_CONNECTION))); + httpConfiguration.setIpAddress(configurationMap + .get(Constants.DEVICE_IP_ADDRESS)); + return httpConfiguration; + } + } diff --git a/src/main/java/com/paypal/core/rest/RESTConfiguration.java b/src/main/java/com/paypal/core/rest/RESTConfiguration.java index 118685d..f441bd6 100644 --- a/src/main/java/com/paypal/core/rest/RESTConfiguration.java +++ b/src/main/java/com/paypal/core/rest/RESTConfiguration.java @@ -8,70 +8,25 @@ import java.util.HashMap; import java.util.Map; - +import com.paypal.core.APICallPreHandler; import com.paypal.core.Constants; -import com.paypal.core.HttpConfiguration; import com.paypal.core.SDKUtil; import com.paypal.core.codec.binary.Base64; +import com.paypal.core.credential.ICredential; +import com.paypal.exception.ClientActionRequiredException; +import com.paypal.sdk.util.UserAgentHeader; /** * RESTConfiguration helps {@link PayPalResource} with state dependent utility * methods */ -public class RESTConfiguration { - - /** - * Java Version and bit header computed during construction - */ - private static final String JAVAHEADER; - - /** - * OS Version and bit header computed during construction - */ - private static final String OSHEADER; +public class RESTConfiguration implements APICallPreHandler { /** * Configuration Map used for dynamic configuration */ private Map configurationMap = null; - static { - - // Java Version computed statically - StringBuilder javaVersion = new StringBuilder("lang=Java"); - if (System.getProperty("java.version") != null - && System.getProperty("java.version").length() > 0) { - javaVersion.append(";v=") - .append(System.getProperty("java.version")); - } - if (System.getProperty("java.vm.name") != null - && System.getProperty("java.vm.name").length() > 0) { - javaVersion.append(";bit="); - if (System.getProperty("java.vm.name").contains("Client")) { - javaVersion.append("32"); - } else { - javaVersion.append("64"); - } - } - JAVAHEADER = javaVersion.toString(); - - // OS Version Header - StringBuilder osVersion = new StringBuilder(); - if (System.getProperty("os.name") != null - && System.getProperty("os.name").length() > 0) { - osVersion.append("os="); - osVersion.append(System.getProperty("os.name").replace(' ', '_')); - } else { - osVersion.append("os="); - } - if (System.getProperty("os.version") != null - && System.getProperty("os.version").length() > 0) { - osVersion.append(" " - + System.getProperty("os.version").replace(' ', '_')); - } - OSHEADER = osVersion.toString(); - } - /** * Base URL for the service */ @@ -82,11 +37,6 @@ public class RESTConfiguration { */ private String authorizationToken; - /** - * {@link HttpMethod} - */ - private HttpMethod httpMethod; - /** * Resource URI as defined in the WSDL */ @@ -102,6 +52,11 @@ public class RESTConfiguration { */ private Map headersMap; + /** + * Request Payload + */ + private String payLoad; + /** * Constructor using configurations dynamically * @@ -135,14 +90,6 @@ public void setAuthorizationToken(String authorizationToken) { this.authorizationToken = authorizationToken; } - /** - * @param httpMethod - * the httpMethod to set - */ - public void setHttpMethod(HttpMethod httpMethod) { - this.httpMethod = httpMethod; - } - /** * @param resourcePath * the resourcePath to set @@ -160,17 +107,11 @@ public void setRequestId(String requestId) { } /** - * @return the javaheader - */ - public static String getJavaheader() { - return JAVAHEADER; - } - - /** - * @return the osheader + * @param payLoad + * the payLoad to set */ - public static String getOsheader() { - return OSHEADER; + public void setPayLoad(String payLoad) { + this.payLoad = payLoad; } /** @@ -178,76 +119,8 @@ public static String getOsheader() { * * @return {@link Map} of Http headers */ - public Map getHeaders() { - Map headers = new HashMap(); - if (authorizationToken != null - && authorizationToken.trim().length() > 0) { - headers.put("Authorization", authorizationToken); - } else if (getClientID() != null && getClientID().trim().length() > 0 - && getClientSecret() != null - && getClientSecret().trim().length() > 0) { - try { - headers.put( - "Authorization", - "Basic " - + encodeToBase64(getClientID(), - getClientSecret())); - } catch (UnsupportedEncodingException e) { - // - } - } - headers.put("User-Agent", formUserAgentHeader()); - if (requestId != null && requestId.length() > 0) { - headers.put("PayPal-Request-Id", requestId); - } - return headers; - } - - /** - * Returns a {@link HttpConfiguration} based on configuration - * - * @return {@link HttpConfiguration} - * @throws MalformedURLException - * @throws URISyntaxException - */ - public HttpConfiguration getHttpConfigurations() - throws MalformedURLException, URISyntaxException { - HttpConfiguration httpConfiguration = new HttpConfiguration(); - httpConfiguration.setHttpMethod(httpMethod.toString()); - httpConfiguration.setEndPointUrl(getBaseURL().toURI() - .resolve(resourcePath).toString()); - httpConfiguration.setContentType((headersMap != null) ? ((headersMap - .get(Constants.HTTP_CONTENT_TYPE_HEADER) != null) ? headersMap - .get("Content-Type") : Constants.HTTP_CONTENT_TYPE_JSON) - : Constants.HTTP_CONTENT_TYPE_JSON); - httpConfiguration.setGoogleAppEngine(Boolean - .parseBoolean(this.configurationMap - .get(Constants.GOOGLE_APP_ENGINE))); - if (Boolean.parseBoolean(this.configurationMap - .get((Constants.USE_HTTP_PROXY)))) { - httpConfiguration.setProxyPort(Integer - .parseInt(this.configurationMap - .get((Constants.HTTP_PROXY_PORT)))); - httpConfiguration.setProxyHost(this.configurationMap - .get((Constants.HTTP_PROXY_HOST))); - httpConfiguration.setProxyUserName(this.configurationMap - .get((Constants.HTTP_PROXY_USERNAME))); - httpConfiguration.setProxyPassword(this.configurationMap - .get((Constants.HTTP_PROXY_PASSWORD))); - } - httpConfiguration.setConnectionTimeout(Integer - .parseInt(this.configurationMap - .get(Constants.HTTP_CONNECTION_TIMEOUT))); - httpConfiguration.setMaxRetry(Integer.parseInt(this.configurationMap - .get(Constants.HTTP_CONNECTION_RETRY))); - httpConfiguration.setReadTimeout(Integer.parseInt(this.configurationMap - .get(Constants.HTTP_CONNECTION_READ_TIMEOUT))); - httpConfiguration.setMaxHttpConnection(Integer - .parseInt(this.configurationMap - .get(Constants.HTTP_CONNECTION_MAX_CONNECTION))); - httpConfiguration.setIpAddress(this.configurationMap - .get(Constants.DEVICE_IP_ADDRESS)); - return httpConfiguration; + public Map getHeaderMap() { + return getProcessedHeaderMap(); } /** @@ -258,12 +131,12 @@ public HttpConfiguration getHttpConfigurations() */ public URL getBaseURL() throws MalformedURLException { if (url == null) { - String urlString = this.configurationMap.get("service.EndPoint"); + String urlString = this.configurationMap.get(Constants.ENDPOINT); if (urlString == null || urlString.length() <= 0) { - String mode = this.configurationMap.get("mode"); - if ("sandbox".equalsIgnoreCase(mode)) { + String mode = this.configurationMap.get(Constants.MODE); + if (Constants.SANDBOX.equalsIgnoreCase(mode)) { urlString = Constants.REST_SANDBOX_ENDPOINT; - } else if ("live".equalsIgnoreCase(mode)) { + } else if (Constants.LIVE.equalsIgnoreCase(mode)) { urlString = Constants.REST_LIVE_ENDPOINT; } else { throw new MalformedURLException( @@ -297,19 +170,10 @@ public void setUrl(String urlString) throws MalformedURLException { * * @return */ - public static String formUserAgentHeader() { - String header = null; - StringBuilder stringBuilder = new StringBuilder("PayPalSDK/" - + PayPalResource.SDK_ID + " " + PayPalResource.SDK_VERSION - + " "); - stringBuilder.append("(").append(JAVAHEADER); - String osVersion = OSHEADER; - if (osVersion.length() > 0) { - stringBuilder.append(";").append(osVersion); - } - stringBuilder.append(")"); - header = stringBuilder.toString(); - return header; + protected Map formUserAgentHeader() { + UserAgentHeader userAgentHeader = new UserAgentHeader( + PayPalResource.SDK_ID, PayPalResource.SDK_VERSION); + return userAgentHeader.getHeader(); } /* @@ -348,4 +212,63 @@ private String generateBase64String(String clientID) return base64ClientID; } + public String getPayLoad() { + return getProcessedPayLoad(); + } + + public String getEndPoint() { + return getProcessedEndPoint(); + } + + public ICredential getCredential() { + return null; + } + + public void validate() throws ClientActionRequiredException { + // TODO + } + + protected String getProcessedEndPoint() { + String endPoint = null; + try { + endPoint = getBaseURL().toURI().resolve(resourcePath).toString(); + } catch (MalformedURLException e) { + // + } catch (URISyntaxException e) { + // + } + return endPoint; + } + + protected Map getProcessedHeaderMap() { + Map headers = new HashMap(); + if (authorizationToken != null + && authorizationToken.trim().length() > 0) { + headers.put(Constants.AUTHORIZATION_HEADER, authorizationToken); + } else if (getClientID() != null && getClientID().trim().length() > 0 + && getClientSecret() != null + && getClientSecret().trim().length() > 0) { + try { + headers.put(Constants.AUTHORIZATION_HEADER, "Basic " + + encodeToBase64(getClientID(), getClientSecret())); + } catch (UnsupportedEncodingException e) { + // TODO + } + } + if (requestId != null && requestId.length() > 0) { + headers.put(Constants.PAYPAL_REQUEST_ID_HEADER, requestId); + } + headers.putAll(formUserAgentHeader()); + + // Add any custom headers + if (headersMap != null && headersMap.size() > 0) { + headers.putAll(headersMap); + } + return headers; + } + + protected String getProcessedPayLoad() { + return payLoad; + } + } diff --git a/src/main/java/com/paypal/sdk/util/UserAgentHeader.java b/src/main/java/com/paypal/sdk/util/UserAgentHeader.java new file mode 100644 index 0000000..f79c500 --- /dev/null +++ b/src/main/java/com/paypal/sdk/util/UserAgentHeader.java @@ -0,0 +1,92 @@ +package com.paypal.sdk.util; + +import java.util.HashMap; +import java.util.Map; + +import com.paypal.core.Constants; + +public class UserAgentHeader { + + private String productId; + + private String productVersion; + + public UserAgentHeader(String productId, String productVersion) { + super(); + this.productId = productId; + this.productVersion = productVersion; + } + + /** + * Java Version and bit header computed during construction + */ + private static final String JAVAHEADER; + + /** + * OS Version and bit header computed during construction + */ + private static final String OSHEADER; + + static { + + // Java Version computed statically + StringBuilder javaVersion = new StringBuilder("lang=Java"); + if (System.getProperty("java.version") != null + && System.getProperty("java.version").length() > 0) { + javaVersion.append(";v=") + .append(System.getProperty("java.version")); + } + if (System.getProperty("java.vm.name") != null + && System.getProperty("java.vm.name").length() > 0) { + javaVersion.append(";bit="); + if (System.getProperty("java.vm.name").contains("Client")) { + javaVersion.append("32"); + } else { + javaVersion.append("64"); + } + } + JAVAHEADER = javaVersion.toString(); + + // OS Version Header + StringBuilder osVersion = new StringBuilder(); + if (System.getProperty("os.name") != null + && System.getProperty("os.name").length() > 0) { + osVersion.append("os="); + osVersion.append(System.getProperty("os.name").replace(' ', '_')); + } else { + osVersion.append("os="); + } + if (System.getProperty("os.version") != null + && System.getProperty("os.version").length() > 0) { + osVersion.append(" " + + System.getProperty("os.version").replace(' ', '_')); + } + OSHEADER = osVersion.toString(); + } + + public Map getHeader() { + Map userAgentMap = new HashMap(); + userAgentMap.put(Constants.USER_AGENT_HEADER, formUserAgentHeader()); + return userAgentMap; + } + + /** + * Returns User-Agent header + * + * @return + */ + private String formUserAgentHeader() { + String header = null; + StringBuilder stringBuilder = new StringBuilder("PayPalSDK/" + + productId + " " + productVersion + " "); + stringBuilder.append("(").append(JAVAHEADER); + String osVersion = OSHEADER; + if (osVersion.length() > 0) { + stringBuilder.append(";").append(osVersion); + } + stringBuilder.append(")"); + header = stringBuilder.toString(); + return header; + } + +} diff --git a/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java b/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java index f58ae66..31ca12b 100644 --- a/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java +++ b/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java @@ -17,38 +17,26 @@ public void testRESTConfiguration() { Map configurationMap = new HashMap(); configurationMap.put("service.EndPoint", "https://localhost.sandbox.paypal.com"); RESTConfiguration restConfiguration = new RESTConfiguration(configurationMap); - restConfiguration.setHttpMethod(HttpMethod.POST); restConfiguration.setResourcePath("/a/b/c"); - restConfiguration.getHttpConfigurations(); URL url = restConfiguration.getBaseURL(); Assert.assertEquals(true, url.toString().endsWith("/")); } catch (MalformedURLException e) { Assert.fail(); - } catch (URISyntaxException e) { - Assert.fail(); - } + } } @Test(dependsOnMethods = { "testRESTConfiguration" }) public void testRESTHeaderConfiguration() { - try { Map configurationMap = new HashMap(); configurationMap.put("service.EndPoint", "https://localhost.sandbox.paypal.com"); RESTConfiguration restConfiguration = new RESTConfiguration(configurationMap); - restConfiguration.setHttpMethod(HttpMethod.POST); restConfiguration.setResourcePath("/a/b/c"); - restConfiguration.getHttpConfigurations(); - Map headers = restConfiguration.getHeaders(); + Map headers = restConfiguration.getHeaderMap(); Assert.assertEquals(headers.size() != 0, true); String header = headers.get("User-Agent"); String[] hdrs = header.split("\\("); hdrs = hdrs[1].split(";"); Assert.assertEquals(hdrs.length == 4, true); - } catch (MalformedURLException e) { - Assert.fail(); - } catch (URISyntaxException e) { - Assert.fail(); - } } @Test(dependsOnMethods = { "testRESTHeaderConfiguration" }) @@ -57,7 +45,6 @@ public void testRESTConfigurationURL() { Map configurationMap = new HashMap(); configurationMap.put("service.EndPoint", "https://localhost.sandbox.paypal.com"); RESTConfiguration restConfiguration = new RESTConfiguration(configurationMap); - restConfiguration.setHttpMethod(HttpMethod.POST); restConfiguration.setResourcePath("/a/b/c"); String urlString = "https://sample.com"; restConfiguration.setUrl(urlString); From fa8d6b8dbba238262ca56c96dea6fe80589100d6 Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Tue, 25 Jun 2013 16:52:11 +0530 Subject: [PATCH 3/8] Updated documentation comments for opensourcing. Fixed bug in RESTUtil.java in formatURIPath method, changed if..else ladder to if..elseif ladder --- .../com/paypal/core/APICallPreHandler.java | 7 + .../paypal/core/APICallPreHandlerFactory.java | 19 ++ .../java/com/paypal/core/ConfigManager.java | 24 +- src/main/java/com/paypal/core/SDKUtil.java | 15 +- .../java/com/paypal/core/rest/APIContext.java | 14 +- .../com/paypal/core/rest/PayPalResource.java | 283 ++++++++++++++---- ...ration.java => RESTAPICallPreHandler.java} | 77 ++++- .../java/com/paypal/core/rest/RESTUtil.java | 6 +- src/test/java/com/paypal/core/OpenIdTest.java | 8 +- .../core/rest/RESTConfigurationTestCase.java | 6 +- 10 files changed, 373 insertions(+), 86 deletions(-) create mode 100644 src/main/java/com/paypal/core/APICallPreHandlerFactory.java rename src/main/java/com/paypal/core/rest/{RESTConfiguration.java => RESTAPICallPreHandler.java} (67%) diff --git a/src/main/java/com/paypal/core/APICallPreHandler.java b/src/main/java/com/paypal/core/APICallPreHandler.java index 9d0ac69..cee0926 100644 --- a/src/main/java/com/paypal/core/APICallPreHandler.java +++ b/src/main/java/com/paypal/core/APICallPreHandler.java @@ -3,12 +3,19 @@ import java.util.Map; import com.paypal.core.credential.ICredential; +import com.paypal.core.nvp.PlatformAPICallPreHandler; +import com.paypal.core.rest.RESTAPICallPreHandler; +import com.paypal.core.soap.MerchantAPICallPreHandler; import com.paypal.exception.ClientActionRequiredException; import com.paypal.sdk.exceptions.OAuthException; /** * APICallPreHandler defines a high level abstraction for call * specific operations. The calls may be divided as per formats as SOAP or NVP. + * PayPal Merchant Services are provided by {@link MerchantAPICallPreHandler} which + * is a decorator over the basic {@link DefaultSOAPAPICallHandler}. PayPal Platform + * Services are provided by {@link PlatformAPICallPreHandler}. PayPal REST API is + * provided by {@link RESTAPICallPreHandler} * */ public interface APICallPreHandler { diff --git a/src/main/java/com/paypal/core/APICallPreHandlerFactory.java b/src/main/java/com/paypal/core/APICallPreHandlerFactory.java new file mode 100644 index 0000000..7f51749 --- /dev/null +++ b/src/main/java/com/paypal/core/APICallPreHandlerFactory.java @@ -0,0 +1,19 @@ +package com.paypal.core; + +/** + * APICallPreHandlerFactory factory for returning implementations if + * {@link APICallPreHandler} + * + * @author kjayakumar + * + */ +public interface APICallPreHandlerFactory { + + /** + * Creates an instance of {@link APICallPreHandler} + * + * @return + */ + APICallPreHandler createAPICallPreHandler(); + +} diff --git a/src/main/java/com/paypal/core/ConfigManager.java b/src/main/java/com/paypal/core/ConfigManager.java index 6d8a9f3..a689b06 100644 --- a/src/main/java/com/paypal/core/ConfigManager.java +++ b/src/main/java/com/paypal/core/ConfigManager.java @@ -13,8 +13,14 @@ import com.paypal.sdk.util.ResourceLoader; /** - * ConfigManager loads the configuration file and hands out - * appropriate parameters to application + * ConfigManager loads configuration from 'skd_config.properties' + * file found in the classpath. There are certain default parameters that the + * system chooses to use if not seen a part of the configuration. They are + * enumerated below with the defaults is parenthesis + * + * http.ConnectionTimeOut(5000 ms), http.Retry(2), http.ReadTimeOut(30000 ms), + * http.MaxConnections(100), http.IPAddress(127.0.0.1), + * http.GoogleAppEngine(false) * */ public final class ConfigManager { @@ -60,10 +66,8 @@ public final class ConfigManager { DEFAULT_PROPERTIES.put(Constants.GOOGLE_APP_ENGINE, "false"); defaultMapView = new HashMap(); for (Object object : DEFAULT_PROPERTIES.keySet()) { - defaultMapView.put( - object.toString().trim(), - DEFAULT_PROPERTIES.getProperty( - object.toString()).trim()); + defaultMapView.put(object.toString().trim(), DEFAULT_PROPERTIES + .getProperty(object.toString()).trim()); } } @@ -71,6 +75,10 @@ public final class ConfigManager { * Private constructor */ private ConfigManager() { + + /* + * Load configuration for default 'sdk_config.properties' + */ ResourceLoader resourceLoader = new ResourceLoader( Constants.DEFAULT_CONFIGURATION_FILE); try { @@ -177,7 +185,9 @@ public void load(Properties properties) { } /** - * Constructs a {@link Map} object from the underlying {@link Properties} + * Constructs a {@link Map} object from the underlying {@link Properties}. + * The {@link Properties} object is loaded for 'sdk_config.properties' file + * in the classpath * * @return {@link Map} */ diff --git a/src/main/java/com/paypal/core/SDKUtil.java b/src/main/java/com/paypal/core/SDKUtil.java index 63a483f..158ca0d 100644 --- a/src/main/java/com/paypal/core/SDKUtil.java +++ b/src/main/java/com/paypal/core/SDKUtil.java @@ -1,5 +1,6 @@ package com.paypal.core; +import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -11,8 +12,9 @@ * */ public final class SDKUtil { - - private SDKUtil() {} + + private SDKUtil() { + } /** * Pattern for replacing Ampersand '&' character @@ -210,7 +212,8 @@ public static String escapeInvalidXmlCharsRegex(Double doubleContent) { /** * Constructs a Map from a {@link Properties} object by - * combining the default values from Default {@link Properties} + * combining the default values. See {@link ConfigManager} for default + * values * * @param properties * Input {@link Properties} @@ -234,13 +237,15 @@ public static Map constructMap(Properties properties) { } /** - * Combines some {@link Map} with Default {@link Map} + * Combines some {@link Map} with default values. See {@link ConfigManager} + * for default values. * * @param receivedMap * {@link Map} used to combine with Default {@link Map} * @return Combined {@link Map} */ - public static Map combineDefaultMap(Map receivedMap) { + public static Map combineDefaultMap( + Map receivedMap) { Map combinedMap = ConfigManager.getDefaultSDKMap(); combinedMap.putAll(receivedMap); return combinedMap; diff --git a/src/main/java/com/paypal/core/rest/APIContext.java b/src/main/java/com/paypal/core/rest/APIContext.java index 4e00ff4..f815e05 100644 --- a/src/main/java/com/paypal/core/rest/APIContext.java +++ b/src/main/java/com/paypal/core/rest/APIContext.java @@ -14,7 +14,7 @@ public class APIContext { /** - * Access Token + * OAuth Token */ private String accessToken; @@ -41,7 +41,8 @@ public APIContext() { } /** - * APIContext + * APIContext, requestId is auto generated, calling setMaskRequestId(true) + * will override the requestId getter to return null * * @param accessToken * AccessToken required for the call. @@ -57,9 +58,14 @@ public APIContext(String accessToken) { * APIContext * * @param accessToken - * AccessToken required for the call. + * OAuthToken required for the call. OAuth token used by the REST + * API service. The token should be of the form 'Bearer xxxx..'. + * See {@link OAuthTokenCredential} to generate OAuthToken * @param requestId - * Unique requestId required for the call. + * Unique requestId required for the call. Idempotency id, Calling + * setMaskRequestId(true) will override the requestId getter to + * return null, which can be used by the client to forcibly not + * sent requestId in the API call. */ public APIContext(String accessToken, String requestId) { this(accessToken); diff --git a/src/main/java/com/paypal/core/rest/PayPalResource.java b/src/main/java/com/paypal/core/rest/PayPalResource.java index cc0da3d..d382bf8 100644 --- a/src/main/java/com/paypal/core/rest/PayPalResource.java +++ b/src/main/java/com/paypal/core/rest/PayPalResource.java @@ -5,11 +5,13 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.Properties; import com.paypal.core.APICallPreHandler; +import com.paypal.core.APICallPreHandlerFactory; import com.paypal.core.ConfigManager; import com.paypal.core.ConnectionManager; import com.paypal.core.Constants; @@ -19,10 +21,16 @@ import com.paypal.core.SDKUtil; /** - * PayPalResource acts as a base class for REST enabled resources + * PayPalResource acts as a base class for REST enabled resources. The class + * operates by using a {@link APICallPreHandler} as an abstraction for making + * calls. */ public abstract class PayPalResource { + /* + * The class uses an implementation APICallPreHandler (here + * RESTAPICallPreHandler)to get access to endpoint, HTTP headers, payload. + */ /** * SDK ID used in User-Agent HTTP header */ @@ -42,6 +50,11 @@ public abstract class PayPalResource { * Configuration enabled flag */ private static boolean configInitialized = false; + + /** + * Default {@link APICallPreHandlerFactory} + */ + private static APICallPreHandlerFactory apiCallPreHandlerFactory = new RESTAPICallPreHandlerFactory(); /** * Last request sent to Service @@ -54,28 +67,19 @@ public abstract class PayPalResource { private static final ThreadLocal LASTRESPONSE = new ThreadLocal(); /** - * Initialize using InputStream(of a Properties file) - * - * @param is - * InputStream - * @throws PayPalRESTException + * Sets a custom {@link APICallPreHandlerFactory}, the new system may also want to + * override the class level createAPICallPreHandler(...) method + * @param preHandlerFactory the preHandlerFactory to set */ - public static void initConfig(InputStream inputStream) - throws PayPalRESTException { - try { - Properties properties = new Properties(); - properties.load(inputStream); - configurationMap = SDKUtil.constructMap(properties); - configInitialized = true; - } catch (IOException ioe) { - LoggingManager.severe(PayPalResource.class, ioe.getMessage(), ioe); - throw new PayPalRESTException(ioe.getMessage(), ioe); - } - + static void setPreHandlerFactory(APICallPreHandlerFactory apiCallPreHandlerFactory) { + PayPalResource.apiCallPreHandlerFactory = apiCallPreHandlerFactory; } /** - * Initialize using a File(Properties file) + * Initialize the system using a File(Properties file). The system is + * initialized using the given file and if the initialization succeeds the + * default 'sdk_config.properties' can only be loaded by calling the method + * initializeToDefault() * * @param file * File object of a properties entity @@ -97,7 +101,10 @@ public static void initConfig(File file) throws PayPalRESTException { } /** - * Initialize using Properties + * Initialize using Properties. The system is initialized using the given + * properties object and if the initialization succeeds the default + * 'sdk_config.properties' can only be loaded by calling the method + * initializeToDefault() * * @param properties * Properties object @@ -107,12 +114,42 @@ public static void initConfig(Properties properties) { configInitialized = true; } + /** + * Initialize using {@link InputStream}(of a Properties file).. The system + * is initialized using the given {@link InputStream} and if the + * initialization succeeds the default 'sdk_config.properties' can only be + * loaded by calling the method initializeToDefault(). The system is + * initialized with the information after loading defaults for the + * parameters that are not passed as part of the configuration. For defaults + * see {@link ConfigManager} + * + * @param inputStream + * InputStream + * @throws PayPalRESTException + */ + public static void initConfig(InputStream inputStream) + throws PayPalRESTException { + try { + Properties properties = new Properties(); + properties.load(inputStream); + + /* + * Create a Map instance and combine it with default values + */ + configurationMap = SDKUtil.constructMap(properties); + configInitialized = true; + } catch (IOException ioe) { + LoggingManager.severe(PayPalResource.class, ioe.getMessage(), ioe); + throw new PayPalRESTException(ioe.getMessage(), ioe); + } + } + /** * Initialize to default properties * * @throws PayPalRESTException */ - private static void initializeToDefault() throws PayPalRESTException { + public static void initializeToDefault() throws PayPalRESTException { configurationMap = SDKUtil.combineDefaultMap(ConfigManager .getInstance().getConfigurationMap()); } @@ -232,64 +269,95 @@ private static T configureAndExecute( Class clazz) throws PayPalRESTException { T t = null; Map cMap = null; + + /* + * Check for null before combining with default + */ if (configurationMap != null) { cMap = SDKUtil.combineDefaultMap(configurationMap); } else { if (!configInitialized) { initializeToDefault(); } + + /* + * The Map returned here is already combined with default values + */ cMap = new HashMap(PayPalResource.configurationMap); } + + /* + * Search for Content-Type header passed as a part of headersMap remove + * the header and pass it to HttpConfiguration object with creates the + * base connection + */ String contentType = (headersMap != null && headersMap .containsKey(Constants.HTTP_CONTENT_TYPE_HEADER)) ? headersMap .remove(Constants.HTTP_CONTENT_TYPE_HEADER) : null; - APICallPreHandler apiCallPreHandler = createAPICallPreHandler( - cMap, payLoad, resourcePath, headersMap, - accessToken, requestId); - HttpConfiguration httpConfiguration = createHttpConfiguration( - cMap, httpMethod, contentType, apiCallPreHandler); + APICallPreHandler apiCallPreHandler = createAPICallPreHandler(cMap, + payLoad, resourcePath, headersMap, accessToken, requestId); + HttpConfiguration httpConfiguration = createHttpConfiguration(cMap, + httpMethod, contentType, apiCallPreHandler); t = execute(apiCallPreHandler, httpConfiguration, clazz); return t; } - private static APICallPreHandler createAPICallPreHandler( + /** + * Returns a implementation of {@link APICallPreHandler} for the underlying + * layer. + * + * @param configurationMap + * configuration Map + * @param payLoad + * Raw payload + * @param resourcePath + * URI part of the resource operated on + * @param headersMap + * Custom HTTP headers map + * @param accessToken + * OAuth Token + * @param requestId + * PayPal Request Id + * @return + */ + protected static APICallPreHandler createAPICallPreHandler( Map configurationMap, String payLoad, String resourcePath, Map headersMap, String accessToken, String requestId) { - APICallPreHandler apiCallPreHandler = null; - RESTConfiguration restConfiguration = new RESTConfiguration( - configurationMap, headersMap); - restConfiguration.setResourcePath(resourcePath); - restConfiguration.setRequestId(requestId); - restConfiguration.setAuthorizationToken(accessToken); - restConfiguration.setPayLoad(payLoad); - apiCallPreHandler = restConfiguration; - return apiCallPreHandler; + + /* + * Override this method in the subclass level to return + * any implementation of APICallPreHandler for the + * system + */ + if (apiCallPreHandlerFactory == null) { + setPreHandlerFactory(new RESTAPICallPreHandlerFactory()); + } + RESTAPICallPreHandlerFactory restAPICallPreHandlerFactory = (RESTAPICallPreHandlerFactory) apiCallPreHandlerFactory; + restAPICallPreHandlerFactory.setConfigurationMap(configurationMap); + restAPICallPreHandlerFactory.setHeadersMap(headersMap); + restAPICallPreHandlerFactory.setResourcePath(resourcePath); + restAPICallPreHandlerFactory.setRequestId(requestId); + restAPICallPreHandlerFactory.setAuthorizationToken(accessToken); + restAPICallPreHandlerFactory.setPayLoad(payLoad); + return apiCallPreHandlerFactory.createAPICallPreHandler(); } /** * Execute the API call and return response * * @param - * Type of the return object - * @param restConfiguration - * {@link RESTConfiguration} - * @param payLoad - * Payload - * @param resourcePath - * Resource URI + * Generic Type for response object construction + * @param apiCallPreHandler + * Implementation of {@link APICallPreHandler} + * @param httpConfiguration + * {@link HttpConfiguration} * @param clazz - * Class of the return object - * @return API response type object + * Response Object class + * @return Response Type * @throws PayPalRESTException - * @throws URISyntaxException - * @throws IOException - * @throws InterruptedException - * @throws ClientActionRequiredException - * @throws HttpErrorException - * @throws InvalidResponseDataException */ private static T execute(APICallPreHandler apiCallPreHandler, HttpConfiguration httpConfiguration, Class clazz) @@ -322,6 +390,20 @@ private static T execute(APICallPreHandler apiCallPreHandler, return t; } + /** + * Utility method that creates a {@link HttpConfiguration} object from the + * passed information + * + * @param configurationMap + * Configuration to base the construction upon. + * @param httpMethod + * HTTP Method + * @param contentType + * Content-Type header + * @param apiCallPreHandler + * {@link APICallPreHandler} for retrieving EndPoint + * @return + */ private static HttpConfiguration createHttpConfiguration( Map configurationMap, HttpMethod httpMethod, String contentType, APICallPreHandler apiCallPreHandler) { @@ -360,4 +442,103 @@ private static HttpConfiguration createHttpConfiguration( return httpConfiguration; } + /** + * Implementation of {@link APICallPreHandlerFactory} that returns an instance of + * {@link RESTAPICallPreHandler} + * @author kjayakumar + * + */ + private static class RESTAPICallPreHandlerFactory implements + APICallPreHandlerFactory { + + /** + * Configuration map + */ + Map configurationMap = null; + + /** + * Raw Payload + */ + String payLoad = null; + + /** + * Resource URI path + */ + String resourcePath = null; + + /** + * Custom HTTP headers map + */ + Map headersMap = null; + + /** + * Authorization Token + */ + String authorizationToken = null; + + /** + * Request Id + */ + String requestId = null; + + RESTAPICallPreHandlerFactory() { + + } + + /** + * @param configurationMap the configurationMap to set + */ + void setConfigurationMap(Map configurationMap) { + this.configurationMap = configurationMap; + } + + /** + * @param payLoad the payLoad to set + */ + void setPayLoad(String payLoad) { + this.payLoad = payLoad; + } + + /** + * @param resourcePath the resourcePath to set + */ + void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + /** + * @param headersMap the headersMap to set + */ + void setHeadersMap(Map headersMap) { + this.headersMap = headersMap; + } + + /** + * @param authorizationToken the authorizationToken to set + */ + void setAuthorizationToken(String authorizationToken) { + this.authorizationToken = authorizationToken; + } + + /** + * @param requestId the requestId to set + */ + void setRequestId(String requestId) { + this.requestId = requestId; + } + + public APICallPreHandler createAPICallPreHandler() { + APICallPreHandler apiCallPreHandler = null; + RESTAPICallPreHandler restAPICallPreHandler = new RESTAPICallPreHandler( + configurationMap, headersMap); + restAPICallPreHandler.setResourcePath(resourcePath); + restAPICallPreHandler.setRequestId(requestId); + restAPICallPreHandler.setAuthorizationToken(authorizationToken); + restAPICallPreHandler.setPayLoad(payLoad); + apiCallPreHandler = restAPICallPreHandler; + return apiCallPreHandler; + } + + } + } diff --git a/src/main/java/com/paypal/core/rest/RESTConfiguration.java b/src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java similarity index 67% rename from src/main/java/com/paypal/core/rest/RESTConfiguration.java rename to src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java index f441bd6..2bd0bb0 100644 --- a/src/main/java/com/paypal/core/rest/RESTConfiguration.java +++ b/src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java @@ -17,11 +17,23 @@ import com.paypal.sdk.util.UserAgentHeader; /** - * RESTConfiguration helps {@link PayPalResource} with state dependent utility - * methods + * RESTApiCallPreHandler acts as a {@link APICallPreHandler} for REST API calls. The implementation + * is PayPal specific, To do custom implementation override the protected methods */ -public class RESTConfiguration implements APICallPreHandler { - +public class RESTAPICallPreHandler implements APICallPreHandler { + + /* + * RESTApiCallPreHandler requires a configuration system to function properly. The configuration + * is initialized to default in PayPalResource class if no configuration methods initConfig(..) + * was attempted before making the API call. The users can override this default file 'sdk_config.properties' + * by choosing different version of PayPalResource.initConfi(...) and passing their custom configuration. + * Initializing to default means the system looks for a + * file specifically named 'sdk_config.properties' in the classpath and reads the configuration + * from there. 'Dynamic Configuration' enables the users to pass custom configuration (per call basis) + * as a Map object to override the default behavior for the system to function. For Dynamic configuration + * to take effect create a Map of custom configuration and set it in APIContext object, choose the overloaded + * method of the Resource class that takes APIContext object as a parameter and pass the APIContext object. + */ /** * Configuration Map used for dynamic configuration */ @@ -63,7 +75,7 @@ public class RESTConfiguration implements APICallPreHandler { * @param configurationMap * Map used for dynamic configuration */ - public RESTConfiguration(Map configurationMap) { + public RESTAPICallPreHandler(Map configurationMap) { this.configurationMap = SDKUtil.combineDefaultMap(configurationMap); } @@ -75,7 +87,7 @@ public RESTConfiguration(Map configurationMap) { * @param headersMap * Headers Map */ - public RESTConfiguration(Map configurationMap, + public RESTAPICallPreHandler(Map configurationMap, Map headersMap) { this(configurationMap); this.headersMap = (headersMap == null) ? Collections @@ -125,11 +137,19 @@ public Map getHeaderMap() { /** * Returns the base URL configured in application resources + * or {@link Map} passed for dynamic configuration * - * @return Base {@link URL} - * @throws MalformedURLException + * @return BaseUrl ending with a '/' character {@link URL} + * @throws MalformedURLException if endpoint cannot be found or formed */ public URL getBaseURL() throws MalformedURLException { + + /* + * Check for property 'service.EndPoint' in the configuration, + * if not found, check for 'mode' property in the configuration + * and default endpoint to PayPal sandbox or live endpoints. + * Throw exception if the above rules fail + */ if (url == null) { String urlString = this.configurationMap.get(Constants.ENDPOINT); if (urlString == null || urlString.length() <= 0) { @@ -168,7 +188,7 @@ public void setUrl(String urlString) throws MalformedURLException { /** * Returns User-Agent header * - * @return + * @return {@link Map} storing the User-Agent header */ protected Map formUserAgentHeader() { UserAgentHeader userAgentHeader = new UserAgentHeader( @@ -228,7 +248,17 @@ public void validate() throws ClientActionRequiredException { // TODO } + /** + * Override this method to process EndPoint + * @return Endpoint as String + */ protected String getProcessedEndPoint() { + + /* + * Process the EndPoint to append the resourcePath sent as + * a part of the method call with the base endPoint retrieved + * from configuration system + */ String endPoint = null; try { endPoint = getBaseURL().toURI().resolve(resourcePath).toString(); @@ -240,7 +270,19 @@ protected String getProcessedEndPoint() { return endPoint; } + /** + * Override this method to return a {@link Map} of HTTP headers + * @return {@link Map} of HTTP headers + */ protected Map getProcessedHeaderMap() { + + /* + * The implementation is PayPal specific. The Authorization header is + * formed for OAuth or Basic, for OAuth system the authorization token + * passed as a parameter is used in creation of HTTP header, for Basic + * Authorization the ClientID and ClientSecret passed as parameters are + * used after a Base64 encoding. + */ Map headers = new HashMap(); if (authorizationToken != null && authorizationToken.trim().length() > 0) { @@ -255,9 +297,18 @@ && getClientSecret().trim().length() > 0) { // TODO } } + + /* + * Appends request Id which is used by PayPal API service for + * Idempotency + */ if (requestId != null && requestId.length() > 0) { headers.put(Constants.PAYPAL_REQUEST_ID_HEADER, requestId); } + + /* + * Add User-Agent header for tracking in PayPal system + */ headers.putAll(formUserAgentHeader()); // Add any custom headers @@ -267,7 +318,15 @@ && getClientSecret().trim().length() > 0) { return headers; } + /** + * Override this method to process payload for processing + * @return PayLoad as String + */ protected String getProcessedPayLoad() { + /* + * Since the REST API of PayPal depends on json, which is + * well formed, no additional processing is required. + */ return payLoad; } diff --git a/src/main/java/com/paypal/core/rest/RESTUtil.java b/src/main/java/com/paypal/core/rest/RESTUtil.java index 38d779c..94924f9 100644 --- a/src/main/java/com/paypal/core/rest/RESTUtil.java +++ b/src/main/java/com/paypal/core/rest/RESTUtil.java @@ -33,19 +33,19 @@ public static String formatURIPath(String pattern, Object[] parameters) { // Form a object array using the passed CreateFromAuthorizationCodeParameters finalParameters = splitParameters(pattern, ((CreateFromAuthorizationCodeParameters) parameters[0]).getContainerMap()); - } if (parameters != null && parameters.length == 1 + } else if (parameters != null && parameters.length == 1 && parameters[0] instanceof CreateFromRefreshTokenParameters) { // Form a object array using the passed CreateFromRefreshTokenParameters finalParameters = splitParameters(pattern, ((CreateFromRefreshTokenParameters) parameters[0]).getContainerMap()); - } if (parameters != null && parameters.length == 1 + } else if (parameters != null && parameters.length == 1 && parameters[0] instanceof UserinfoParameters) { // Form a object array using the passed UserinfoParameters finalParameters = splitParameters(pattern, ((UserinfoParameters) parameters[0]).getContainerMap()); - } if (parameters != null && parameters.length == 1 + } else if (parameters != null && parameters.length == 1 && parameters[0] instanceof QueryParameters) { // Form a object array using the passed UserinfoParameters diff --git a/src/test/java/com/paypal/core/OpenIdTest.java b/src/test/java/com/paypal/core/OpenIdTest.java index 1cf282a..7567eb0 100644 --- a/src/test/java/com/paypal/core/OpenIdTest.java +++ b/src/test/java/com/paypal/core/OpenIdTest.java @@ -28,16 +28,16 @@ public class OpenIdTest { Map configurationMap = new HashMap(); public OpenIdTest() { -// configurationMap.put("clientId", ""); -// configurationMap.put("clientSecret", ""); -// configurationMap.put("mode", "live"); + /*configurationMap.put("clientId", ""); + configurationMap.put("clientSecret", ""); + configurationMap.put("mode", "live");*/ } @Test(enabled = false) public void testCreateFromAuthorizationCodeDynamic() throws PayPalRESTException, UnsupportedEncodingException { CreateFromAuthorizationCodeParameters param = new CreateFromAuthorizationCodeParameters(); - param.setCode("74N5JOwI_TDXuP6ZyhQhyw3tCk6i0B6q3ztOlMWQuNHptgQ41dQfgapu_eZ1k77U6XuHhYSwdxUXjXkagmEjr8j24AUzQa2GJPqHOh273PSmPcXO"); + param.setCode("xxxxx"); APIContext apiContext = new APIContext(); apiContext.setConfigurationMap(configurationMap); info = Tokeninfo.createFromAuthorizationCode(apiContext, param); diff --git a/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java b/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java index 31ca12b..f538307 100644 --- a/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java +++ b/src/test/java/com/paypal/core/rest/RESTConfigurationTestCase.java @@ -16,7 +16,7 @@ public void testRESTConfiguration() { try { Map configurationMap = new HashMap(); configurationMap.put("service.EndPoint", "https://localhost.sandbox.paypal.com"); - RESTConfiguration restConfiguration = new RESTConfiguration(configurationMap); + RESTAPICallPreHandler restConfiguration = new RESTAPICallPreHandler(configurationMap); restConfiguration.setResourcePath("/a/b/c"); URL url = restConfiguration.getBaseURL(); Assert.assertEquals(true, url.toString().endsWith("/")); @@ -29,7 +29,7 @@ public void testRESTConfiguration() { public void testRESTHeaderConfiguration() { Map configurationMap = new HashMap(); configurationMap.put("service.EndPoint", "https://localhost.sandbox.paypal.com"); - RESTConfiguration restConfiguration = new RESTConfiguration(configurationMap); + RESTAPICallPreHandler restConfiguration = new RESTAPICallPreHandler(configurationMap); restConfiguration.setResourcePath("/a/b/c"); Map headers = restConfiguration.getHeaderMap(); Assert.assertEquals(headers.size() != 0, true); @@ -44,7 +44,7 @@ public void testRESTConfigurationURL() { try { Map configurationMap = new HashMap(); configurationMap.put("service.EndPoint", "https://localhost.sandbox.paypal.com"); - RESTConfiguration restConfiguration = new RESTConfiguration(configurationMap); + RESTAPICallPreHandler restConfiguration = new RESTAPICallPreHandler(configurationMap); restConfiguration.setResourcePath("/a/b/c"); String urlString = "https://sample.com"; restConfiguration.setUrl(urlString); From 1aaee59d2fc399110592d10ca9e24694079b49df Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Tue, 25 Jun 2013 17:41:45 +0530 Subject: [PATCH 4/8] Update document comments. Moved request payload of OAuthTokenCredential as protected method and added a setter for uri part --- .../java/com/paypal/core/rest/APIContext.java | 8 +-- .../core/rest/OAuthTokenCredential.java | 62 ++++++++++++++++--- .../paypal/core/rest/PayPalRESTException.java | 5 ++ .../java/com/paypal/core/rest/RESTUtil.java | 6 +- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/paypal/core/rest/APIContext.java b/src/main/java/com/paypal/core/rest/APIContext.java index f815e05..aca4f05 100644 --- a/src/main/java/com/paypal/core/rest/APIContext.java +++ b/src/main/java/com/paypal/core/rest/APIContext.java @@ -62,10 +62,10 @@ public APIContext(String accessToken) { * API service. The token should be of the form 'Bearer xxxx..'. * See {@link OAuthTokenCredential} to generate OAuthToken * @param requestId - * Unique requestId required for the call. Idempotency id, Calling - * setMaskRequestId(true) will override the requestId getter to - * return null, which can be used by the client to forcibly not - * sent requestId in the API call. + * Unique requestId required for the call. Idempotency id, + * Calling setMaskRequestId(true) will override the requestId + * getter to return null, which can be used by the client (null + * check) to forcibly not sent requestId in the API call. */ public APIContext(String accessToken, String requestId) { this(accessToken); diff --git a/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java b/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java index 06ed2eb..f22110a 100644 --- a/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java +++ b/src/main/java/com/paypal/core/rest/OAuthTokenCredential.java @@ -16,12 +16,29 @@ import com.paypal.core.credential.ICredential; import com.paypal.sdk.util.UserAgentHeader; +/** + * OAuthTokenCredential is used for generation of OAuth Token used by PayPal + * REST API service. ClientID and ClientSecret are required by the class to + * generate OAuth Token, the resulting token is of the form "Bearer xxxxxx". The + * class has two constructors, one of it taking an additional configuration map + * used for dynamic configuration. When using the constructor with out + * configuration map the endpoint is fetched from the configuration that is used + * during initialization. See {@link PayPalResource} for configuring the system. + * When using a configuration map the class expects an entry by the name + * "oauth.EndPoint" or "service.EndPoint" to retrieve the value of the endpoint + * for the OAuth Service. If either are not present the configuration should + * have a entry by the name "mode" with values sandbox or live wherein the + * corresponding endpoints are default to PayPal endpoints. + * + * @author kjayakumar + * + */ public final class OAuthTokenCredential implements ICredential { /** * OAuth URI path parameter */ - private static final String OAUTH_TOKEN_PATH = "/v1/oauth2/token"; + private static String OAUTH_TOKEN_PATH = "/v1/oauth2/token"; /** * Client ID for OAuth @@ -43,6 +60,17 @@ public final class OAuthTokenCredential implements ICredential { */ private Map configurationMap; + /** + * Sets the URI path for the OAuth Token service. If not set it defaults to + * "/v1/oauth2/token" + * + * @param oauthTokenPath + * the URI part to set + */ + public static void setOAUTH_TOKEN_PATH(String oauthTokenPath) { + OAUTH_TOKEN_PATH = oauthTokenPath; + } + /** * @param clientID * Client ID for the OAuth @@ -58,10 +86,17 @@ public OAuthTokenCredential(String clientID, String clientSecret) { } /** + * Configuration Constructor for dynamic configuration + * * @param clientID * Client ID for the OAuth * @param clientSecret * Client Secret for OAuth + * @param configurationMap + * Dynamic configuration map which should have an entry for + * 'oauth.EndPoint' or 'service.EndPoint'. If either are not + * present then there should be entry for 'mode' with values + * sandbox/live, wherein PayPals endpoints are used. */ public OAuthTokenCredential(String clientID, String clientSecret, Map configurationMap) { @@ -73,16 +108,13 @@ public OAuthTokenCredential(String clientID, String clientSecret, /** * Computes Access Token by placing a call to OAuth server using ClientID - * and ClientSecret. The token is appended to the token type. + * and ClientSecret. The token is appended to the token type (Bearer). * * @return the accessToken * @throws PayPalRESTException */ public String getAccessToken() throws PayPalRESTException { if (accessToken == null) { - // Write Logic for passing in Detail to Identity Api Serv and - // computing the token - // Set the Value inside the accessToken and result accessToken = generateAccessToken(); } return accessToken; @@ -125,12 +157,13 @@ private String generateOAuthToken(String base64ClientID) httpConfiguration = getOAuthHttpConfiguration(); connection.createAndconfigureHttpConnection(httpConfiguration); Map headers = new HashMap(); - headers.put(Constants.AUTHORIZATION_HEADER, "Basic " + base64ClientID); + headers.put(Constants.AUTHORIZATION_HEADER, "Basic " + + base64ClientID); headers.put(Constants.HTTP_ACCEPT_HEADER, "*/*"); UserAgentHeader userAgentHeader = new UserAgentHeader( PayPalResource.SDK_ID, PayPalResource.SDK_VERSION); headers.putAll(userAgentHeader.getHeader()); - String postRequest = "grant_type=client_credentials"; + String postRequest = getRequestPayload(); String jsonResponse = connection.execute("", postRequest, headers); JsonParser parser = new JsonParser(); JsonElement jsonElement = parser.parse(jsonResponse); @@ -145,6 +178,16 @@ private String generateOAuthToken(String base64ClientID) return generatedToken; } + /** + * Returns the request payload for OAuth Service. Override this method to + * alter the payload + * + * @return Payload as String + */ + protected String getRequestPayload() { + return "grant_type=client_credentials"; + } + /* * Get HttpConfiguration object for OAuth server */ @@ -152,9 +195,8 @@ private HttpConfiguration getOAuthHttpConfiguration() { HttpConfiguration httpConfiguration = new HttpConfiguration(); httpConfiguration .setHttpMethod(Constants.HTTP_CONFIG_DEFAULT_HTTP_METHOD); - String endPointUrl = (configurationMap.get(Constants.OAUTH_ENDPOINT) != null - && configurationMap.get(Constants.OAUTH_ENDPOINT).trim() - .length() >= 0) ? configurationMap + String endPointUrl = (configurationMap.get(Constants.OAUTH_ENDPOINT) != null && configurationMap + .get(Constants.OAUTH_ENDPOINT).trim().length() >= 0) ? configurationMap .get(Constants.OAUTH_ENDPOINT) : configurationMap .get(Constants.ENDPOINT); if (endPointUrl == null || endPointUrl.trim().length() <= 0) { diff --git a/src/main/java/com/paypal/core/rest/PayPalRESTException.java b/src/main/java/com/paypal/core/rest/PayPalRESTException.java index 0a1dc13..f5b5569 100644 --- a/src/main/java/com/paypal/core/rest/PayPalRESTException.java +++ b/src/main/java/com/paypal/core/rest/PayPalRESTException.java @@ -5,6 +5,11 @@ */ public class PayPalRESTException extends Exception { + /** + * Serial Version ID + */ + private static final long serialVersionUID = 1L; + public PayPalRESTException(String message) { super(message); } diff --git a/src/main/java/com/paypal/core/rest/RESTUtil.java b/src/main/java/com/paypal/core/rest/RESTUtil.java index 94924f9..dc34afe 100644 --- a/src/main/java/com/paypal/core/rest/RESTUtil.java +++ b/src/main/java/com/paypal/core/rest/RESTUtil.java @@ -9,7 +9,11 @@ import com.paypal.sdk.openidconnect.CreateFromRefreshTokenParameters; import com.paypal.sdk.openidconnect.UserinfoParameters; - +/** + * RESTUtil acts as utility class used by REST API system + * @author kjayakumar + * + */ public final class RESTUtil { private RESTUtil() {} From 072e1f6f86b520f869e80f7bf009f4f62351e6de Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Mon, 1 Jul 2013 16:36:59 +0530 Subject: [PATCH 5/8] Updated developer and javadoc comments, Updated code on JSONFormatter to include setter for FieldNamingPolicy, Updated RESTUtil with new formatURIPath method --- .../java/com/paypal/core/rest/APIContext.java | 12 +- .../com/paypal/core/rest/JSONFormatter.java | 38 ++++-- .../java/com/paypal/core/rest/RESTUtil.java | 110 ++++++++++++++++-- .../com/paypal/core/rest/RESTUtilTest.java | 4 +- 4 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/paypal/core/rest/APIContext.java b/src/main/java/com/paypal/core/rest/APIContext.java index aca4f05..afa141c 100644 --- a/src/main/java/com/paypal/core/rest/APIContext.java +++ b/src/main/java/com/paypal/core/rest/APIContext.java @@ -4,9 +4,11 @@ import java.util.UUID; /** - * APIContext holds wire-level parameters for the API call. - * AccessToken is treated as a mandatory parameter. RequestId is generated if - * not supplied + * APIContext wraps wire-level parameters for the API call. + * AccessToken, which is essentially a OAuth token, is treated as a mandatory + * parameter for (PayPal REST APIs). RequestId is generated if not supplied for + * marking Idempotency of the API call. OAuth token can be generated using + * {@link OAuthTokenCredential} * * @author kjayakumar * @@ -45,7 +47,9 @@ public APIContext() { * will override the requestId getter to return null * * @param accessToken - * AccessToken required for the call. + * OAuthToken required for the call. OAuth token used by the REST + * API service. The token should be of the form 'Bearer xxxx..'. + * See {@link OAuthTokenCredential} to generate OAuthToken */ public APIContext(String accessToken) { if (accessToken == null || accessToken.length() <= 0) { diff --git a/src/main/java/com/paypal/core/rest/JSONFormatter.java b/src/main/java/com/paypal/core/rest/JSONFormatter.java index 4aa8f22..98c4155 100644 --- a/src/main/java/com/paypal/core/rest/JSONFormatter.java +++ b/src/main/java/com/paypal/core/rest/JSONFormatter.java @@ -5,22 +5,46 @@ import com.google.gson.GsonBuilder; /** - * JSONFormatter converts objects to JSON representation and vice-versa + * JSONFormatter converts objects to JSON representation and vice-versa. This + * class depends on Google's GSON library to do the transformation. This class + * is not thread-safe. * * @author kjayakumar * */ public final class JSONFormatter { - - private JSONFormatter() {} + + /* + * JSONFormatter is coupled to the stubs generated using the SDK generator. + * Since PayPal REST APIs support only JSON, this class is bound to the + * stubs for their json representation. + */ + private JSONFormatter() { + } + + /** + * FieldNamingPolicy used by the underlying Gson library. Alter this + * property to set a fieldnamingpolicy other than + * LOWER_CASE_WITH_UNDERSCORES used by PayPal REST APIs + */ + private static FieldNamingPolicy FIELD_NAMING_POLICY = FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES; /** * Gson */ - public static final Gson GSON = new GsonBuilder() - .setPrettyPrinting() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .create(); + public static Gson GSON = new GsonBuilder().setPrettyPrinting() + .setFieldNamingPolicy(FIELD_NAMING_POLICY).create(); + + /** + * Set a format for gson FIELD_NAMING_POLICY. See {@link FieldNamingPolicy} + * + * @param FIELD_NAMING_POLICY + */ + public static final void setFIELD_NAMING_POLICY( + FieldNamingPolicy FIELD_NAMING_POLICY) { + GSON = new GsonBuilder().setPrettyPrinting() + .setFieldNamingPolicy(FIELD_NAMING_POLICY).create(); + } /** * Converts a Raw Type to JSON String diff --git a/src/main/java/com/paypal/core/rest/RESTUtil.java b/src/main/java/com/paypal/core/rest/RESTUtil.java index dc34afe..a6d59bf 100644 --- a/src/main/java/com/paypal/core/rest/RESTUtil.java +++ b/src/main/java/com/paypal/core/rest/RESTUtil.java @@ -1,9 +1,12 @@ package com.paypal.core.rest; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import com.paypal.sdk.openidconnect.CreateFromAuthorizationCodeParameters; import com.paypal.sdk.openidconnect.CreateFromRefreshTokenParameters; @@ -11,12 +14,14 @@ /** * RESTUtil acts as utility class used by REST API system + * * @author kjayakumar - * + * */ public final class RESTUtil { - - private RESTUtil() {} + + private RESTUtil() { + } /** * Formats the URI path for REST calls. @@ -31,18 +36,24 @@ public static String formatURIPath(String pattern, Object[] parameters) { String formattedPath = null; Object[] finalParameters = null; if (pattern != null) { - if (parameters != null && parameters.length == 1 + if (parameters != null + && parameters.length == 1 && parameters[0] instanceof CreateFromAuthorizationCodeParameters) { - // Form a object array using the passed CreateFromAuthorizationCodeParameters + // Form a object array using the passed + // CreateFromAuthorizationCodeParameters finalParameters = splitParameters(pattern, - ((CreateFromAuthorizationCodeParameters) parameters[0]).getContainerMap()); - } else if (parameters != null && parameters.length == 1 + ((CreateFromAuthorizationCodeParameters) parameters[0]) + .getContainerMap()); + } else if (parameters != null + && parameters.length == 1 && parameters[0] instanceof CreateFromRefreshTokenParameters) { - // Form a object array using the passed CreateFromRefreshTokenParameters + // Form a object array using the passed + // CreateFromRefreshTokenParameters finalParameters = splitParameters(pattern, - ((CreateFromRefreshTokenParameters) parameters[0]).getContainerMap()); + ((CreateFromRefreshTokenParameters) parameters[0]) + .getContainerMap()); } else if (parameters != null && parameters.length == 1 && parameters[0] instanceof UserinfoParameters) { @@ -57,14 +68,14 @@ public static String formatURIPath(String pattern, Object[] parameters) { ((QueryParameters) parameters[0]).getContainerMap()); } else if (parameters != null && parameters.length == 1 && parameters[0] instanceof Map) { - + // Form a object array using the passed Map finalParameters = splitParameters(pattern, ((Map) parameters[0])); } else { finalParameters = parameters; } - + // Perform a simple message formatting String fString = MessageFormat.format(pattern, finalParameters); @@ -74,6 +85,83 @@ public static String formatURIPath(String pattern, Object[] parameters) { return formattedPath; } + /** + * Formats the URI path for REST calls. Replaces any occurrences of the form + * {name} in pattern with the corresponding value of key name in the passes + * {@link Map} + * + * @param pattern + * URI pattern with named place holders + * @param pathParameters + * Parameter {@link Map} + * @return Processed URI path + * @throws PayPalRESTException + */ + public static String formatURIPath(String pattern, + Map pathParameters) throws PayPalRESTException { + return formatURIPath(pattern, pathParameters, null); + } + + /** + * Formats the URI path for REST calls. Replaces any occurrences of the form + * {name} in pattern with the corresponding value of key name in the passes + * {@link Map}. Query parameters are appended to the end of the URI path + * + * @param pattern + * URI pattern with named place holders + * @param pathParameters + * Parameter {@link Map} + * @param queryParameters + * Query parameters {@link Map} + * @return Processed URI path + * @throws PayPalRESTException + */ + public static String formatURIPath(String pattern, + Map pathParameters, + Map queryParameters) throws PayPalRESTException { + String formattedURIPath = null; + if (pattern != null && pattern.trim().length() > 0 + && pathParameters != null && pathParameters.size() > 0) { + for (Entry entry : pathParameters.entrySet()) { + String placeHolderName = "{" + entry.getKey().trim() + "}"; + if (pattern.contains(placeHolderName)) { + pattern = pattern.replace(placeHolderName, entry.getValue() + .trim()); + } + } + + } + formattedURIPath = pattern; + if (queryParameters != null && queryParameters.size() > 0) { + StringBuilder stringBuilder = new StringBuilder(formattedURIPath); + if (stringBuilder.toString().contains("?") + && !stringBuilder.toString().endsWith("&")) { + stringBuilder.append("&"); + } else { + stringBuilder.append("?"); + } + for (Entry entry : queryParameters.entrySet()) { + try { + stringBuilder + .append(URLEncoder.encode(entry.getKey(), "UTF-8")) + .append("=") + .append(URLEncoder.encode(entry.getValue(), "UTF-8")) + .append("&"); + } catch (UnsupportedEncodingException e) { + // TODO + } + } + formattedURIPath = stringBuilder.toString(); + } + if (formattedURIPath.contains("{") || formattedURIPath.contains("}")) { + throw new PayPalRESTException("Unable to formatURI Path : " + + formattedURIPath + + ", unable to replace placeholders with the map : " + + pathParameters); + } + return formattedURIPath; + } + /** * Remove null parameters from query string * diff --git a/src/test/java/com/paypal/core/rest/RESTUtilTest.java b/src/test/java/com/paypal/core/rest/RESTUtilTest.java index b211dc1..51c1c87 100644 --- a/src/test/java/com/paypal/core/rest/RESTUtilTest.java +++ b/src/test/java/com/paypal/core/rest/RESTUtilTest.java @@ -7,14 +7,14 @@ public class RESTUtilTest { @Test() public void testFormatURIPathForNull() { - String nullString = RESTUtil.formatURIPath(null, null); + String nullString = RESTUtil.formatURIPath((String) null, (Object []) null); Assert.assertNull(nullString); } @Test(dependsOnMethods = {"testFormatURIPathForNull"}) public void testFormatURIPathNoPattern() { String pattern = "/a/b/c"; - String uriPath = RESTUtil.formatURIPath(pattern, null); + String uriPath = RESTUtil.formatURIPath(pattern, (Object []) null); Assert.assertEquals(uriPath, pattern); } From db01548723b14c490f40db950a522e2de519ac8f Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Tue, 2 Jul 2013 11:35:45 +0530 Subject: [PATCH 6/8] Fixed queryParameter addition logic flaw in RESTUtil, updated testcases --- .../java/com/paypal/core/rest/RESTUtil.java | 8 +- .../com/paypal/core/rest/RESTUtilTest.java | 170 +++++++++++++++--- 2 files changed, 155 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/paypal/core/rest/RESTUtil.java b/src/main/java/com/paypal/core/rest/RESTUtil.java index a6d59bf..27a8758 100644 --- a/src/main/java/com/paypal/core/rest/RESTUtil.java +++ b/src/main/java/com/paypal/core/rest/RESTUtil.java @@ -134,9 +134,11 @@ public static String formatURIPath(String pattern, formattedURIPath = pattern; if (queryParameters != null && queryParameters.size() > 0) { StringBuilder stringBuilder = new StringBuilder(formattedURIPath); - if (stringBuilder.toString().contains("?") - && !stringBuilder.toString().endsWith("&")) { - stringBuilder.append("&"); + if (stringBuilder.toString().contains("?")) { + if (!(stringBuilder.toString().endsWith("?") + || stringBuilder.toString().endsWith("&"))) { + stringBuilder.append("&"); + } } else { stringBuilder.append("?"); } diff --git a/src/test/java/com/paypal/core/rest/RESTUtilTest.java b/src/test/java/com/paypal/core/rest/RESTUtilTest.java index 51c1c87..09a603b 100644 --- a/src/test/java/com/paypal/core/rest/RESTUtilTest.java +++ b/src/test/java/com/paypal/core/rest/RESTUtilTest.java @@ -1,61 +1,191 @@ package com.paypal.core.rest; +import java.util.HashMap; +import java.util.Map; + import org.testng.Assert; import org.testng.annotations.Test; public class RESTUtilTest { - + @Test() public void testFormatURIPathForNull() { - String nullString = RESTUtil.formatURIPath((String) null, (Object []) null); + String nullString = RESTUtil.formatURIPath((String) null, + (Object[]) null); Assert.assertNull(nullString); } - - @Test(dependsOnMethods = {"testFormatURIPathForNull"}) + + @Test(dependsOnMethods = { "testFormatURIPathForNull" }) public void testFormatURIPathNoPattern() { String pattern = "/a/b/c"; - String uriPath = RESTUtil.formatURIPath(pattern, (Object []) null); + String uriPath = RESTUtil.formatURIPath(pattern, (Object[]) null); Assert.assertEquals(uriPath, pattern); } - - @Test(dependsOnMethods = {"testFormatURIPathNoPattern"}) + + @Test(dependsOnMethods = { "testFormatURIPathNoPattern" }) public void testFormatURIPathNoQS() { String pattern = "/a/b/{0}"; - Object[] parameters = new Object[] {"replace"}; + Object[] parameters = new Object[] { "replace" }; String uriPath = RESTUtil.formatURIPath(pattern, parameters); Assert.assertEquals(uriPath, "/a/b/replace"); } - - @Test(dependsOnMethods = {"testFormatURIPathNoQS"}) + + @Test(dependsOnMethods = { "testFormatURIPathNoQS" }) public void testFormatURIPath() { String pattern = "/a/b/{0}?name={1}"; - Object[] parameters = new Object[] {"replace", "nameValue"}; + Object[] parameters = new Object[] { "replace", "nameValue" }; String uriPath = RESTUtil.formatURIPath(pattern, parameters); Assert.assertEquals(uriPath, "/a/b/replace?name=nameValue"); } - - @Test(dependsOnMethods = {"testFormatURIPath"}) + + @Test(dependsOnMethods = { "testFormatURIPath" }) public void testFormatURIPathWithNull() { String pattern = "/a/b/{0}?name={1}&age={2}"; - Object[] parameters = new Object[] {"replace", "nameValue", null}; + Object[] parameters = new Object[] { "replace", "nameValue", null }; String uriPath = RESTUtil.formatURIPath(pattern, parameters); Assert.assertEquals(uriPath, "/a/b/replace?name=nameValue"); } - - @Test(dependsOnMethods = {"testFormatURIPathWithNull"}) + + @Test(dependsOnMethods = { "testFormatURIPathWithNull" }) public void testFormatURIPathWithEmpty() { String pattern = "/a/b/{0}?name={1}&age="; - Object[] parameters = new Object[] {"replace", "nameValue", null}; + Object[] parameters = new Object[] { "replace", "nameValue", null }; String uriPath = RESTUtil.formatURIPath(pattern, parameters); Assert.assertEquals(uriPath, "/a/b/replace?name=nameValue"); } - - @Test(dependsOnMethods = {"testFormatURIPathWithEmpty"}) + + @Test(dependsOnMethods = { "testFormatURIPathWithEmpty" }) public void testFormatURIPathTwoQS() { String pattern = "/a/b/{0}?name={1}&age={2}"; - Object[] parameters = new Object[] {"replace", "nameValue", "1"}; + Object[] parameters = new Object[] { "replace", "nameValue", "1" }; String uriPath = RESTUtil.formatURIPath(pattern, parameters); Assert.assertEquals(uriPath, "/a/b/replace?name=nameValue&age=1"); } + @Test(dependsOnMethods = { "testFormatURIPathTwoQS" }) + public void testFormatURIPathMap() throws PayPalRESTException { + String pattern = "/a/b/{first}/{second}"; + Map pathParameters = new HashMap(); + pathParameters.put("first", "value1"); + pathParameters.put("second", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters); + Assert.assertEquals(uriPath, "/a/b/value1/value2"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMap" }) + public void testFormatURIPathMapTraillingSlash() throws PayPalRESTException { + String pattern = "/a/b/{first}/{second}/"; + Map pathParameters = new HashMap(); + pathParameters.put("first", "value1"); + pathParameters.put("second", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters); + Assert.assertEquals(uriPath, "/a/b/value1/value2/"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapTraillingSlash" }) + public void testFormatURIPathMapNullMap() throws PayPalRESTException { + String pattern = "/a/b/first/second"; + String uriPath = RESTUtil.formatURIPath(pattern, + (Map) null); + Assert.assertEquals(uriPath, "/a/b/first/second"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapNullMap" }) + public void testFormatURIPathMapIncorrectMap() throws PayPalRESTException { + String pattern = "/a/b/first/second"; + Map pathParameters = new HashMap(); + pathParameters.put("invalid1", "value1"); + pathParameters.put("invalid2", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters); + Assert.assertEquals(uriPath, "/a/b/first/second"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapIncorrectMap" }, expectedExceptions = PayPalRESTException.class) + public void testFormatURIPathMapInsufficientMap() + throws PayPalRESTException { + String pattern = "/a/b/{first}/{second}"; + Map pathParameters = new HashMap(); + pathParameters.put("first", "value1"); + RESTUtil.formatURIPath(pattern, pathParameters); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapInsufficientMap" }) + public void testFormatURIPathMapNullQueryMap() throws PayPalRESTException { + String pattern = "/a/b/{first}/{second}"; + Map pathParameters = new HashMap(); + pathParameters.put("first", "value1"); + pathParameters.put("second", "value2"); + Map queryParameters = null; + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters, + queryParameters); + Assert.assertEquals(uriPath, "/a/b/value1/value2"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapNullQueryMap" }) + public void testFormatURIPathMapEmptyQueryMap() throws PayPalRESTException { + String pattern = "/a/b/{first}/{second}"; + Map pathParameters = new HashMap(); + pathParameters.put("first", "value1"); + pathParameters.put("second", "value2"); + Map queryParameters = new HashMap(); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters, + queryParameters); + Assert.assertEquals(uriPath, "/a/b/value1/value2"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapEmptyQueryMap" }) + public void testFormatURIPathMapQueryMap() throws PayPalRESTException { + String pattern = "/a/b/first/second"; + Map pathParameters = new HashMap(); + Map queryParameters = new HashMap(); + queryParameters.put("query1", "value1"); + queryParameters.put("query2", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters, + queryParameters); + Assert.assertEquals(uriPath, + "/a/b/first/second?query1=value1&query2=value2&"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapQueryMap" }) + public void testFormatURIPathMapQueryMapQueryURIPath() + throws PayPalRESTException { + String pattern = "/a/b/first/second?"; + Map pathParameters = new HashMap(); + Map queryParameters = new HashMap(); + queryParameters.put("query1", "value1"); + queryParameters.put("query2", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters, + queryParameters); + Assert.assertEquals(uriPath, + "/a/b/first/second?query1=value1&query2=value2&"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapQueryMapQueryURIPath" }) + public void testFormatURIPathMapQueryMapQueryURIPathEncode() + throws PayPalRESTException { + String pattern = "/a/b/first/second"; + Map pathParameters = new HashMap(); + Map queryParameters = new HashMap(); + queryParameters.put("query1", "value&1"); + queryParameters.put("query2", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters, + queryParameters); + Assert.assertEquals(uriPath, + "/a/b/first/second?query1=value%261&query2=value2&"); + } + + @Test(dependsOnMethods = { "testFormatURIPathMapQueryMapQueryURIPathEncode" }) + public void testFormatURIPathMapQueryMapQueryValueURIPath() + throws PayPalRESTException { + String pattern = "/a/b/first/second?alreadypresent=value"; + Map pathParameters = new HashMap(); + Map queryParameters = new HashMap(); + queryParameters.put("query1", "value1"); + queryParameters.put("query2", "value2"); + String uriPath = RESTUtil.formatURIPath(pattern, pathParameters, + queryParameters); + Assert.assertEquals(uriPath, + "/a/b/first/second?alreadypresent=value&query1=value1&query2=value2&"); + } + } From 3a1b0a422204faaad91669f6a04eb93f61fcc5d2 Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Tue, 9 Jul 2013 16:00:07 +0530 Subject: [PATCH 7/8] Update APIContext to hold custom headers, deprecated PayPalResource configureAndExecute() method that takes headers argument. --- .../java/com/paypal/core/rest/APIContext.java | 21 +++++++++++++- .../com/paypal/core/rest/PayPalResource.java | 14 ++++------ .../paypal/sdk/openidconnect/Tokeninfo.java | 28 +++++++++++++++---- .../paypal/sdk/openidconnect/Userinfo.java | 8 ++++-- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/paypal/core/rest/APIContext.java b/src/main/java/com/paypal/core/rest/APIContext.java index afa141c..08f15d9 100644 --- a/src/main/java/com/paypal/core/rest/APIContext.java +++ b/src/main/java/com/paypal/core/rest/APIContext.java @@ -34,7 +34,12 @@ public class APIContext { * Configuration Map used for dynamic configuration */ private Map configurationMap; - + + /** + * Custom HTTP headers + */ + private Map headersMap; + /** * Default Constructor */ @@ -128,4 +133,18 @@ public void setMaskRequestId(boolean maskRequestId) { this.maskRequestId = maskRequestId; } + /** + * @return the headersMap + */ + public Map getHeadersMap() { + return headersMap; + } + + /** + * @param headersMap the headersMap to set + */ + public void setHeadersMap(Map headersMap) { + this.headersMap = headersMap; + } + } diff --git a/src/main/java/com/paypal/core/rest/PayPalResource.java b/src/main/java/com/paypal/core/rest/PayPalResource.java index d382bf8..eb76d1f 100644 --- a/src/main/java/com/paypal/core/rest/PayPalResource.java +++ b/src/main/java/com/paypal/core/rest/PayPalResource.java @@ -222,18 +222,20 @@ public static T configureAndExecute(APIContext apiContext, Map cMap = null; String accessToken = null; String requestId = null; + Map headersMap = null; if (apiContext != null) { cMap = apiContext.getConfigurationMap(); accessToken = apiContext.getAccessToken(); requestId = apiContext.getRequestId(); + headersMap = apiContext.getHeadersMap(); } return configureAndExecute(cMap, accessToken, httpMethod, resourcePath, - null, payLoad, requestId, clazz); + headersMap, payLoad, requestId, clazz); } /** * Configures and executes REST call: Supports JSON - * + * @deprecated * @param * @param apiContext * {@link APIContext} to be used for the call. @@ -322,16 +324,10 @@ private static T configureAndExecute( * PayPal Request Id * @return */ - protected static APICallPreHandler createAPICallPreHandler( + public static APICallPreHandler createAPICallPreHandler( Map configurationMap, String payLoad, String resourcePath, Map headersMap, String accessToken, String requestId) { - - /* - * Override this method in the subclass level to return - * any implementation of APICallPreHandler for the - * system - */ if (apiCallPreHandlerFactory == null) { setPreHandlerFactory(new RESTAPICallPreHandlerFactory()); } diff --git a/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java b/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java index 3fee9c0..a8f3bc4 100644 --- a/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java +++ b/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java @@ -195,8 +195,11 @@ public static Tokeninfo createFromAuthorizationCode( resourcePath = resourcePath.substring(0, resourcePath.indexOf('?')); Map headersMap = new HashMap(); headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); - return PayPalResource.configureAndExecute(null, HttpMethod.POST, - resourcePath, headersMap, payLoad, Tokeninfo.class); + APIContext apiContext = new APIContext(); + apiContext.setMaskRequestId(true); + apiContext.setHeadersMap(headersMap); + return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, + resourcePath, payLoad, Tokeninfo.class); } /** @@ -220,8 +223,13 @@ public static Tokeninfo createFromAuthorizationCode( resourcePath = resourcePath.substring(0, resourcePath.indexOf('?')); Map headersMap = new HashMap(); headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); + if (apiContext == null) { + apiContext = new APIContext(); + apiContext.setMaskRequestId(true); + } + apiContext.setHeadersMap(headersMap); return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, - resourcePath, headersMap, payLoad, Tokeninfo.class); + resourcePath, payLoad, Tokeninfo.class); } /** @@ -249,8 +257,11 @@ public Tokeninfo createFromRefreshToken( resourcePath = resourcePath.substring(0, resourcePath.indexOf('?')); Map headersMap = new HashMap(); headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); - return PayPalResource.configureAndExecute(null, HttpMethod.POST, - resourcePath, headersMap, payLoad, Tokeninfo.class); + APIContext apiContext = new APIContext(); + apiContext.setMaskRequestId(true); + apiContext.setHeadersMap(headersMap); + return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, + resourcePath, payLoad, Tokeninfo.class); } /** @@ -280,8 +291,13 @@ public Tokeninfo createFromRefreshToken(APIContext apiContext, resourcePath = resourcePath.substring(0, resourcePath.indexOf('?')); Map headersMap = new HashMap(); headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); + if (apiContext == null) { + apiContext = new APIContext(); + apiContext.setMaskRequestId(true); + } + apiContext.setHeadersMap(headersMap); return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, - resourcePath, headersMap, payLoad, Tokeninfo.class); + resourcePath, payLoad, Tokeninfo.class); } /** diff --git a/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java b/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java index e2872f6..1277135 100644 --- a/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java +++ b/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java @@ -444,8 +444,10 @@ public static Userinfo getUserinfo(UserinfoParameters userinfoParameters) Object[] parameters = new Object[] { userinfoParameters }; String resourcePath = RESTUtil.formatURIPath(pattern, parameters); String payLoad = ""; - return PayPalResource.configureAndExecute(null, HttpMethod.GET, - resourcePath, null, payLoad, Userinfo.class); + APIContext apiContext = new APIContext(); + apiContext.setMaskRequestId(true); + return PayPalResource.configureAndExecute(apiContext, HttpMethod.GET, + resourcePath, payLoad, Userinfo.class); } /** @@ -465,7 +467,7 @@ public static Userinfo getUserinfo(APIContext apiContext, String resourcePath = RESTUtil.formatURIPath(pattern, parameters); String payLoad = ""; return PayPalResource.configureAndExecute(apiContext, HttpMethod.GET, - resourcePath, null, payLoad, Userinfo.class); + resourcePath, payLoad, Userinfo.class); } /** From b0e916ac5be318d18ec93c8f447b56144e847e98 Mon Sep 17 00:00:00 2001 From: kumaravel-jayakumar Date: Fri, 12 Jul 2013 15:05:48 +0530 Subject: [PATCH 8/8] Update PayPalResource by removing RESTAPICallPreHandlerFactory, Updated document comments Updated Tokeninfo, Userinfo to call APIContext parameterized method from the other similar method that does not take APIContext object to use headersMap inside APIContext object --- .../com/paypal/core/rest/PayPalResource.java | 139 ++---------------- .../core/rest/RESTAPICallPreHandler.java | 32 ++-- .../paypal/sdk/openidconnect/Tokeninfo.java | 39 +---- .../paypal/sdk/openidconnect/Userinfo.java | 9 +- 4 files changed, 34 insertions(+), 185 deletions(-) diff --git a/src/main/java/com/paypal/core/rest/PayPalResource.java b/src/main/java/com/paypal/core/rest/PayPalResource.java index eb76d1f..ddb5d51 100644 --- a/src/main/java/com/paypal/core/rest/PayPalResource.java +++ b/src/main/java/com/paypal/core/rest/PayPalResource.java @@ -5,13 +5,11 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.Properties; import com.paypal.core.APICallPreHandler; -import com.paypal.core.APICallPreHandlerFactory; import com.paypal.core.ConfigManager; import com.paypal.core.ConnectionManager; import com.paypal.core.Constants; @@ -29,7 +27,7 @@ public abstract class PayPalResource { /* * The class uses an implementation APICallPreHandler (here - * RESTAPICallPreHandler)to get access to endpoint, HTTP headers, payload. + * RESTAPICallPreHandler)to get access to endpoint, HTTP headers, and payload. */ /** * SDK ID used in User-Agent HTTP header @@ -51,11 +49,6 @@ public abstract class PayPalResource { */ private static boolean configInitialized = false; - /** - * Default {@link APICallPreHandlerFactory} - */ - private static APICallPreHandlerFactory apiCallPreHandlerFactory = new RESTAPICallPreHandlerFactory(); - /** * Last request sent to Service */ @@ -66,15 +59,6 @@ public abstract class PayPalResource { */ private static final ThreadLocal LASTRESPONSE = new ThreadLocal(); - /** - * Sets a custom {@link APICallPreHandlerFactory}, the new system may also want to - * override the class level createAPICallPreHandler(...) method - * @param preHandlerFactory the preHandlerFactory to set - */ - static void setPreHandlerFactory(APICallPreHandlerFactory apiCallPreHandlerFactory) { - PayPalResource.apiCallPreHandlerFactory = apiCallPreHandlerFactory; - } - /** * Initialize the system using a File(Properties file). The system is * initialized using the given file and if the initialization succeeds the @@ -179,7 +163,7 @@ public static String getLastResponse() { * @param * Response Type for de-serialization * @param accessToken - * AccessToken to be used for the call. + * OAuth AccessToken to be used for the call. * @param httpMethod * Http Method verb * @param resourcePath @@ -328,17 +312,15 @@ public static APICallPreHandler createAPICallPreHandler( Map configurationMap, String payLoad, String resourcePath, Map headersMap, String accessToken, String requestId) { - if (apiCallPreHandlerFactory == null) { - setPreHandlerFactory(new RESTAPICallPreHandlerFactory()); - } - RESTAPICallPreHandlerFactory restAPICallPreHandlerFactory = (RESTAPICallPreHandlerFactory) apiCallPreHandlerFactory; - restAPICallPreHandlerFactory.setConfigurationMap(configurationMap); - restAPICallPreHandlerFactory.setHeadersMap(headersMap); - restAPICallPreHandlerFactory.setResourcePath(resourcePath); - restAPICallPreHandlerFactory.setRequestId(requestId); - restAPICallPreHandlerFactory.setAuthorizationToken(accessToken); - restAPICallPreHandlerFactory.setPayLoad(payLoad); - return apiCallPreHandlerFactory.createAPICallPreHandler(); + APICallPreHandler apiCallPreHandler = null; + RESTAPICallPreHandler restAPICallPreHandler = new RESTAPICallPreHandler( + configurationMap, headersMap); + restAPICallPreHandler.setResourcePath(resourcePath); + restAPICallPreHandler.setRequestId(requestId); + restAPICallPreHandler.setAuthorizationToken(accessToken); + restAPICallPreHandler.setPayLoad(payLoad); + apiCallPreHandler = restAPICallPreHandler; + return apiCallPreHandler; } /** @@ -438,103 +420,4 @@ private static HttpConfiguration createHttpConfiguration( return httpConfiguration; } - /** - * Implementation of {@link APICallPreHandlerFactory} that returns an instance of - * {@link RESTAPICallPreHandler} - * @author kjayakumar - * - */ - private static class RESTAPICallPreHandlerFactory implements - APICallPreHandlerFactory { - - /** - * Configuration map - */ - Map configurationMap = null; - - /** - * Raw Payload - */ - String payLoad = null; - - /** - * Resource URI path - */ - String resourcePath = null; - - /** - * Custom HTTP headers map - */ - Map headersMap = null; - - /** - * Authorization Token - */ - String authorizationToken = null; - - /** - * Request Id - */ - String requestId = null; - - RESTAPICallPreHandlerFactory() { - - } - - /** - * @param configurationMap the configurationMap to set - */ - void setConfigurationMap(Map configurationMap) { - this.configurationMap = configurationMap; - } - - /** - * @param payLoad the payLoad to set - */ - void setPayLoad(String payLoad) { - this.payLoad = payLoad; - } - - /** - * @param resourcePath the resourcePath to set - */ - void setResourcePath(String resourcePath) { - this.resourcePath = resourcePath; - } - - /** - * @param headersMap the headersMap to set - */ - void setHeadersMap(Map headersMap) { - this.headersMap = headersMap; - } - - /** - * @param authorizationToken the authorizationToken to set - */ - void setAuthorizationToken(String authorizationToken) { - this.authorizationToken = authorizationToken; - } - - /** - * @param requestId the requestId to set - */ - void setRequestId(String requestId) { - this.requestId = requestId; - } - - public APICallPreHandler createAPICallPreHandler() { - APICallPreHandler apiCallPreHandler = null; - RESTAPICallPreHandler restAPICallPreHandler = new RESTAPICallPreHandler( - configurationMap, headersMap); - restAPICallPreHandler.setResourcePath(resourcePath); - restAPICallPreHandler.setRequestId(requestId); - restAPICallPreHandler.setAuthorizationToken(authorizationToken); - restAPICallPreHandler.setPayLoad(payLoad); - apiCallPreHandler = restAPICallPreHandler; - return apiCallPreHandler; - } - - } - } diff --git a/src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java b/src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java index 2bd0bb0..915d789 100644 --- a/src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java +++ b/src/main/java/com/paypal/core/rest/RESTAPICallPreHandler.java @@ -134,6 +134,22 @@ public void setPayLoad(String payLoad) { public Map getHeaderMap() { return getProcessedHeaderMap(); } + + public String getPayLoad() { + return getProcessedPayLoad(); + } + + public String getEndPoint() { + return getProcessedEndPoint(); + } + + public ICredential getCredential() { + return null; + } + + public void validate() throws ClientActionRequiredException { + // TODO + } /** * Returns the base URL configured in application resources @@ -232,21 +248,7 @@ private String generateBase64String(String clientID) return base64ClientID; } - public String getPayLoad() { - return getProcessedPayLoad(); - } - - public String getEndPoint() { - return getProcessedEndPoint(); - } - - public ICredential getCredential() { - return null; - } - - public void validate() throws ClientActionRequiredException { - // TODO - } + /** * Override this method to process EndPoint diff --git a/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java b/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java index a8f3bc4..47e76dc 100644 --- a/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java +++ b/src/main/java/com/paypal/sdk/openidconnect/Tokeninfo.java @@ -188,18 +188,7 @@ public Integer getExpiresIn() { public static Tokeninfo createFromAuthorizationCode( CreateFromAuthorizationCodeParameters createFromAuthorizationCodeParameters) throws PayPalRESTException { - String pattern = "v1/identity/openidconnect/tokenservice ?grant_type={0}&code={1}&redirect_uri={2}"; - Object[] parameters = new Object[] { createFromAuthorizationCodeParameters }; - String resourcePath = RESTUtil.formatURIPath(pattern, parameters); - String payLoad = resourcePath.substring(resourcePath.indexOf('?') + 1); - resourcePath = resourcePath.substring(0, resourcePath.indexOf('?')); - Map headersMap = new HashMap(); - headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); - APIContext apiContext = new APIContext(); - apiContext.setMaskRequestId(true); - apiContext.setHeadersMap(headersMap); - return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, - resourcePath, payLoad, Tokeninfo.class); + return createFromAuthorizationCode(null, createFromAuthorizationCodeParameters); } /** @@ -225,8 +214,8 @@ public static Tokeninfo createFromAuthorizationCode( headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); if (apiContext == null) { apiContext = new APIContext(); - apiContext.setMaskRequestId(true); } + apiContext.setMaskRequestId(true); apiContext.setHeadersMap(headersMap); return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, resourcePath, payLoad, Tokeninfo.class); @@ -243,25 +232,7 @@ public static Tokeninfo createFromAuthorizationCode( public Tokeninfo createFromRefreshToken( CreateFromRefreshTokenParameters createFromRefreshTokenParameters) throws PayPalRESTException { - String pattern = "v1/identity/openidconnect/tokenservice ?grant_type={0}&refresh_token={1}&scope={2}&client_id={3}&client_secret={4}"; - Map paramsMap = new HashMap(); - paramsMap.putAll(createFromRefreshTokenParameters.getContainerMap()); - try { - paramsMap.put("refresh_token", URLEncoder.encode(getRefreshToken(), "UTF-8")); - } catch (UnsupportedEncodingException ex) { - // Ignore - } - Object[] parameters = new Object[] { paramsMap }; - String resourcePath = RESTUtil.formatURIPath(pattern, parameters); - String payLoad = resourcePath.substring(resourcePath.indexOf('?') + 1); - resourcePath = resourcePath.substring(0, resourcePath.indexOf('?')); - Map headersMap = new HashMap(); - headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); - APIContext apiContext = new APIContext(); - apiContext.setMaskRequestId(true); - apiContext.setHeadersMap(headersMap); - return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, - resourcePath, payLoad, Tokeninfo.class); + return createFromRefreshToken(null, createFromRefreshTokenParameters); } /** @@ -281,7 +252,7 @@ public Tokeninfo createFromRefreshToken(APIContext apiContext, Map paramsMap = new HashMap(); paramsMap.putAll(createFromRefreshTokenParameters.getContainerMap()); try { - paramsMap.put("refresh_token", URLEncoder.encode(getRefreshToken(), "UTF-8")); + paramsMap.put("refresh_token", URLEncoder.encode(getRefreshToken(), Constants.ENCODING_FORMAT)); } catch (UnsupportedEncodingException ex) { // Ignore } @@ -293,8 +264,8 @@ public Tokeninfo createFromRefreshToken(APIContext apiContext, headersMap.put(Constants.HTTP_CONTENT_TYPE_HEADER, Constants.HTTP_CONFIG_DEFAULT_CONTENT_TYPE); if (apiContext == null) { apiContext = new APIContext(); - apiContext.setMaskRequestId(true); } + apiContext.setMaskRequestId(true); apiContext.setHeadersMap(headersMap); return PayPalResource.configureAndExecute(apiContext, HttpMethod.POST, resourcePath, payLoad, Tokeninfo.class); diff --git a/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java b/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java index 1277135..369a402 100644 --- a/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java +++ b/src/main/java/com/paypal/sdk/openidconnect/Userinfo.java @@ -440,14 +440,7 @@ public String getPayerId() { */ public static Userinfo getUserinfo(UserinfoParameters userinfoParameters) throws PayPalRESTException { - String pattern = "v1/identity/openidconnect/userinfo?schema={0}&access_token={1}"; - Object[] parameters = new Object[] { userinfoParameters }; - String resourcePath = RESTUtil.formatURIPath(pattern, parameters); - String payLoad = ""; - APIContext apiContext = new APIContext(); - apiContext.setMaskRequestId(true); - return PayPalResource.configureAndExecute(apiContext, HttpMethod.GET, - resourcePath, payLoad, Userinfo.class); + return getUserinfo(null, userinfoParameters); } /**