Skip to content

Commit 514d140

Browse files
author
Karl Rieb
committed
Add longpoll example and made network read timeouts configurable for StandardHttpRequestor.
1 parent 2d8ddc1 commit 514d140

File tree

9 files changed

+495
-38
lines changed

9 files changed

+495
-38
lines changed

ChangeLog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
- Make read timeouts more easily configurable for StandardHttpRequestor.
2+
- Add longpoll example.
13
- Fix android example bugs and linter warnings
24
- Fix compilation error
35
- Fix upload failure bug

examples/longpoll/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

examples/longpoll/pom.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4+
http://maven.apache.org/maven-v4_0_0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<name>List Folder Longpoll Example (Dropbox Core API SDK)</name>
8+
<artifactId>examples-longpoll</artifactId>
9+
<packaging>jar</packaging>
10+
11+
<parent>
12+
<groupId>com.dropbox.core</groupId>
13+
<artifactId>examples</artifactId>
14+
<version>0-SNAPSHOT</version>
15+
<relativePath>../pom.xml</relativePath>
16+
</parent>
17+
18+
<build>
19+
<plugins>
20+
<plugin>
21+
<groupId>org.apache.maven.plugins</groupId>
22+
<artifactId>maven-dependency-plugin</artifactId>
23+
<executions>
24+
<execution>
25+
<phase>compile</phase>
26+
<goals>
27+
<goal>build-classpath</goal>
28+
</goals>
29+
</execution>
30+
</executions>
31+
</plugin>
32+
</plugins>
33+
</build>
34+
</project>
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package com.dropbox.core.examples.longpoll;
2+
3+
import com.dropbox.core.DbxApiException;
4+
import com.dropbox.core.DbxAuthInfo;
5+
import com.dropbox.core.DbxException;
6+
import com.dropbox.core.DbxRequestConfig;
7+
import com.dropbox.core.DbxWebAuth;
8+
import com.dropbox.core.json.JsonReader;
9+
import com.dropbox.core.http.StandardHttpRequestor;
10+
import com.dropbox.core.v2.DbxClientV2;
11+
import com.dropbox.core.v2.DbxFiles;
12+
13+
import java.io.IOException;
14+
import java.util.Locale;
15+
import java.util.concurrent.TimeUnit;
16+
import java.util.logging.Level;
17+
import java.util.logging.Logger;
18+
19+
/**
20+
* An example command-line application that demonstrates how to use
21+
* longpolling to watch for file changes in a Dropbox account.
22+
*/
23+
public class Main {
24+
/**
25+
* Will perform longpoll request for changes in the user's Dropbox
26+
* account and display those changes. This is more efficient that
27+
* periodic polling the endpoint.
28+
*/
29+
public static void longpoll(DbxAuthInfo auth, String path) throws IOException {
30+
long longpollTimeoutSecs = TimeUnit.MINUTES.toSeconds(2);
31+
32+
// need 2 Dropbox clients for making calls:
33+
//
34+
// (1) One for longpoll requests, with its read timeout set longer than our polling timeout
35+
// (2) One for all other requests, with its read timeout set to the default, shorter timeout
36+
//
37+
StandardHttpRequestor.Config config = StandardHttpRequestor.Config.DEFAULT_INSTANCE;
38+
StandardHttpRequestor.Config longpollConfig = config.copy()
39+
.withReadTimeout(longpollTimeoutSecs + 1, TimeUnit.SECONDS)
40+
.build();
41+
42+
DbxClientV2 dbxClient = createClient(auth, config);
43+
DbxClientV2 dbxLongpollClient = createClient(auth, longpollConfig);
44+
45+
try {
46+
// We only care about file changes, not existing files, so grab latest cursor for this
47+
// path and then longpoll for changes.
48+
String cursor = getLatestCursor(dbxClient, path);
49+
50+
System.out.println("Longpolling for changes... press CTRL-C to exit.");
51+
while (true) {
52+
// will block for longpollTimeoutSecs or until a change is made in the folder
53+
DbxFiles.ListFolderLongpollResult result = dbxLongpollClient.files.listFolderLongpoll(cursor, longpollTimeoutSecs);
54+
55+
// we have changes, list them
56+
if (result.changes) {
57+
cursor = printChanges(dbxClient, cursor);
58+
}
59+
60+
// we were asked to back off from our polling, wait the requested amount of seconds
61+
// before issuing another longpoll request.
62+
if (result.backoff != null) {
63+
System.out.printf("\n\nBacking off for %d seconds\n\n", result.backoff);
64+
try {
65+
Thread.sleep(TimeUnit.SECONDS.toMillis(result.backoff));
66+
} catch (InterruptedException ex) {
67+
System.exit(0);
68+
}
69+
}
70+
}
71+
} catch (DbxApiException ex) {
72+
// if a user message is available, try using that instead
73+
String message = ex.userMessage != null ? ex.userMessage.toString() : ex.getMessage();
74+
System.err.println("Error making API call: " + message);
75+
System.exit(1);
76+
return;
77+
} catch (DbxException ex) {
78+
System.err.println("Error making API call: " + ex.getMessage());
79+
System.exit(1);
80+
return;
81+
}
82+
}
83+
84+
/**
85+
* Create a new Dropbox client using the given authentication
86+
* information and HTTP client config.
87+
*
88+
* @param auth Authentication information
89+
* @param config HTTP request configuration
90+
*
91+
* @return new Dropbox V2 client
92+
*/
93+
private static DbxClientV2 createClient(DbxAuthInfo auth, StandardHttpRequestor.Config config) {
94+
String clientUserAgentId = "examples-longpoll";
95+
String userLocale = Locale.getDefault().toString();
96+
StandardHttpRequestor requestor = new StandardHttpRequestor(config);
97+
DbxRequestConfig requestConfig = new DbxRequestConfig(clientUserAgentId, userLocale, requestor);
98+
99+
return new DbxClientV2(requestConfig, auth.accessToken, auth.host);
100+
}
101+
102+
/**
103+
* Returns latest cursor for listing changes to a directory in
104+
* Dropbox with the given path.
105+
*
106+
* @param dbxClient Dropbox client to use for fetching the latest cursor
107+
* @param path path to directory in Dropbox
108+
*
109+
* @return cursor for listing changes to the given Dropbox directory
110+
*/
111+
private static String getLatestCursor(DbxClientV2 dbxClient, String path)
112+
throws DbxApiException, DbxException {
113+
DbxFiles.ListFolderGetLatestCursorResult result = dbxClient.files.listFolderGetLatestCursorBuilder(path)
114+
.includeDeleted(true)
115+
.includeMediaInfo(false)
116+
.recursive(true)
117+
.start();
118+
return result.cursor;
119+
}
120+
121+
/**
122+
* Prints changes made to a folder in Dropbox since the given
123+
* cursor was retrieved.
124+
*
125+
* @param dbxClient Dropbox client to use for fetching folder changes
126+
* @param cursor lastest cursor received since last set of changes
127+
*
128+
* @return latest cursor after changes
129+
*/
130+
private static String printChanges(DbxClientV2 client, String cursor)
131+
throws DbxApiException, DbxException {
132+
133+
while (true) {
134+
DbxFiles.ListFolderResult result = client.files.listFolderContinue(cursor);
135+
for (DbxFiles.Metadata metadata : result.entries) {
136+
String type;
137+
String details;
138+
if (metadata instanceof DbxFiles.FileMetadata) {
139+
DbxFiles.FileMetadata fileMetadata = (DbxFiles.FileMetadata) metadata;
140+
type = "file";
141+
details = "(rev=" + fileMetadata.rev + ")";
142+
} else if (metadata instanceof DbxFiles.FolderMetadata) {
143+
DbxFiles.FolderMetadata folderMetadata = (DbxFiles.FolderMetadata) metadata;
144+
type = "folder";
145+
details = folderMetadata.sharingInfo != null ? "(shared)" : "";
146+
} else if (metadata instanceof DbxFiles.DeletedMetadata) {
147+
type = "deleted";
148+
details = "";
149+
} else {
150+
throw new IllegalStateException("Unrecognized metadata type: " + metadata.getClass());
151+
}
152+
153+
System.out.printf("\t%10s %24s \"%s\"\n", type, details, metadata.pathLower);
154+
}
155+
// update cursor to fetch remaining results
156+
cursor = result.cursor;
157+
158+
if (!result.hasMore) {
159+
break;
160+
}
161+
}
162+
163+
return cursor;
164+
}
165+
166+
public static void main(String[] args) throws IOException {
167+
// Only display important log messages.
168+
Logger.getLogger("").setLevel(Level.WARNING);
169+
170+
if (args.length == 0) {
171+
System.out.println("");
172+
System.out.println("Usage: COMMAND <auth-file> <dropbox-path>");
173+
System.out.println("");
174+
System.out.println(" <auth-file>: An \"auth file\" that contains the information necessary to make");
175+
System.out.println(" an authorized Dropbox API request. Generate this file using the \"authorize\"");
176+
System.out.println(" example program.");
177+
System.out.println("");
178+
System.out.println(" <dropbox-path>: The path on Dropbox to watch for changes.");
179+
System.out.println("");
180+
return;
181+
}
182+
183+
if (args.length != 2) {
184+
System.err.println("Expecting exactly 2 arguments, got " + args.length + ".");
185+
System.err.println("Run with no arguments for help.");
186+
System.exit(1); return;
187+
}
188+
189+
String authFile = args[0];
190+
String path = args[1];
191+
192+
// Read auth info file.
193+
DbxAuthInfo auth;
194+
try {
195+
auth = DbxAuthInfo.Reader.readFromFile(authFile);
196+
}
197+
catch (JsonReader.FileLoadException ex) {
198+
System.err.println("Error loading <auth-file>: " + ex.getMessage());
199+
System.exit(1); return;
200+
}
201+
202+
longpoll(auth, path);
203+
}
204+
}

