Who am I?
• Julian Robichaux
– Programmer and product manager at panagenda
– Developing applications using IBM products since mid-90’s
– Several open-source projects on OpenNTF
– “Learn Java by Example” video course
on lynda.com (LinkedIn Learning)
– IBM Champion since 2011
– nsftools.com (old stuff)
– @jrobichaux
2
Why are we here?
• To talk about Java 8!
– specifically lambdas and streams
– syntax, gotchas, and “why change?”
• Assuming that everyone in the room is a Java programmer
– you don’t have to be an expert
– any version is fine, we will start with Java 6
– we will also touch on Java 7, just in case
• What to do if you’re still on a Java 6 platform
3
Example Code
• Connect to a web server via HTTPS, and report the key size and expiration
date of the SSL certificate
– check servers for vulnerabilities due to old/weak keys
– tell your admins it’s time to renew the certificate
4
URL url = new URL( "https://my.server" );
checkSSLCertificate(url);
public static void checkSSLCertificate(URL url) {
HttpsURLConnection con = null;
try {
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory( fakeSocketFactory() );
con.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true; // trust everyone!
}
});
con.connect();
Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
System.out.println( "Valid until: " + ((X509Certificate)cert).getNotAfter());
System.out.println( "Server key bit length: " +
((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() );
}
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} finally {
con.disconnect();
}
}
Example Code (Java 6)
create an HTTPS connection
tell it that self-signed and
expired certificates are okay
report the expiration date and
RSA key length of each SSL
certificate on the server
Exception handling!
Example Code (Java 6)
/**
* Creates an SSLSocketFactory that doesn't check certificates at all
* DO NOT USE THIS IN REAL LIFE
*/
private static SSLSocketFactory fakeSocketFactory() throws GeneralSecurityException {
TrustManager trustEveryone = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustEveryone }, new SecureRandom());
return sslContext.getSocketFactory();
}
A Few Words About Java 7
because you’ll see this stuff in Java 8
Catch Multiple Exceptions
• Try-catch blocks can catch multiple exceptions in one statement
HttpsURLConnection con = null;
try {
con = (HttpsURLConnection) url.openConnection();
// do stuff..
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} finally {
con.disconnect();
}
HttpsURLConnection con = null;
try {
con = (HttpsURLConnection) url.openConnection();
// do stuff..
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
} finally {
con.disconnect();
}
Try-with-resources
• Classes that implement the AutoCloseable interface can be automatically
closed by a try-catch block
HttpsURLConnection con = null;
try {
con = (HttpsURLConnection) url.openConnection();
// do stuff..
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
} finally {
con.disconnect();
}
try (CloseableURLConnection closeableCon = new CloseableURLConnection(url)) {
HttpsURLConnection con = closeableCon.getHttpsConnection();
// do stuff..
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
*
*
*
Creating a Closeable Class
static class CloseableURLConnection implements AutoCloseable {
URLConnection con;
public CloseableURLConnection(URL url) throws IOException {
con = url.openConnection();
}
public HttpsURLConnection getHttpsConnection() {
return (HttpsURLConnection)con;
}
@Override
public void close() {
getHttpsConnection().disconnect();
}
}
implement AutoCloseable
create a close() method
Closeable Classes
• Several standard Java classes are now AutoClosable
– InputStream
– OutputStream
– Reader
– Writer
– java.sql.Connection, Statement, and ResultSet
• You no longer have to declare your streams outside the try/catch block, or
remember to close them when you’re done!
• Like a finally clause, try-with-resources objects get closed even if an
Exception is thrown
11
A Few Other Java 7 Goodies
• New and/or improved classes
– java.nio.file, updated XML libraries, new/enhanced cryptography providers
• Binary literals and underscores in numeric literals
– int byteNum = 0b00100001;
– int phoneNum = 555_1212;
• Type inference for generics
– Map<String, List<String>> map = new HashMap<>();
• Switch statements operate on Strings
• http://docs.oracle.com/javase/8/docs/technotes/guides/language/enhancements.html
http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html
The Code in Java 7…
public static void checkSSLCertificate(URL url) {
try (CloseableURLConnection closeableCon = new CloseableURLConnection(url)) {
HttpsURLConnection con = closeableCon.getHttpsConnection();
con.setSSLSocketFactory( fakeSocketFactory() );
con.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true; // trust everyone!
}
});
con.connect();
Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
System.out.println( "Valid until: " + ((X509Certificate)cert).getNotAfter());
System.out.println( "Server key bit length: " +
((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() );
}
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
}
anonymous class
loop through an array
The Code in Java 8
public static void checkSSLCertificate(URL url) {
try (CloseableURLConnection closeableCon = new CloseableURLConnection(url)) {
HttpsURLConnection con = closeableCon.getHttpsConnection();
con.setSSLSocketFactory( fakeSocketFactory() );
con.setHostnameVerifier( (hostname, session) -> true );
con.connect();
Certificate[] certs = con.getServerCertificates();
Arrays.asList(certs)
.stream()
.map ( cert -> (X509Certificate)cert )
.peek( cert -> System.out.println("Valid until: " + cert.getNotAfter()) )
.map ( cert -> ((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() )
.forEach( i -> System.out.println("Server key bit length: " + i) );
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
}
lambda hostname verifier
read the array as a stream,
not a loop
What’s a Lambda?
• Simplified syntax for writing single-method classes
• You are effectively passing a function as a parameter to a method
16
con.setHostnameVerifier( (hostname, session) -> true );
(parameters)
arrow
expression
How Does that Lambda Work?
con.setHostnameVerifier( new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
con.setHostnameVerifier( (hostname, session) -> true );
?
How Does that Lambda Work?
con.setHostnameVerifier( new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
con.setHostnameVerifier( (hostname, session) -> true );
?
How Does that Lambda Work?
con.setHostnameVerifier( new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
con.setHostnameVerifier( (hostname, session) -> true );
?
How Does that Lambda Work?
con.setHostnameVerifier( new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
con.setHostnameVerifier( (hostname, session) -> true );
?
How Does that Lambda Work?
con.setHostnameVerifier( new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
con.setHostnameVerifier( (hostname, session) -> true );
!
Functional Interfaces
• Lambdas rely on “functional interfaces” to work
– an interface with only one abstract method
– also called a “single abstract method” or SAM interface
– there can be only one! <insert highlander joke here>
• If there’s only one method, the compiler knows which one you will call
• If you know the method, and the compiler knows the method, we don’t
have to talk about the method
– everyone already knows!
– skip the obvious stuff
22
Functional Interfaces
con.setHostnameVerifier( new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
con.setHostnameVerifier( (hostname, session) -> true );
the class MUST be a HostnameVerifier, and the
method MUST be verify(String, SSLSession)
therefore… throw away everything obvious
Method References
• There is an alternative syntax called “Method References”
– for a lambda that only calls a single method of the parameter, or a single
static method that uses the parameter, or creates a single new Object
object.method( s -> s.getLength() );
object.method( String::getLength );
http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Another Example: Sandwiches!
static enum Sandwich {
PB_J ("Peanut butter & jelly", 500),
SUB ("Sub", 500),
HOTDOG ("Hot dog", 900),
TACO ("Taco", 500),
PIZZA ("Pizza", 900);
private String name;
private int calories;
private Sandwich(String name, int calories) {
this.name = name;
this.calories = calories;
}
public String getName() { return name; }
public int getCalories() { return calories; }
public String toString() { return "n" + calories +
" caloriest" + name; }
}
List the Sandwiches in Order of Calories
List<Sandwich> sandwiches = Arrays.asList( Sandwich.values() );
Collections.sort(sandwiches, new Comparator<Sandwich>() {
public int compare(Sandwich s1, Sandwich s2) {
return Integer.compare(s1.getCalories(), s2.getCalories());
}
});
System.out.println(sandwiches);
PB_J ("Peanut butter & jelly", 500),
SUB ("Sub", 500),
HOTDOG ("Hot dog", 900),
TACO ("Taco", 500),
PIZZA ("Pizza", 900);
500 calories Peanut butter & jelly,
500 calories Sub,
500 calories Taco,
900 calories Hot dog,
900 calories Pizza
List the Sandwiches in Order of Calories
List<Sandwich> sandwiches = Arrays.asList( Sandwich.values() );
/*
Collections.sort(sandwiches, new Comparator<Sandwich>() {
public int compare(Sandwich s1, Sandwich s2) {
return Integer.compare(s1.getCalories(), s2.getCalories());
}
});
*/
Collections.sort(sandwiches,
(s1, s2) -> Integer.compare(s1.getCalories(), s2.getCalories()) );
System.out.println(sandwiches);
PB_J ("Peanut butter & jelly", 500),
SUB ("Sub", 500),
HOTDOG ("Hot dog", 900),
TACO ("Taco", 500),
PIZZA ("Pizza", 900);
500 calories Peanut butter & jelly,
500 calories Sub,
500 calories Taco,
900 calories Hot dog,
900 calories Pizza
List the Sandwiches in Order of Calories
List<Sandwich> sandwiches = Arrays.asList( Sandwich.values() );
/*
Collections.sort(sandwiches, new Comparator<Sandwich>() {
public int compare(Sandwich s1, Sandwich s2) {
return Integer.compare(s1.getCalories(), s2.getCalories());
}
});
*/
Collections.sort(sandwiches,
Comparator.comparing( sandwich -> sandwich.getCalories() ) );
System.out.println(sandwiches);
PB_J ("Peanut butter & jelly", 500),
SUB ("Sub", 500),
HOTDOG ("Hot dog", 900),
TACO ("Taco", 500),
PIZZA ("Pizza", 900);
500 calories Peanut butter & jelly,
500 calories Sub,
500 calories Taco,
900 calories Hot dog,
900 calories Pizza
List the Sandwiches in Order of Calories
List<Sandwich> sandwiches = Arrays.asList( Sandwich.values() );
/*
Collections.sort(sandwiches, new Comparator<Sandwich>() {
public int compare(Sandwich s1, Sandwich s2) {
return Integer.compare(s1.getCalories(), s2.getCalories());
}
});
*/
Collections.sort(sandwiches,
Comparator.comparing( Sandwich::getCalories ) );
System.out.println(sandwiches);
PB_J ("Peanut butter & jelly", 500),
SUB ("Sub", 500),
HOTDOG ("Hot dog", 900),
TACO ("Taco", 500),
PIZZA ("Pizza", 900);
500 calories Peanut butter & jelly,
500 calories Sub,
500 calories Taco,
900 calories Hot dog,
900 calories Pizza
All These are the Same Comparator
Collections.sort(sandwiches, new Comparator<Sandwich>() {
public int compare(Sandwich s1, Sandwich s2) {
return Integer.compare(s1.getCalories(), s2.getCalories());
}
});
Collections.sort(sandwiches,
(s1, s2) -> Integer.compare(s1.getCalories(), s2.getCalories()) );
Collections.sort(sandwiches,
Comparator.comparing( sandwich -> sandwich.getCalories() ) );
Collections.sort(sandwiches,
Comparator.comparing( Sandwich::getCalories ) );
But Really, What’s the Point?
• This all seems like needless tomfoolery
– after all, the old code still works!
• Two major advantages:
1. Forces (or strongly encourages) you to write functional code
2. Less visual noise makes code easier to read and understand
33
Collections.sort(sandwiches, new Comparator<Sandwich>() {
public int compare(Sandwich s1, Sandwich s2) {
return Integer.compare(s1.getCalories(), s2.getCalories());
}
});
Collections.sort(sandwiches,
Comparator.comparing( Sandwich::getCalories ) );
Dealing with Exceptions
Collections.sort(sandwiches,
Comparator.comparing( Sandwich::getCalories )
.thenComparing( Sandwich::getName )
.reversed() );
NULLWICH (null, 500),
PB_J ("Peanut butter & jelly", 500),
SUB ("Sub", 500),
HOTDOG ("Hot dog", 900),
TACO ("Taco", 500),
PIZZA ("Pizza", 900);
Exception in thread "main" java.lang.NullPointerException
at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:217)
at java.util.Collections$ReverseComparator2.compare(Collections.java:5178)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
at java.util.TimSort.sort(TimSort.java:220)
at java.util.Arrays.sort(Arrays.java:1438)
at java.util.Arrays$ArrayList.sort(Arrays.java:3895)
at java.util.Collections.sort(Collections.java:175)
at com.panagenda.ssltest.MoreJava8Examples.main(MoreJava8Examples.java:52)
Dealing with Exceptions
• It’s not always obvious what caused the Exception
• It’s cumbersome to add a try/catch to a lambda
– same with adding logging
• If an Exception is possible, maybe better to call out to a method instead
36
Collections.sort(sandwiches,
Comparator.comparing( sandwich -> getCalories(sandwich) )
.thenComparing( sandwich -> getName(sandwich) )
.reversed() );
A Few More Examples
// process all the keys and values of a HashMap
Map<String, Integer> myHashMap = new HashMap<>();
myHashMap.forEach( (key, value) -> System.out.println(key + "; " + value) );
// remove all the null elements in a List
List<String> myArrayList = new ArrayList<>();
myArrayList.removeIf( s -> s == null );
// get all the JPG files in a directory
File photoDir = new File("c:/photos");
File[] docFiles = photoDir.listFiles( file -> file.getName().endsWith("jpg") );
// run some code in a thread
new Thread( () -> doSomething() ).start();
Under the Hood
• If you’re really curious about how all this works…
– http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
• It’s all Function and Predicate objects
– https://docs.oracle.com/javase/8/docs/api/java/util/function/package-
summary.html
– Closures? By some definitions.
38
Function<Sandwich, Integer> caloryCompare = sandwich -> sandwich.getCalories();
What Does that Loop Actually Do?
41
Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
System.out.println( "Valid until: " + ((X509Certificate)cert).getNotAfter());
System.out.println( "Server key bit length: " +
((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() );
}
print the information
get something from the objectprint the information
get something from the object
How Does the Stream Work?
Certificate[] certs = con.getServerCertificates();
Arrays.asList(certs)
.stream()
.map ( cert -> (X509Certificate)cert )
.peek( cert -> System.out.println("Valid until: " + cert.getNotAfter()) )
.map ( cert -> ((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() )
.forEach( i -> System.out.println("Server key bit length: " + i) );
start with an array,
convert it to a List
convert the List to a stream
TIP: this could also be just
Arrays.stream(certs)
}
change the stream
do something to each item
change the stream
do something to each item
Breaking Down the Example
Certificate[] certs = con.getServerCertificates();
Arrays.asList(certs)
.stream()
.map ( cert -> (X509Certificate)cert )
.peek( cert -> System.out.println("Valid until: " + cert.getNotAfter()) )
.map ( cert -> ((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() )
.forEach( i -> System.out.println("Server key bit length: " + i) );
map() converts each item by
casting it to X509Certificate,
then returns a Stream of
X509Certificate objects
map() gets an int value from
each item, then returns a
Stream of int values
start with an array of
Certificates
Breaking Down the Example
Certificate[] certs = con.getServerCertificates();
Arrays.asList(certs)
.stream()
.map ( cert -> (X509Certificate)cert )
.peek( cert -> System.out.println("Valid until: " + cert.getNotAfter()) )
.map ( cert -> ((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() )
.forEach( i -> System.out.println("Server key bit length: " + i) );
peek() is an intermediate
operation: it does something
with each item and returns
the original Stream
forEach() is a terminal
operation: it does something
with each item and stops
What’s a Stream?
• Allows you to perform operations on a collection of data
– multiple operations can be combined in a pipeline
• Has the following properties
– uses functional arguments (lambdas!) to process data
– does not store data
– pipeline operations are optimized for laziness
– optionally process in parallel (multi-threaded) with no extra code
45
Is a Stream Better than a Loop?
Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
System.out.println( "Valid until: " + ((X509Certificate)cert).getNotAfter());
System.out.println( "Server key bit length: " +
((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() );
}
Certificate[] certs = con.getServerCertificates();
Arrays.asList(certs)
.stream()
.map ( cert -> (X509Certificate)cert )
.peek( cert -> System.out.println("Valid until: " + cert.getNotAfter()) )
.map ( cert -> ((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() )
.forEach( i -> System.out.println("Server key bit length: " + i) );
!Stream version is more
descriptive of the behavior
of the code
Stream version can easily be
turned into multi-threaded
code
Multi-threaded You Say?
Arrays.asList(certs)
.stream()
.parallel()
.map ( cert -> (X509Certificate)cert )
.peek( cert -> System.out.println("Valid until: " + cert.getNotAfter()) )
.map ( cert -> ((RSAPublicKey)cert.getPublicKey()).getModulus().bitLength() )
.forEach( i -> System.out.println("Server key bit length: " + i) );
DANGER: The forEach() and peek()
output might be out-of-order!
Adding Numbers in a Loop
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
45
int sum = 0;
for (int i : new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}) {
sum += i;
}
return sum;
Adding Numbers with Reduce
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
1 + 2 3 + 4 + 5 6 + 8 7 + 9
3 + 14 12 + 16
45
By abstracting away the very concept
of looping, you can implement looping
any way you want, including
implementing it in a way that scales
nicely with extra hardware.
Joel Spolsky
https://www.joelonsoftware.com/2006/08/01/
can-your-programming-language-do-this
each computation could be on a separate processor core… or a separate machine!
Other Things to Know About Streams
• Normally get them from Collections or arrays
– List.stream(), Map.entrySet().stream(), Arrays.stream(Object[])
– also other fun places, like BufferedReader.lines() or Files.list()
• Some operations will short circuit instead of processing each item
– built-in optimizations
• Stream operations should be stateless
– for example, don’t update Lists as part of a stream operation
– items could process out of order, and might not be thread-safe
– collect the results at the end instead
51
Other Things to Know About Streams
• Operations on Streams should be associative when possible
– (a + b + c) == (c + b + a) == b + (a + c)
– if order of operations does matter, make sure you:
• start with an ordered set of data, like a List
• use forEachOrdered() instead of forEach()
• don’t use reduce()
• For reference:
– https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-
summary.html
52
Under the Hood
• If you’re really curious about how all this works…
– http://www.ibm.com/developerworks/java/library/j-java-streams-1-brian-
goetz
• Read about java.util.function
– https://docs.oracle.com/javase/8/docs/api/java/util/function/package-
summary.html
– Predicate, Consumer, and Supplier interfaces
53
Java 8 API Changes (partial list)
• Streams
– in its own package
– throughout Collections, Files, etc.
• Functional classes in java.util.function
• Brand new Date-Time package and classes
– http://docs.oracle.com/javase/tutorial/datetime
• JavaFX for GUIs
• http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
http://docs.oracle.com/javase/8/docs/technotes/guides/language/lambda_api_jdk8.html
55
Function<Sandwich, Integer> caloryCompare =
sandwich -> sandwich.getCalories();
Compiling to Earlier Versions
• Java can compile for previous versions, but…
– you can’t use any of the new APIs (classes or methods)
– you can’t use any of the new language features
• What happens if you use a Java 8 JAR in a Java 6 environment?
– it won’t work
– the JRE checks the JAR file for a version number first
– if the version number is too high, you get errors
• Some tools can convert bytecode to earlier versions
– but new APIs still won’t work
56
Options for Backwards Compatibility
• If you don’t want to use new language features at all
– set “Source compatibility” and “Generated .class files compatibility” to your
desired older Java version
– use a tool to make sure you didn’t use new APIs
Animal Sniffer: http://www.mojohaus.org/animal-sniffer
57
Options for Backwards Compatibility
• If you only want to use lambdas and try-with-resources
– compile as Java 8, then use RetroLambda to backport your JAR file
https://github.com/orfjackal/retrolambda
– you still have to make sure you didn’t use new APIs: AnimalSniffer again
• You might be able to backport Streams too (mixed results)
– http://sourceforge.net/projects/streamsupport
58
Is It Worth It?
• Backporting? Maybe, but probably not regularly
– Risky: even if it seems to work, hard to test if it really worked
– make sure you have great unit tests to run against the backported code
• Learning? Absolutely!
– even if you can’t use Java 8 now, you will later
– functional programming can be much “safer”
– teaches you good coding habits
– helps you understand code examples on StackOverflow ;)
59
Articles and Musings
• State of the lambda (Brian Goetz)
– http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
• IBM DeveloperWorks articles by Brian Goetz
– http://www.ibm.com/developerworks/java/library/j-java-streams-1-brian-goetz
– http://www.ibm.com/developerworks/java/library/j-java-streams-2-brian-goetz
– http://www.ibm.com/developerworks/java/library/j-java-streams-3-brian-goetz
– http://www.ibm.com/developerworks/java/library/j-java-streams-4-brian-goetz
– http://www.ibm.com/developerworks/java/library/j-java-streams-5-brian-goetz
• Joel Spolsky on MapReduce
– https://www.joelonsoftware.com/2006/08/01/can-your-programming-language-
do-this
62
Books (!)
• Java 8 in Action
– Raoul-Gabriel Urma
Manning Press
• Mastering Lambdas:
Java Programming in
a Multicore World
– Maurice Naftalin
Oracle Press
Notices and
disclaimers
continued
Information concerning non-IBM products was obtained from the suppliers of those products, their published announcements or other
publicly available sources. IBM has not tested those products in connection with this publication and cannot confirm the accuracy of
performance, compatibility or any other claims related to non-IBM products. Questions on the capabilities of non-IBM products should
be addressed to the suppliers of those products. IBM does not warrant the quality of any third-party products, or the ability of any such
third-party products to interoperate with IBM’s products. IBM EXPRESSLY DISCLAIMS ALL WARRANTIES, EXPRESSED OR
IMPLIED, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE.
The provision of the information contained herein is not intended to, and does not, grant any right or license under any IBM patents,
copyrights, trademarks or other intellectual property right.
IBM, the IBM logo, ibm.com, Aspera®, Bluemix, Blueworks Live, CICS, Clearcase, Cognos®, DOORS®, Emptoris®, Enterprise
Document Management System™, FASP®, FileNet®, Global Business Services ®, Global Technology Services ®, IBM
ExperienceOne™, IBM SmartCloud®, IBM Social Business®, Information on Demand, ILOG, Maximo®, MQIntegrator®, MQSeries®,
Netcool®, OMEGAMON, OpenPower, PureAnalytics™, PureApplication®, pureCluster™, PureCoverage®, PureData®,
PureExperience®, PureFlex®, pureQuery®, pureScale®, PureSystems®, QRadar®, Rational®, Rhapsody®, Smarter Commerce®,
SoDA, SPSS, Sterling Commerce®, StoredIQ, Tealeaf®, Tivoli®, Trusteer®, Unica®, urban{code}®, Watson, WebSphere®, Worklight®,
X-Force® and System z® Z/OS, are trademarks of International Business Machines Corporation, registered in many jurisdictions
worldwide. Other product and service names might be trademarks of IBM or other companies. A current list of IBM trademarks is
available on the Web at "Copyright and trademark information" at: www.ibm.com/legal/copytrade.shtml.
65