http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/LogsResource.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/LogsResource.java b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/LogsResource.java new file mode 100755 index 0000000..8c1a252 --- /dev/null +++ b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/LogsResource.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * © Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.microservice.resources; + +import static com.ibm.juno.core.html.HtmlDocSerializerProperties.*; +import static com.ibm.juno.core.html.HtmlSerializerProperties.*; +import static com.ibm.juno.server.RestServletProperties.*; +import static javax.servlet.http.HttpServletResponse.*; + +import java.io.*; +import java.net.*; +import java.nio.charset.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.dto.*; +import com.ibm.juno.core.filters.*; +import com.ibm.juno.core.ini.ConfigFile; +import com.ibm.juno.core.utils.StringUtils; +import com.ibm.juno.microservice.Resource; +import com.ibm.juno.server.*; +import com.ibm.juno.server.annotation.*; +import com.ibm.juno.server.annotation.Properties; +import com.ibm.juno.server.converters.Queryable; + +/** + * REST resource for viewing and accessing log files. + */ +@RestResource( + path="/logs", + label="Log files", + description="Log files from this service", + properties={ + @Property(name=HTML_uriAnchorText, value=PROPERTY_NAME), + @Property(name=REST_allowMethodParam, value="true") + }, + filters={ + IteratorFilter.class, // Allows Iterators and Iterables to be serialized. + DateFilter.ISO8601DT.class // Serialize Date objects as ISO8601 strings. + } +) +@SuppressWarnings("nls") +public class LogsResource extends Resource { + private static final long serialVersionUID = 1L; + + private ConfigFile cf = getConfig(); + + private File logDir = new File(cf.getString("Logging/logDir", ".")); + private LogEntryFormatter leFormatter = new LogEntryFormatter( + cf.getString("Logging/format", "[{date} {level}] {msg}%n"), + cf.getString("Logging/dateFormat", "yyyy.MM.dd hh:mm:ss"), + cf.getBoolean("Logging/useStackTraceHashes") + ); + + private final FileFilter filter = new FileFilter() { + @Override /* FileFilter */ + public boolean accept(File f) { + return f.isDirectory() || f.getName().endsWith(".log"); + } + }; + + /** + * [GET /*] - Get file details or directory listing. + * + * @param req The HTTP request + * @param properties The writable properties for setting the descriptions. + * @param path The log file path. + * @return The log file. + * @throws Exception + */ + @RestMethod(name="GET", path="/*", rc={200,404}) + public Object getFileOrDirectory(RestRequest req, @Properties ObjectMap properties, @PathRemainder String path) throws Exception { + + File f = getFile(path); + + if (f.isDirectory()) { + Set<FileResource> l = new TreeSet<FileResource>(new FileResourceComparator()); + for (File fc : f.listFiles(filter)) { + URL fUrl = new URL(req.getTrimmedRequestURL().append('/').append(fc.getName()).toString()); + l.add(new FileResource(fc, fUrl)); + } + properties.put(HTMLDOC_description, "Contents of " + f.getAbsolutePath()); + return l; + } + + properties.put(HTMLDOC_description, "File details on " + f.getAbsolutePath()); + return new FileResource(f, new URL(req.getTrimmedRequestURL().toString())); + } + + /** + * [VIEW /*] - Retrieve the contents of a log file. + * + * @param req The HTTP request. + * @param res The HTTP response. + * @param path The log file path. + * @param properties The writable properties for setting the descriptions. + * @param highlight If <code>true</code>, add color highlighting based on severity. + * @param start Optional start timestamp. Don't print lines logged before the specified timestamp. Example: "&start=2014-01-23 11:25:47". + * @param end Optional end timestamp. Don't print lines logged after the specified timestamp. Example: "&end=2014-01-23 11:25:47". + * @param thread Optional thread name filter. Only show log entries with the specified thread name. Example: "&thread=pool-33-thread-1". + * @param loggers Optional logger filter. Only show log entries if they were produced by one of the specified loggers (simple class name). Example: "&loggers=(LinkIndexService,LinkIndexRestService)". + * @param severity Optional severity filter. Only show log entries with the specified severity. Example: "&severity=(ERROR,WARN)". + * @throws Exception + */ + @RestMethod(name="VIEW", path="/*", rc={200,404}) + @SuppressWarnings("nls") + public void viewFile(RestRequest req, RestResponse res, @PathRemainder String path, @Properties ObjectMap properties, @Param("highlight") boolean highlight, @Param("start") String start, @Param("end") String end, @Param("thread") String thread, @Param("loggers") String[] loggers, @Param("severity") String[] severity) throws Exception { + + File f = getFile(path); + if (f.isDirectory()) + throw new RestException(SC_METHOD_NOT_ALLOWED, "View not available on directories"); + + Date startDate = StringUtils.parseISO8601Date(start), endDate = StringUtils.parseISO8601Date(end); + + if (! highlight) { + Object o = getReader(f, startDate, endDate, thread, loggers, severity); + res.setContentType("text/plain"); + if (o instanceof Reader) + res.setOutput(o); + else { + LogParser p = (LogParser)o; + Writer w = res.getNegotiatedWriter(); + try { + p.writeTo(w); + } finally { + w.flush(); + w.close(); + } + } + return; + } + + res.setContentType("text/html"); + PrintWriter w = res.getNegotiatedWriter(); + try { + w.println("<html><body style='font-family:monospace;font-size:8pt;white-space:pre;'>"); + LogParser lp = getLogParser(f, startDate, endDate, thread, loggers, severity); + try { + if (! lp.hasNext()) + w.append("<span style='color:gray'>[EMPTY]</span>"); + else for (LogParser.Entry le : lp) { + char s = le.severity.charAt(0); + String color = "black"; + //SEVERE|WARNING|INFO|CONFIG|FINE|FINER|FINEST + if (s == 'I') + color = "#006400"; + else if (s == 'W') + color = "#CC8400"; + else if (s == 'E' || s == 'S') + color = "#DD0000"; + else if (s == 'D' || s == 'F' || s == 'T') + color = "#000064"; + w.append("<span style='color:").append(color).append("'>"); + le.appendHtml(w).append("</span>"); + } + w.append("</body></html>"); + } finally { + lp.close(); + } + } finally { + w.close(); + } + } + + /** + * [VIEW /*] - Retrieve the contents of a log file as parsed entries. + * + * @param req The HTTP request. + * @param path The log file path. + * @param start Optional start timestamp. Don't print lines logged before the specified timestamp. Example: "&start=2014-01-23 11:25:47". + * @param end Optional end timestamp. Don't print lines logged after the specified timestamp. Example: "&end=2014-01-23 11:25:47". + * @param thread Optional thread name filter. Only show log entries with the specified thread name. Example: "&thread=pool-33-thread-1". + * @param loggers Optional logger filter. Only show log entries if they were produced by one of the specified loggers (simple class name). Example: "&loggers=(LinkIndexService,LinkIndexRestService)". + * @param severity Optional severity filter. Only show log entries with the specified severity. Example: "&severity=(ERROR,WARN)". + * @return The parsed contents of the log file. + * @throws Exception + */ + @RestMethod(name="PARSE", path="/*", converters=Queryable.class, rc={200,404}) + public LogParser viewParsedEntries(RestRequest req, @PathRemainder String path, @Param("start") String start, @Param("end") String end, @Param("thread") String thread, @Param("loggers") String[] loggers, @Param("severity") String[] severity) throws Exception { + + File f = getFile(path); + Date startDate = StringUtils.parseISO8601Date(start), endDate = StringUtils.parseISO8601Date(end); + + if (f.isDirectory()) + throw new RestException(SC_METHOD_NOT_ALLOWED, "View not available on directories"); + + return getLogParser(f, startDate, endDate, thread, loggers, severity); + } + + /** + * [DOWNLOAD /*] - Download file. + * + * @param res The HTTP response. + * @param path The log file path. + * @return The contents of the log file. + * @throws Exception + */ + @RestMethod(name="DOWNLOAD", path="/*", rc={200,404}) + public Object downloadFile(RestResponse res, @PathRemainder String path) throws Exception { + + File f = getFile(path); + + if (f.isDirectory()) + throw new RestException(SC_METHOD_NOT_ALLOWED, "Download not available on directories"); + + res.setContentType("application/octet-stream"); //$NON-NLS-1$ + res.setContentLength((int)f.length()); + return new FileInputStream(f); + } + + /** + * [DELETE /*] - Delete a file. + * + * @param path The log file path. + * @return A redirect object to the root. + * @throws Exception + */ + @RestMethod(name="DELETE", path="/*", rc={200,404}) + public Object deleteFile(@PathRemainder String path) throws Exception { + + File f = getFile(path); + + if (f.isDirectory()) + throw new RestException(SC_BAD_REQUEST, "Delete not available on directories."); + + if (f.canWrite()) + if (! f.delete()) + throw new RestException(SC_FORBIDDEN, "Could not delete file."); + + return new Redirect(path + "/.."); //$NON-NLS-1$ + } + + private static BufferedReader getReader(File f) throws IOException { + return new BufferedReader(new InputStreamReader(new FileInputStream(f), Charset.defaultCharset())); + } + + private File getFile(String path) { + if (path != null && path.indexOf("..") != -1) + throw new RestException(SC_NOT_FOUND, "File not found."); + File f = (path == null ? logDir : new File(logDir.getAbsolutePath() + '/' + path)); + if (filter.accept(f)) + return f; + throw new RestException(SC_NOT_FOUND, "File not found."); + } + + /** + * File bean. + */ + @SuppressWarnings("javadoc") + public static class FileResource { + private File f; + public String type; + public Object name; + public Long size; + @BeanProperty(filter=DateFilter.Medium.class) public Date lastModified; + public URL view, highlighted, parsed, download, delete; + + public FileResource(File f, URL url) throws IOException { + this.f = f; + this.type = (f.isDirectory() ? "dir" : "file"); + this.name = f.isDirectory() ? new Link(f.getName(), url.toString()) : f.getName(); + this.size = f.isDirectory() ? null : f.length(); + this.lastModified = new Date(f.lastModified()); + if (f.canRead() && ! f.isDirectory()) { + this.view = new URL(url + "?method=VIEW"); + this.highlighted = new URL(url + "?method=VIEW&highlight=true"); + this.parsed = new URL(url + "?method=PARSE"); + this.download = new URL(url + "?method=DOWNLOAD"); + this.delete = new URL(url + "?method=DELETE"); + } + } + } + + private static class FileResourceComparator implements Comparator<FileResource>, Serializable { + private static final long serialVersionUID = 1L; + @Override /* Comparator */ + public int compare(FileResource o1, FileResource o2) { + int c = o1.type.compareTo(o2.type); + return c != 0 ? c : o1.f.getName().compareTo(o2.f.getName()); + } + } + + private Object getReader(File f, final Date start, final Date end, final String thread, final String[] loggers, final String[] severity) throws IOException { + if (start == null && end == null && thread == null && loggers == null) + return getReader(f); + return getLogParser(f, start, end, thread, loggers, severity); + } + + private LogParser getLogParser(File f, final Date start, final Date end, final String thread, final String[] loggers, final String[] severity) throws IOException { + return new LogParser(leFormatter, f, start, end, thread, loggers, severity); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/SampleRootResource.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/SampleRootResource.java b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/SampleRootResource.java new file mode 100755 index 0000000..8c60073 --- /dev/null +++ b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/SampleRootResource.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * © Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.microservice.resources; + +import com.ibm.juno.microservice.ResourceGroup; +import com.ibm.juno.server.annotation.RestResource; + +/** + * Sample root REST resource. + * + * @author James Bognar ([email protected]) + */ +@RestResource( + path="/", + label="Sample Root Resource", + description="This is a sample router page", + children={ConfigResource.class,LogsResource.class} +) +public class SampleRootResource extends ResourceGroup { + private static final long serialVersionUID = 1L; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/ShutdownResource.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/ShutdownResource.java b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/ShutdownResource.java new file mode 100755 index 0000000..e36d51c --- /dev/null +++ b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/ShutdownResource.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * © Copyright IBM Corporation 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: + * Use, duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.microservice.resources; + +import com.ibm.juno.microservice.Resource; +import com.ibm.juno.server.annotation.RestMethod; +import com.ibm.juno.server.annotation.RestResource; + +/** + * Provides the capability to shut down this REST microservice through a REST call. + */ +@RestResource( + path="/shutdown", + label="Shut down this resource" +) +public class ShutdownResource extends Resource { + + private static final long serialVersionUID = 1L; + + /** + * [GET /] - Shutdown this resource. + * + * @return The string <js>"OK"</js>. + * @throws Exception + */ + @RestMethod(name="GET", path="/", description="Show contents of config file.") + public String shutdown() throws Exception { + new Thread( + new Runnable() { + @Override /* Runnable */ + public void run() { + try { + Thread.sleep(1000); + System.exit(0); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + ).start(); + return "OK"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/package.html b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/package.html new file mode 100755 index 0000000..770c4f5 --- /dev/null +++ b/com.ibm.team.juno.microservice/src/com/ibm/juno/microservice/resources/package.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2015. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../javadoc.css"); + body { + margin: 20px; + } + </style> +</head> +<body> +<p>Predefined Microservice Resources</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/.classpath ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/.classpath b/com.ibm.team.juno.releng/.classpath new file mode 100755 index 0000000..85ebd8a --- /dev/null +++ b/com.ibm.team.juno.releng/.classpath @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry exported="true" kind="lib" path="lib/derby/derby.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jaxrs/jsr311-api-1.1.1.jar"/> + <classpathentry exported="true" kind="lib" path="lib/javax.servlet_2.5.0.jar"/> + <classpathentry exported="true" kind="lib" path="lib/commons-codec-1.9/commons-codec-1.9.jar"/> + <classpathentry exported="true" kind="lib" path="lib/commons-fileupload/org.apache.commons.fileupload_1.3.1.jar"/> + <classpathentry exported="true" kind="lib" path="lib/httpclient/commons-logging-1.1.1.jar"/> + <classpathentry exported="true" kind="lib" path="lib/httpclient/httpclient-4.5.jar"/> + <classpathentry exported="true" kind="lib" path="lib/httpclient/httpcomponents-client-4.5-src.zip"/> + <classpathentry exported="true" kind="lib" path="lib/httpclient/httpcore-4.4.1.jar"/> + <classpathentry exported="true" kind="lib" path="lib/httpclient/httpmime-4.5.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jaxrs/wink-common-1.2.1-incubating.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jaxrs/wink-server-1.2.1-incubating.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jena/jena-core-2.7.1-sources.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jena/jena-core-2.7.1.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jena/jena-iri-0.9.2.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jena/log4j-1.2.16.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jena/slf4j-api-1.6.4.jar"/> + <classpathentry exported="true" kind="lib" path="lib/jena/slf4j-log4j12-1.6.4.jar"/> + <classpathentry exported="true" kind="lib" path="lib/junit/hamcrest-core-1.3.jar"/> + <classpathentry exported="true" kind="lib" path="lib/junit/junit-4.12.jar"/> + <classpathentry kind="output" path="bin"/> +</classpath> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/.jazzignore ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/.jazzignore b/com.ibm.team.juno.releng/.jazzignore new file mode 100755 index 0000000..f8f7ec4 --- /dev/null +++ b/com.ibm.team.juno.releng/.jazzignore @@ -0,0 +1,37 @@ +### Jazz Ignore 0 +# Ignored files and folders will not be committed, but may be modified during +# accept or update. +# - Ignore properties should contain a space separated list of filename patterns. +# - Each pattern is case sensitive and surrounded by braces ('{' and '}'). +# - "*" matches zero or more characters. +# - "?" matches a single character. +# - The pattern list may be split across lines by ending the line with a +# backslash and starting the next line with a tab. +# - Patterns in core.ignore prevent matching resources in the same +# directory from being committed. +# - Patterns in core.ignore.recursive matching resources in the current +# directory and all subdirectories from being committed. +# - The default value of core.ignore.recursive is *.class +# - The default value for core.ignore is bin +# +# To ignore shell scripts and hidden files in this subtree: +# e.g: core.ignore.recursive = {*.sh} {\.*} +# +# To ignore resources named 'bin' in the current directory (but allow +# them in any sub directorybelow): +# e.g: core.ignore = {bin} +# +# NOTE: modifying ignore files will not change the ignore status of +# Eclipse derived resources. + +core.ignore.recursive= \ + {*.class} + +core.ignore= \ + {CT_Results} \ + {CT_Results_html} \ + {bin} \ + {build} \ + {doc} \ + {docstage} \ + {temp.build} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/.project ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/.project b/com.ibm.team.juno.releng/.project new file mode 100755 index 0000000..4b21cbb --- /dev/null +++ b/com.ibm.team.juno.releng/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.ibm.team.juno.releng</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/.settings/org.eclipse.jdt.core.prefs ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/.settings/org.eclipse.jdt.core.prefs b/com.ibm.team.juno.releng/.settings/org.eclipse.jdt.core.prefs new file mode 100755 index 0000000..9370afe --- /dev/null +++ b/com.ibm.team.juno.releng/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,25 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.source=1.6 http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/Juno+SW+Classification+Guidance+&+Questionnaire [v1].doc ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/Juno+SW+Classification+Guidance+&+Questionnaire [v1].doc b/com.ibm.team.juno.releng/Juno+SW+Classification+Guidance+&+Questionnaire [v1].doc new file mode 100755 index 0000000..dea703b Binary files /dev/null and b/com.ibm.team.juno.releng/Juno+SW+Classification+Guidance+&+Questionnaire [v1].doc differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/META-INF/MANIFEST.MF ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/META-INF/MANIFEST.MF b/com.ibm.team.juno.releng/META-INF/MANIFEST.MF new file mode 100755 index 0000000..6e3c020 --- /dev/null +++ b/com.ibm.team.juno.releng/META-INF/MANIFEST.MF @@ -0,0 +1,40 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Juno Releng +Bundle-SymbolicName: com.ibm.team.juno.releng +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: IBM +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ClassPath: + lib/derby/derby.jar, + lib/jaxrs/jsr311-api-1.1.1.jar, + lib/httpclient/httpclient-4.5.jar, + lib/httpclient/httpmime-4.5.jar, + lib/httpclient/httpcore-4.4.1.jar +Export-Package: + org.apache.derby.jdbc, + javax.ws.rs, + javax.ws.rs.core, + javax.ws.rs.ext, + org.apache.http, + org.apache.http.auth, + org.apache.http.client, + org.apache.http.client.config, + org.apache.http.client.entity, + org.apache.http.client.methods, + org.apache.http.client.params, + org.apache.http.client.utils, + org.apache.http.config, + org.apache.http.conn, + org.apache.http.conn.scheme, + org.apache.http.conn.ssl, + org.apache.http.conn.socket, + org.apache.http.entity, + org.apache.http.entity.mime, + org.apache.http.impl.client, + org.apache.http.impl.conn, + org.apache.http.impl.cookie, + org.apache.http.message, + org.apache.http.params, + org.apache.http.protocol, + org.apache.http.util http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/Readme.txt ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/Readme.txt b/com.ibm.team.juno.releng/Readme.txt new file mode 100755 index 0000000..0c2facd --- /dev/null +++ b/com.ibm.team.juno.releng/Readme.txt @@ -0,0 +1,33 @@ +#================================================================================ +# Juno Components List +#================================================================================ + +--------------------------------------------------------------------------------- +juno-all.jar +Contains all binaries from the Core, Server, Client, and Microservice APIs +--------------------------------------------------------------------------------- + +--------------------------------------------------------------------------------- +bundles/* +Contents of juno-all.jar as individual OSGi bundles. +--------------------------------------------------------------------------------- + +--------------------------------------------------------------------------------- +source/* +Same as the binaries, except includes all the source code as well. +--------------------------------------------------------------------------------- + +--------------------------------------------------------------------------------- +juno-javadocs.war +The docs for everything. +--------------------------------------------------------------------------------- + +--------------------------------------------------------------------------------- +microservice-project.zip +The Eclipse project template for creating a microservice. +--------------------------------------------------------------------------------- + +--------------------------------------------------------------------------------- +microservice-samples-project.zip +The Eclipse project for running the samples. +--------------------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/all/META-INF/MANIFEST.MF ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/all/META-INF/MANIFEST.MF b/com.ibm.team.juno.releng/bin/all/META-INF/MANIFEST.MF new file mode 100755 index 0000000..f8481cf --- /dev/null +++ b/com.ibm.team.juno.releng/bin/all/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.8.3 +Created-By: pwa6470_27sr1-20140411_01 (SR1) (IBM Corporation) +Built-By: jbognar +Build-Date: December 30 2015 +Bundle-Name: Juno Cloud API +Bundle-Vendor: IBM +Bundle-SymbolicName: com.ibm.team.juno.all +Bundle-Version: 5.2.0.0 + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/META-INF/MANIFEST.MF ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/META-INF/MANIFEST.MF b/com.ibm.team.juno.releng/bin/client/META-INF/MANIFEST.MF new file mode 100755 index 0000000..6c9827b --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/META-INF/MANIFEST.MF @@ -0,0 +1,24 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.8.3 +Created-By: pwa6470_27sr1-20140411_01 (SR1) (IBM Corporation) +Bundle-ManifestVersion: 2 +Bundle-Name: Juno Cloud API - Client +Bundle-SymbolicName: com.ibm.team.juno.client +Bundle-Version: 5.2.0.0 +Bundle-Vendor: IBM +Require-Bundle: com.ibm.team.juno +Import-Package: org.apache.http,org.apache.http.auth,org.apache.http.c + lient,org.apache.http.client.config,org.apache.http.client.entity,org + .apache.http.client.methods,org.apache.http.client.params,org.apache. + http.client.utils,org.apache.http.config,org.apache.http.conn,org.apa + che.http.conn.routing,org.apache.http.conn.scheme,org.apache.http.con + n.socket,org.apache.http.conn.ssl,org.apache.http.conn.util,org.apach + e.http.cookie,org.apache.http.entity,org.apache.http.impl.client,org. + apache.http.impl.conn,org.apache.http.impl.cookie,org.apache.http.mes + sage,org.apache.http.params,org.apache.http.protocol,org.apache.http. + util +Export-Package: com.ibm.juno.client,com.ibm.juno.client.jazz +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Built-By: jbognar +Build-Date: December 30 2015 + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.class new file mode 100755 index 0000000..42defb1 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.java new file mode 100755 index 0000000..7f68ef0 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/AllowAllRedirects.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.client; + +import org.apache.http.impl.client.*; + +/** + * Redirect strategy that allows for redirects on any request type, not just <code>GET</code> or <code>HEAD</code>. + * <p> + * Note: This class is similar to <code>org.apache.http.impl.client.LaxRedirectStrategy</code> + * in Apache HttpClient 4.2, but also allows for redirects on <code>PUTs</code> and <code>DELETEs</code>. + * + * @author James Bognar ([email protected]) + */ +public class AllowAllRedirects extends DefaultRedirectStrategy { + + @Override /* DefaultRedirectStrategy */ + protected boolean isRedirectable(final String method) { + return true; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.class new file mode 100755 index 0000000..f83558b Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.java new file mode 100755 index 0000000..47247d5 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/DateHeader.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.client; + +import java.util.*; + +import org.apache.http.client.utils.*; +import org.apache.http.message.*; + +/** + * Convenience class for setting date headers in RFC2616 format. + * <p> + * Equivalent to the following code: + * <p class='bcode'> + * Header h = <jk>new</jk> Header(name, DateUtils.<jsm>formatDate</jsm>(value)); + * </p> + * + * @author James Bognar ([email protected]) + */ +public final class DateHeader extends BasicHeader { + + private static final long serialVersionUID = 1L; + + /** + * Creates a date request property in RFC2616 format. + * + * @param name The header name. + * @param value The header value. + */ + public DateHeader(String name, Date value) { + super(name, DateUtils.formatDate(value)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.class new file mode 100755 index 0000000..45e2b0c Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.java new file mode 100755 index 0000000..773ada1 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/HttpMethod.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.client; + +/** + * Enumeration of HTTP methods. + * <p> + * + * @author James Bognar ([email protected]) + */ +public enum HttpMethod { + + /** HTTP GET */ + GET(false), + + /** HTTP PUT */ + PUT(true), + + /** HTTP POST */ + POST(true), + + /** HTTP DELETE */ + DELETE(false), + + /** HTTP OPTIONS */ + OPTIONS(false), + + /** HTTP HEAD */ + HEAD(false), + + /** HTTP TRACE */ + TRACE(false), + + /** HTTP CONNECT */ + CONNECT(false), + + /** HTTP MOVE */ + MOVE(false); + + private boolean hasContent; + + HttpMethod(boolean hasContent) { + this.hasContent = hasContent; + } + + /** + * Returns whether this HTTP method normally has content. + * + * @return <jk>true</jk> if this HTTP method normally has content. + */ + public boolean hasContent() { + return hasContent; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.class new file mode 100755 index 0000000..3373a82 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.java new file mode 100755 index 0000000..9e6e65b --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/NameValuePairs.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.client; + +import java.util.*; + +import org.apache.http.*; +import org.apache.http.client.entity.*; + +/** + * Convenience class for constructing instances of <code>List<NameValuePair></code> + * for the {@link UrlEncodedFormEntity} class. + * + * <h6 class='topic'>Example</h6> + * <p class='bcode'> + * NameValuePairs params = <jk>new</jk> NameValuePairs() + * .append(<jk>new</jk> BasicNameValuePair(<js>"j_username"</js>, user)) + * .append(<jk>new</jk> BasicNameValuePair(<js>"j_password"</js>, pw)); + * request.setEntity(<jk>new</jk> UrlEncodedFormEntity(params)); + * </p> + * + * @author James Bognar ([email protected]) + */ +public final class NameValuePairs extends LinkedList<NameValuePair> { + + private static final long serialVersionUID = 1L; + + /** + * Appends the specified pair to the end of this list. + * + * @param pair The pair to append to this list. + * @return This object (for method chaining). + */ + public NameValuePairs append(NameValuePair pair) { + super.add(pair); + return this; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.class new file mode 100755 index 0000000..6d36243 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.java new file mode 100755 index 0000000..a10e939 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/ResponsePattern.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.client; + +import java.io.*; +import java.util.regex.*; + +/** + * Used to find regular expression matches in REST responses made through {@link RestCall}. + * <p> + * Response patterns are applied to REST calls through the {@link RestCall#addResponsePattern(ResponsePattern)} method. + * <p> + * <h6 class='topic'>Example</h6> + * This example shows how to use a response pattern finder to find and capture patterns for <js>"x=number"</js> and <js>"y=string"</js> + * from a response body. + * <p> + * <p class='bcode'> + * <jk>final</jk> List<Number> xList = <jk>new</jk> ArrayList<Number>(); + * <jk>final</jk> List<String> yList = <jk>new</jk> ArrayList<String>(); + * + * restClient.doGet(<jsf>URL</jsf>) + * .addResponsePattern( + * <jk>new</jk> ResponsePattern(<js>"x=(\\d+)"</js>) { + * <ja>@Override</ja> + * <jk>public void</jk> onMatch(RestCall restCall, Matcher m) <jk>throws</jk> RestCallException { + * xList.add(Integer.<jsm>parseInt</jsm>(m.group(1))); + * } + * <ja>@Override</ja> + * <jk>public void</jk> onNoMatch(RestCall restCall) <jk>throws</jk> RestCallException { + * <jk>throw new</jk> RestCallException(<js>"No X's found!"</js>); + * } + * } + * ) + * .addResponsePattern( + * <jk>new</jk> ResponsePattern(<js>"y=(\\S+)"</js>) { + * <ja>@Override</ja> + * <jk>public void</jk> onMatch(RestCall restCall, Matcher m) <jk>throws</jk> RestCallException { + * yList.add(m.group(1)); + * } + * <ja>@Override</ja> + * <jk>public void</jk> onNoMatch(RestCall restCall) <jk>throws</jk> RestCallException { + * <jk>throw new</jk> RestCallException(<js>"No Y's found!"</js>); + * } + * } + * ) + * .run(); + * </p> + * <p> + * <h5 class='notes'>Important Notes:</h5> + * <ol class='notes'> + * <li><p> + * Using response patterns does not affect the functionality of any of the other methods + * used to retrieve the response such as {@link RestCall#getResponseAsString()} or {@link RestCall#getResponse(Class)}.<br> + * HOWEVER, if you want to retrieve the entire text of the response from inside the match methods, + * use {@link RestCall#getCapturedResponse()} since this method will not absorb the response for those other methods. + * </p> + * <li><p> + * Response pattern methods are NOT executed if a REST exception occurs during the request. + * </p> + * <li><p> + * The {@link RestCall#successPattern(String)} and {@link RestCall#failurePattern(String)} methods use instances of + * this class to throw {@link RestCallException RestCallExceptions} when success patterns are not found or failure patterns + * are found. + * </p> + * <li><p> + * {@link ResponsePattern} objects are reusable and thread-safe. + * </p> + * </ol> + * + * @author James Bognar ([email protected]) + */ +public abstract class ResponsePattern { + + private Pattern pattern; + + /** + * Constructor. + * + * @param pattern Regular expression pattern. + */ + public ResponsePattern(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + void match(RestCall rc) throws RestCallException { + try { + Matcher m = pattern.matcher(rc.getCapturedResponse()); + boolean found = false; + while (m.find()) { + onMatch(rc, m); + found = true; + } + if (! found) + onNoMatch(rc); + } catch (IOException e) { + throw new RestCallException(e); + } + } + + /** + * Returns the pattern passed in through the constructor. + * + * @return The pattern passed in through the constructor. + */ + protected String getPattern() { + return pattern.pattern(); + } + + /** + * Instances can override this method to handle when a regular expression pattern matches + * on the output. + * <p> + * This method is called once for every pattern match that occurs in the response text. + * + * @param rc The {@link RestCall} that this pattern finder is being used on. + * @param m The regular expression {@link Matcher}. Can be used to retrieve group matches in the pattern. + * @throws RestCallException Instances can throw an exception if a failure condition is detected. + */ + public void onMatch(RestCall rc, Matcher m) throws RestCallException {} + + /** + * Instances can override this method to handle when a regular expression pattern doesn't match on the output. + * + * @param rc The {@link RestCall} that this pattern finder is being used on. + * @throws RestCallException Instances can throw an exception if a failure condition is detected. + */ + public void onNoMatch(RestCall rc) throws RestCallException {} +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$1.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$1.class new file mode 100755 index 0000000..3a5e0e9 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$1.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$2.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$2.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$2.class new file mode 100755 index 0000000..711dec8 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$2.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$3.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$3.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$3.class new file mode 100755 index 0000000..60f20dd Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall$3.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.class new file mode 100755 index 0000000..cbdd200 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.java new file mode 100755 index 0000000..67cbff2 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCall.java @@ -0,0 +1,942 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.client; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.logging.*; +import java.util.regex.*; + +import org.apache.http.*; +import org.apache.http.client.*; +import org.apache.http.client.config.*; +import org.apache.http.client.methods.*; +import org.apache.http.impl.client.*; +import org.apache.http.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.encoders.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.parser.ParseException; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; + +/** + * Represents a connection to a remote REST resource. + * <p> + * Instances of this class are created by the various {@code doX()} methods on the {@link RestClient} class. + * <p> + * This class uses only Java standard APIs. Requests can be built up using a fluent interface with method chaining, like so... + * + * <p class='bcode'> + * RestClient client = <jk>new</jk> RestClient(); + * RestCall c = client.doPost(<jsf>URL</jsf>).setInput(o).setHeader(x,y); + * MyBean b = c.getResponse(MyBean.<jk>class</jk>); + * </p> + * <p> + * The actual connection and request/response transaction occurs when calling one of the <code>getResponseXXX()</code> methods. + * + * <h6 class='topic'>Additional Information</h6> + * <ul> + * <li><a class='doclink' href='package-summary.html#RestClient'>com.ibm.juno.client > REST client API</a> for more information and code examples. + * </ul> + * + * @author James Bognar ([email protected]) + */ +public final class RestCall { + + private final RestClient client; // The client that created this call. + private final HttpRequestBase request; // The request. + private HttpResponse response; // The response. + private List<RestCallInterceptor> interceptors = new ArrayList<RestCallInterceptor>(); // Used for intercepting and altering requests. + + private boolean isConnected = false; // connect() has been called. + private boolean allowRedirectsOnPosts; + private int retries = 1; + private int redirectOnPostsTries = 5; + private long retryInterval = -1; + private RetryOn retryOn = RetryOn.DEFAULT; + private boolean ignoreErrors; + private boolean byLines = false; + private TeeWriter writers = new TeeWriter(); + private StringWriter capturedResponseWriter; + private String capturedResponse; + private TeeOutputStream outputStreams = new TeeOutputStream(); + private boolean isClosed = false; + private boolean isFailed = false; + + /** + * Constructs a REST call with the specified method name. + * + * @param client The client that created this request. + * @param request The wrapped Apache HTTP client request object. + * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. + */ + protected RestCall(RestClient client, HttpRequestBase request) throws RestCallException { + this.client = client; + this.request = request; + for (RestCallInterceptor i : this.client.interceptors) + addInterceptor(i); + } + + /** + * Sets the input for this REST call. + * + * @param input The input to be sent to the REST resource (only valid for PUT and POST) requests. <br> + * Can be of the following types: + * <ul> + * <li>{@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. + * <li>{@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. + * <li>{@link Object} - POJO to be converted to text using the {@link Serializer} registered with the {@link RestClient}. + * <li>{@link HttpEntity} - Bypass Juno serialization and pass HttpEntity directly to HttpClient. + * </ul> + * @return This object (for method chaining). + * @throws RestCallException If a retry was attempted, but the entity was not repeatable. + */ + public RestCall setInput(final Object input) throws RestCallException { + if (! (request instanceof HttpEntityEnclosingRequestBase)) + throw new RestCallException(0, "Method does not support content entity.", request.getMethod(), request.getURI(), null); + HttpEntity entity = (input instanceof HttpEntity ? (HttpEntity)input : new RestRequestEntity(input, client.serializer)); + ((HttpEntityEnclosingRequestBase)request).setEntity(entity); + if (retries > 1 && ! entity.isRepeatable()) + throw new RestCallException("Rest call set to retryable, but entity is not repeatable."); + return this; + } + + /** + * Convenience method for setting a header value on the request. + * <p> + * Equivalent to calling <code>restCall.getRequest().setHeader(name, value.toString())</code>. + * + * @param name The header name. + * @param value The header value. + * @return This object (for method chaining). + */ + public RestCall setHeader(String name, Object value) { + request.setHeader(name, value.toString()); + return this; + } + + /** + * Make this call retryable if an error response (>=400) is received. + * + * @param retries The number of retries to attempt. + * @param interval The time in milliseconds between attempts. + * @param retryOn Optional object used for determining whether a retry should be attempted. + * If <jk>null</jk>, uses {@link RetryOn#DEFAULT}. + * @return This object (for method chaining). + * @throws RestCallException If current entity is not repeatable. + */ + public RestCall setRetryable(int retries, long interval, RetryOn retryOn) throws RestCallException { + if (request instanceof HttpEntityEnclosingRequestBase) { + HttpEntity e = ((HttpEntityEnclosingRequestBase)request).getEntity(); + if (e != null && ! e.isRepeatable()) + throw new RestCallException("Attempt to make call retryable, but entity is not repeatable."); + } + this.retries = retries; + this.retryInterval = interval; + this.retryOn = (retryOn == null ? RetryOn.DEFAULT : retryOn); + return this; + + } + + /** + * For this call, allow automatic redirects when a 302 or 307 occurs when + * performing a POST. + * <p> + * Note that this can be inefficient since the POST body needs to be serialized + * twice. + * The preferred approach if possible is to use the {@link LaxRedirectStrategy} strategy + * on the underlying HTTP client. However, this method is provided if you don't + * have access to the underlying client. + * + * @param b Redirect flag. + * @return This object (for method chaining). + */ + public RestCall allowRedirectsOnPosts(boolean b) { + this.allowRedirectsOnPosts = b; + return this; + } + + /** + * Specify the number of redirects to follow before throwing an exception. + * + * @param maxAttempts Allow a redirect to occur this number of times. + * @return This object (for method chaining). + */ + public RestCall setRedirectMaxAttempts(int maxAttempts) { + this.redirectOnPostsTries = maxAttempts; + return this; + } + + /** + * Add an interceptor for this call only. + * + * @param interceptor The interceptor to add to this call. + * @return This object (for method chaining). + */ + public RestCall addInterceptor(RestCallInterceptor interceptor) { + interceptors.add(interceptor); + interceptor.onInit(this); + return this; + } + + /** + * Pipes the request output to the specified writer when {@link #run()} is called. + * <p> + * The writer is not closed. + * <p> + * This method can be called multiple times to pipe to multiple writers. + * + * @param w The writer to pipe the output to. + * @return This object (for method chaining). + */ + public RestCall pipeTo(Writer w) { + return pipeTo(w, false); + } + + /** + * Pipe output from response to the specified writer when {@link #run()} is called. + * <p> + * This method can be called multiple times to pipe to multiple writers. + * + * @param w The writer to write the output to. + * @param close Close the writer when {@link #close()} is called. + * @return This object (for method chaining). + */ + public RestCall pipeTo(Writer w, boolean close) { + return pipeTo(null, w, close); + } + + /** + * Pipe output from response to the specified writer when {@link #run()} is called and associate + * that writer with an ID so it can be retrieved through {@link #getWriter(String)}. + * <p> + * This method can be called multiple times to pipe to multiple writers. + * + * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)} + * @param w The writer to write the output to. + * @param close Close the writer when {@link #close()} is called. + * @return This object (for method chaining). + */ + public RestCall pipeTo(String id, Writer w, boolean close) { + writers.add(id, w, close); + return this; + } + + /** + * Retrieves a writer associated with an ID via {@link #pipeTo(String, Writer, boolean)} + * + * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)} + * @return The writer, or <jk>null</jk> if no writer is associated with that ID. + */ + public Writer getWriter(String id) { + return writers.getWriter(id); + } + + /** + * When output is piped to writers, flush the writers after every line of output. + * + * @return This object (for method chaining). + */ + public RestCall byLines() { + this.byLines = true; + return this; + } + + /** + * Pipes the request output to the specified output stream when {@link #run()} is called. + * <p> + * The output stream is not closed. + * <p> + * This method can be called multiple times to pipe to multiple output streams. + * + * @param os The output stream to pipe the output to. + * @return This object (for method chaining). + */ + public RestCall pipeTo(OutputStream os) { + return pipeTo(os, false); + } + + /** + * Pipe output from response to the specified output stream when {@link #run()} is called. + * <p> + * This method can be called multiple times to pipe to multiple output stream. + * + * @param os The output stream to write the output to. + * @param close Close the output stream when {@link #close()} is called. + * @return This object (for method chaining). + */ + public RestCall pipeTo(OutputStream os, boolean close) { + return pipeTo(null, os, close); + } + + /** + * Pipe output from response to the specified output stream when {@link #run()} is called and associate + * that output stream with an ID so it can be retrieved through {@link #getOutputStream(String)}. + * <p> + * This method can be called multiple times to pipe to multiple output stream. + * + * @param id A string identifier that can be used to retrieve the output stream using {@link #getOutputStream(String)} + * @param os The output stream to write the output to. + * @param close Close the output stream when {@link #close()} is called. + * @return This object (for method chaining). + */ + public RestCall pipeTo(String id, OutputStream os, boolean close) { + outputStreams.add(id, os, close); + return this; + } + + /** + * Retrieves an output stream associated with an ID via {@link #pipeTo(String, OutputStream, boolean)} + * + * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)} + * @return The writer, or <jk>null</jk> if no writer is associated with that ID. + */ + public OutputStream getOutputStream(String id) { + return outputStreams.getOutputStream(id); + } + + /** + * Prevent {@link RestCallException RestCallExceptions} from being thrown when HTTP status 400+ is encountered. + * @return This object (for method chaining). + */ + public RestCall ignoreErrors() { + this.ignoreErrors = true; + return this; + } + + /** + * Stores the response text so that it can later be captured using {@link #getCapturedResponse()}. + * <p> + * This method should only be called once. Multiple calls to this method are ignored. + * + * @return This object (for method chaining). + */ + public RestCall captureResponse() { + if (capturedResponseWriter == null) { + capturedResponseWriter = new StringWriter(); + writers.add(capturedResponseWriter, false); + } + return this; + } + + + /** + * Look for the specified regular expression pattern in the response output. + * <p> + * Causes a {@link RestCallException} to be thrown if the specified pattern is found in the output. + * <p> + * This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output + * methods such as {@link #getResponseAsString()}. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Throw a RestCallException if FAILURE or ERROR is found in the output.</jc> + * restClient.doGet(<jsf>URL</jsf>) + * .failurePattern(<js>"FAILURE|ERROR"</js>) + * .run(); + * </p> + * </dd> + * </dl> + * + * @param errorPattern A regular expression to look for in the response output. + * @return This object (for method chaining). + */ + public RestCall failurePattern(final String errorPattern) { + addResponsePattern( + new ResponsePattern(errorPattern) { + @Override + public void onMatch(RestCall rc, Matcher m) throws RestCallException { + throw new RestCallException("Failure pattern detected."); + } + } + ); + return this; + } + + /** + * Look for the specified regular expression pattern in the response output. + * <p> + * Causes a {@link RestCallException} to be thrown if the specified pattern is not found in the output. + * <p> + * This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output + * methods such as {@link #getResponseAsString()}. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Throw a RestCallException if SUCCESS is not found in the output.</jc> + * restClient.doGet(<jsf>URL</jsf>) + * .successPattern(<js>"SUCCESS"</js>) + * .run(); + * </p> + * </dd> + * </dl> + * + * @param successPattern A regular expression to look for in the response output. + * @return This object (for method chaining). + */ + public RestCall successPattern(String successPattern) { + addResponsePattern( + new ResponsePattern(successPattern) { + @Override + public void onNoMatch(RestCall rc) throws RestCallException { + throw new RestCallException("Success pattern not detected."); + } + } + ); + return this; + } + + /** + * Adds a response pattern finder to look for regular expression matches in the response output. + * <p> + * This method can be called multiple times to add multiple response pattern finders. + * <p> + * {@link ResponsePattern ResponsePatterns} use the {@link #getCapturedResponse()} to read the response text and so does not affect the other output + * methods such as {@link #getResponseAsString()}. + * + * @param responsePattern The response pattern finder. + * @return This object (for method chaining). + */ + public RestCall addResponsePattern(final ResponsePattern responsePattern) { + captureResponse(); + addInterceptor( + new RestCallInterceptor() { + @Override + public void onClose(RestCall restCall) throws RestCallException { + responsePattern.match(RestCall.this); + } + } + ); + return this; + } + + /** + * Set configuration settings on this request. + * <p> + * Use {@link RequestConfig#custom()} to create configuration parameters for the request. + * + * @param config The new configuration settings for this request. + * @return This object (for method chaining). + */ + public RestCall setConfig(RequestConfig config) { + this.request.setConfig(config); + return this; + } + + /** + * @return The HTTP response code. + * @throws RestCallException + * @deprecated Use {@link #run()}. + */ + @Deprecated + public int execute() throws RestCallException { + return run(); + } + + /** + * Method used to execute an HTTP response where you're only interested in the HTTP response code. + * <p> + * The response entity is discarded unless one of the pipe methods have been specified to pipe the + * output to an output stream or writer. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jk>try</jk> { + * RestClient client = <jk>new</jk> RestClient(); + * <jk>int</jk> rc = client.doGet(url).execute(); + * <jc>// Succeeded!</jc> + * } <jk>catch</jk> (RestCallException e) { + * <jc>// Failed!</jc> + * } + * </p> + * </dd> + * </dl> + * + * @return This object (for method chaining). + * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. + */ + public int run() throws RestCallException { + connect(); + try { + StatusLine status = response.getStatusLine(); + int sc = status.getStatusCode(); + if (sc >= 400 && ! ignoreErrors) + throw new RestCallException(sc, status.getReasonPhrase(), request.getMethod(), request.getURI(), getResponseAsString()).setHttpResponse(response); + if (outputStreams.size() > 0 || writers.size() > 0) + getReader(); + return sc; + } catch (RestCallException e) { + isFailed = true; + throw e; + } catch (IOException e) { + isFailed = true; + throw new RestCallException(e).setHttpResponse(response); + } finally { + close(); + } + } + + /** + * Connects to the REST resource. + * <p> + * If this is a <code>PUT</code> or <code>POST</code>, also sends the input to the remote resource.<br> + * <p> + * Typically, you would only call this method if you're not interested in retrieving the body of the HTTP response. + * Otherwise, you're better off just calling one of the {@link #getReader()}/{@link #getResponse(Class)}/{@link #pipeTo(Writer)} + * methods directly which automatically call this method already. + * + * @return This object (for method chaining). + * @throws RestCallException If an exception or <code>400+</code> HTTP status code occurred during the connection attempt. + */ + public RestCall connect() throws RestCallException { + + if (isConnected) + return this; + isConnected = true; + + try { + int sc = 0; + while (retries > 0) { + retries--; + Exception ex = null; + try { + response = client.execute(request); + sc = response == null ? -1 : response.getStatusLine().getStatusCode(); + } catch (Exception e) { + ex = e; + sc = -1; + } + if (! retryOn.onCode(sc)) + retries = 0; + if (retries > 0) { + for (RestCallInterceptor rci : interceptors) + rci.onRetry(this, sc, request, response, ex); + request.reset(); + long w = retryInterval; + synchronized(this) { + wait(w); + } + } else if (ex != null) { + throw ex; + } + } + for (RestCallInterceptor rci : interceptors) + rci.onConnect(this, sc, request, response); + if (response == null) + throw new RestCallException("HttpClient returned a null response"); + StatusLine sl = response.getStatusLine(); + String method = request.getMethod(); + sc = sl.getStatusCode(); // Read it again in case it was changed by one of the interceptors. + if (sc >= 400 && ! ignoreErrors) + throw new RestCallException(sc, sl.getReasonPhrase(), method, request.getURI(), getResponseAsString()).setHttpResponse(response); + if ((sc == 307 || sc == 302) && allowRedirectsOnPosts && method.equalsIgnoreCase("POST")) { + if (redirectOnPostsTries-- < 1) + throw new RestCallException(sc, "Maximum number of redirects occurred. Location header: " + response.getFirstHeader("Location"), method, request.getURI(), getResponseAsString()); + Header h = response.getFirstHeader("Location"); + if (h != null) { + reset(); + request.setURI(URI.create(h.getValue())); + retries++; // Redirects should affect retries. + connect(); + } + } + + } catch (RestCallException e) { + isFailed = true; + try { + close(); + } catch (RestCallException e2) { /* Ignore */ } + throw e; + } catch (Exception e) { + isFailed = true; + close(); + throw new RestCallException(e).setHttpResponse(response); + } + + return this; + } + + private void reset() { + if (response != null) + EntityUtils.consumeQuietly(response.getEntity()); + request.reset(); + isConnected = false; + isClosed = false; + isFailed = false; + if (capturedResponseWriter != null) + capturedResponseWriter.getBuffer().setLength(0); + } + + /** + * Connects to the remote resource (if <code>connect()</code> hasn't already been called) and returns the HTTP response message body as a reader. + * <p> + * If an {@link Encoder} has been registered with the {@link RestClient}, then the underlying input stream + * will be wrapped in the encoded stream (e.g. a <code>GZIPInputStream</code>). + * <p> + * If present, automatically handles the <code>charset</code> value in the <code>Content-Type</code> response header. + * <p> + * <b>IMPORTANT:</b> It is your responsibility to close this reader once you have finished with it. + * + * @return The HTTP response message body reader. <jk>null</jk> if response was successful but didn't contain a body (e.g. HTTP 204). + * @throws IOException If an exception occurred while streaming was already occurring. + */ + public Reader getReader() throws IOException { + InputStream is = getInputStream(); + if (is == null) + return null; + + // Figure out what the charset of the response is. + String cs = null; + Header contentType = response.getLastHeader("Content-Type"); + String ct = contentType == null ? null : contentType.getValue(); + + // First look for "charset=" in Content-Type header of response. + if (ct != null && ct.contains("charset=")) + cs = ct.substring(ct.indexOf("charset=")+8).trim(); + + if (cs == null) + cs = "UTF-8"; + + Reader isr = new InputStreamReader(is, cs); + + if (writers.size() > 0) { + StringWriter sw = new StringWriter(); + writers.add(sw, true); + IOPipe.create(isr, writers).byLines(byLines).run(); + return new StringReader(sw.toString()); + } + + return new InputStreamReader(is, cs); + } + + /** + * Returns the response text as a string if {@link #captureResponse()} was called on this object. + * <p> + * Note that while similar to {@link #getResponseAsString()}, this method can be called multiple times + * to retrieve the response text multiple times. + * <p> + * Note that this method returns <jk>null</jk> if you have not called one of the methods that cause + * the response to be processed. (e.g. {@link #run()}, {@link #getResponse()}, {@link #getResponseAsString()}. + * + * @return The captured response, or <jk>null</jk> if {@link #captureResponse()} has not been called. + * @throws IllegalStateException If trying to call this method before the response is consumed. + */ + public String getCapturedResponse() { + if (! isClosed) + throw new IllegalStateException("This method cannot be called until the response has been consumed."); + if (capturedResponse == null && capturedResponseWriter != null && capturedResponseWriter.getBuffer().length() > 0) + capturedResponse = capturedResponseWriter.toString(); + return capturedResponse; + } + + /** + * Returns the parser specified on the client to use for parsing HTTP response bodies. + * + * @return The parser. + * @throws RestCallException If no parser was defined on the client. + */ + protected Parser<?> getParser() throws RestCallException { + if (client.parser == null) + throw new RestCallException(0, "No parser defined on client", request.getMethod(), request.getURI(), null); + return client.parser; + } + + /** + * Returns the serializer specified on the client to use for serializing HTTP request bodies. + * + * @return The serializer. + * @throws RestCallException If no serializer was defined on the client. + */ + protected Serializer<?> getSerializer() throws RestCallException { + if (client.serializer == null) + throw new RestCallException(0, "No serializer defined on client", request.getMethod(), request.getURI(), null); + return client.serializer; + } + + /** + * Returns the value of the <code>Content-Length</code> header. + * + * @return The value of the <code>Content-Length</code> header, or <code>-1</code> if header is not present. + * @throws IOException + */ + public int getContentLength() throws IOException { + connect(); + Header h = response.getLastHeader("Content-Length"); + if (h == null) + return -1; + long l = Long.parseLong(h.getValue()); + if (l > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + return (int)l; + } + + /** + * Connects to the remote resource (if <code>connect()</code> hasn't already been called) and returns the HTTP response message body as an input stream. + * <p> + * If an {@link Encoder} has been registered with the {@link RestClient}, then the underlying input stream + * will be wrapped in the encoded stream (e.g. a <code>GZIPInputStream</code>). + * <p> + * <b>IMPORTANT:</b> It is your responsibility to close this reader once you have finished with it. + * + * @return The HTTP response message body input stream. <jk>null</jk> if response was successful but didn't contain a body (e.g. HTTP 204). + * @throws IOException If an exception occurred while streaming was already occurring. + * @throws IllegalStateException If an attempt is made to read the response more than once. + */ + public InputStream getInputStream() throws IOException { + if (isClosed) + throw new IllegalStateException("Method cannot be called. Response has already been consumed."); + connect(); + if (response == null) + throw new RestCallException("Response was null"); + if (response.getEntity() == null) // HTTP 204 results in no content. + return null; + InputStream is = response.getEntity().getContent(); + + if (outputStreams.size() > 0) { + ByteArrayInOutStream baios = new ByteArrayInOutStream(); + outputStreams.add(baios, true); + IOPipe.create(is, baios).run(); + return baios.getInputStream(); + } + return is; + } + + /** + * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response message body as plain text. + * + * @return The response as a string. + * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. + * @throws IOException If an exception occurred while streaming was already occurring. + */ + public String getResponseAsString() throws IOException { + try { + Reader r = getReader(); + String s = IOUtils.read(r).toString(); + return s; + } catch (IOException e) { + isFailed = true; + throw e; + } finally { + close(); + } + } + + /** + * Converts the output from the connection into an object of the specified class using the registered {@link Parser}. + * + * @param type The class to convert the input to. + * @param <T> The class to convert the input to. + * @return The parsed output. + * @throws IOException If a connection error occurred. + * @throws ParseException If the input contains a syntax error or is malformed for the <code>Content-Type</code> header. + */ + public <T> T getResponse(Class<T> type) throws IOException, ParseException { + BeanContext bc = getParser().getBeanContext(); + if (bc == null) + bc = BeanContext.DEFAULT; + return getResponse(bc.getClassMeta(type)); + } + + /** + * Parses the output from the connection into the specified type and then wraps that in a {@link PojoRest}. + * <p> + * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. + * + * @param innerType The class type of the POJO being wrapped. + * @return The parsed output wapped in a {@link PojoRest}. + * @throws IOException If a connection error occurred. + * @throws ParseException If the input contains a syntax error or is malformed for the <code>Content-Type</code> header. + */ + public PojoRest getResponsePojoRest(Class<?> innerType) throws IOException, ParseException { + return new PojoRest(getResponse(innerType)); + } + + /** + * Converts the output from the connection into an {@link ObjectMap} and then wraps that in a {@link PojoRest}. + * <p> + * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. + * + * @return The parsed output wapped in a {@link PojoRest}. + * @throws IOException If a connection error occurred. + * @throws ParseException If the input contains a syntax error or is malformed for the <code>Content-Type</code> header. + */ + public PojoRest getResponsePojoRest() throws IOException, ParseException { + return getResponsePojoRest(ObjectMap.class); + } + + /** + * Convenience method when you want to parse into a Map<K,V> object. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * Map<String,MyBean> m = client.doGet(url).getResponseMap(LinkedHashMap.<jk>class</jk>, String.<jk>class</jk>, MyBean.<jk>class</jk>); + * </p> + * <p> + * A simpler approach is often to just extend the map class you want and just use the normal {@link #getResponse(Class)} method: + * </p> + * <p class='bcode'> + * <jk>public static class</jk> MyMap <jk>extends</jk> LinkedHashMap<String,MyBean> {} + * + * Map<String,MyBean> m = client.doGet(url).getResponse(MyMap.<jk>class</jk>); + * </p> + * </dd> + * </dl> + * + * @param mapClass The map class to use (e.g. <code>TreeMap</code>) + * @param keyClass The class type of the keys (e.g. <code>String</code>) + * @param valueClass The class type of the values (e.g. <code>MyBean</code>) + * @return The response parsed as a map. + * @throws ParseException + * @throws IOException + */ + public final <K,V,T extends Map<K,V>> T getResponseMap(Class<T> mapClass, Class<K> keyClass, Class<V> valueClass) throws ParseException, IOException { + ClassMeta<T> cm = getBeanContext().getMapClassMeta(mapClass, keyClass, valueClass); + return getResponse(cm); + } + + /** + * Convenience method when you want to parse into a Collection<E> object. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * List<MyBean> l = client.doGet(url).getResponseCollection(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); + * </p> + * <p> + * A simpler approach is often to just extend the collection class you want and just use the normal {@link #getResponse(Class)} method: + * </p> + * <p class='bcode'> + * <jk>public static class</jk> MyList <jk>extends</jk> LinkedList<MyBean> {} + * + * List<MyBean> l = client.doGet(url).getResponse(MyList.<jk>class</jk>); + * </p> + * </dd> + * </dl> + * + * @param collectionClass The collection class to use (e.g. <code>LinkedList</code>) + * @param entryClass The class type of the values (e.g. <code>MyBean</code>) + * @return The response parsed as a collection. + * @throws ParseException + * @throws IOException + */ + public final <E,T extends Collection<E>> T getResponseCollection(Class<T> collectionClass, Class<E> entryClass) throws ParseException, IOException { + ClassMeta<T> cm = getBeanContext().getCollectionClassMeta(collectionClass, entryClass); + return getResponse(cm); + } + + <T> T getResponse(ClassMeta<T> type) throws IOException, ParseException { + try { + Parser<?> p = getParser(); + T o = null; + int contentLength = getContentLength(); + if (! p.isReaderParser()) { + InputStream is = getInputStream(); + o = ((InputStreamParser)p).parse(is, contentLength, type); + } else { + Reader r = getReader(); + o = ((ReaderParser)p).parse(r, contentLength, type); + } + return o; + } catch (ParseException e) { + isFailed = true; + throw e; + } catch (IOException e) { + isFailed = true; + throw e; + } finally { + close(); + } + } + + BeanContext getBeanContext() throws RestCallException { + BeanContext bc = getParser().getBeanContext(); + if (bc == null) + bc = BeanContext.DEFAULT; + return bc; + } + + /** + * Returns access to the {@link HttpUriRequest} passed to {@link HttpClient#execute(HttpUriRequest)}. + * + * @return The {@link HttpUriRequest} object. + */ + public HttpUriRequest getRequest() { + return request; + } + + /** + * Returns access to the {@link HttpResponse} returned by {@link HttpClient#execute(HttpUriRequest)}. + * Returns <jk>null</jk> if {@link #connect()} has not yet been called. + * + * @return The HTTP response object. + * @throws IOException + */ + public HttpResponse getResponse() throws IOException { + connect(); + return response; + } + + /** + * Shortcut for calling <code>getRequest().setHeader(header)</code> + * + * @param header The header to set on the request. + * @return This object (for method chaining). + */ + public RestCall setHeader(Header header) { + request.setHeader(header); + return this; + } + + /** Use close() */ + @Deprecated + public void consumeResponse() { + if (response != null) + EntityUtils.consumeQuietly(response.getEntity()); + } + + /** + * Cleans up this HTTP call. + * + * @return This object (for method chaining). + * @throws RestCallException Can be thrown by one of the {@link RestCallInterceptor#onClose(RestCall)} calls. + */ + public RestCall close() throws RestCallException { + if (response != null) + EntityUtils.consumeQuietly(response.getEntity()); + isClosed = true; + if (! isFailed) + for (RestCallInterceptor r : interceptors) + r.onClose(this); + return this; + } + + /** + * Adds a {@link RestCallLogger} to the list of interceptors on this class. + * + * @param level The log level to log events at. + * @param log The logger. + * @return This object (for method chaining). + */ + public RestCall logTo(Level level, Logger log) { + addInterceptor(new RestCallLogger(level, log)); + return this; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCallException.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCallException.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCallException.class new file mode 100755 index 0000000..57f3148 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/RestCallException.class differ