examples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<modules>
1515
<module>authorize</module>
1616
<module>account-info</module>
17+
<module>longpoll</module>
1718
<module>upload-file</module>
1819
<module>web-file-browser</module>
1920
<module>upgrade-oauth1-token</module>

src/com/dropbox/core/DbxRequestConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class DbxRequestConfig
5151
/**
5252
* The {@link HttpRequestor} implementation to use when making
5353
* HTTP requests to the Dropbox API servers. If you don't specify one, this defaults to
54-
* {@link StandardHttpRequestor#Instance}.
54+
* {@link StandardHttpRequestor#INSTANCE}.
5555
*/
5656
public final HttpRequestor httpRequestor;
5757

@@ -76,6 +76,6 @@ public DbxRequestConfig(String clientIdentifier, /*@Nullable*/String userLocale,
7676
*/
7777
public DbxRequestConfig(String clientIdentifier, String userLocale)
7878
{
79-
this(clientIdentifier, userLocale, StandardHttpRequestor.Instance);
79+
this(clientIdentifier, userLocale, StandardHttpRequestor.INSTANCE);
8080
}
8181
}

src/com/dropbox/core/http/HttpRequestor.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,32 @@
88
import java.util.Map;
99
import java.util.TreeMap;
1010

11+
import java.util.concurrent.TimeUnit;
12+
1113
/**
1214
* An interface that the Dropbox client library uses to make HTTP requests.
1315
* If you're fine with the standard Java {@link java.net.HttpURLConnection}
14-
* implementation, then just use {@link StandardHttpRequestor#Instance}.
16+
* implementation, then just use {@link StandardHttpRequestor#INSTANCE}.
1517
*/
1618
public abstract class HttpRequestor
1719
{
20+
/**
21+
* Default timeout, in milliseconds, for opening a connection to a network resource.
22+
*
23+
* A value of 0 indicates the timeout should be disabled.
24+
*/
25+
public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
26+
/**
27+
* Default timeout, in milliseconds, for receiving a response from a network resource.
28+
*
29+
* A value of 0 indicates the timeout should be disabled.
30+
*/
31+
public static final long DEFAULT_READ_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
32+
1833
public abstract Response doGet(String url, Iterable<Header> headers) throws IOException;
1934
public abstract Uploader startPost(String url, Iterable<Header> headers) throws IOException;
2035
public abstract Uploader startPut(String url, Iterable<Header> headers) throws IOException;
2136

22-
/**
23-
* The default socket connect/read/write timeout.
24-
*/
25-
public static final int DefaultTimeoutMillis = 20 * 1000;
26-
2737
/**
2838
* A simple structure holding an HTTP header, which is key/value pair.
2939
* Used with {@link HttpRequestor}.

src/com/dropbox/core/http/OkHttpRequestor.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,28 @@
2424
* You can only use this if your project includes the OkHttp library.
2525
*
2626
* <p>
27-
* To use this, pass {@link #Instance} to the {@link com.dropbox.core.DbxRequestConfig} constructor.
27+
* To use this, pass {@link #INSTANCE} to the {@link com.dropbox.core.DbxRequestConfig} constructor.
2828
* </p>
2929
*/
3030
public class OkHttpRequestor extends HttpRequestor
3131
{
32+
/**
33+
* A thread-safe instance of {@code OkHttpRequestor} that connects directly
34+
* (as opposed to using a proxy).
35+
*/
36+
public static final OkHttpRequestor INSTANCE = new OkHttpRequestor(defaultOkHttpClient());
37+
3238
private final OkHttpClient client;
3339

3440
private static OkHttpClient defaultOkHttpClient()
3541
{
3642
OkHttpClient client = new OkHttpClient();
37-
client.setConnectTimeout(DefaultTimeoutMillis, TimeUnit.MILLISECONDS);
38-
client.setReadTimeout(DefaultTimeoutMillis, TimeUnit.MILLISECONDS);
39-
client.setWriteTimeout(DefaultTimeoutMillis, TimeUnit.MILLISECONDS);
43+
client.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
44+
client.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
45+
client.setWriteTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
4046
return client;
4147
}
4248

43-
/**
44-
* A thread-safe instance of {@code OkHttpRequestor} that connects directly
45-
* (as opposed to using a proxy).
46-
*/
47-
public static final OkHttpRequestor Instance = new OkHttpRequestor(defaultOkHttpClient());
48-
4949
public OkHttpRequestor(OkHttpClient client)
5050
{
5151
this.client = client;

0 commit comments

Comments
 (0)