Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: make Twilio not singleton #675

Closed
wants to merge 40 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f742ad7
docs: add unfinished report to repo (#2)
emilmyresten Mar 4, 2022
5d0ee1f
docs: change report to correct one (#2)
emilmyresten Mar 4, 2022
5ec80d1
Branch/emil into main (#6)
emilmyresten Mar 4, 2022
33d7dc3
merge: pr #14, creator now takes object reference instead of static m…
emilmyresten Mar 4, 2022
ab2861a
merge: PR #15 update usage of .create() to .create(tw) in all tests
emilmyresten Mar 4, 2022
41c4ff9
merge: PR #16, feat: update Updater to take in Twilio (#13)
SimonOsnes Mar 5, 2022
9f0651f
merge: PR #18: fix bug in .setUsername method (#17)
emilmyresten Mar 5, 2022
e9f1b4e
test: add multitenancy test and cleanup testclass (#19)
emilmyresten Mar 5, 2022
3e40e09
merge: PR #21, test: change all occurences of .update() to .update(tw…
SimonOsnes Mar 5, 2022
c40893c
feat: update Fetcher method update() to update(Twilio) #11
ViktorAryd Mar 5, 2022
6192b3a
test: change tests to use updated method call update(Twilio) #11
ViktorAryd Mar 5, 2022
ff07500
merge: PR #20 add multitenancy test and cleanup TwilioTest.java (#19)
SimonOsnes Mar 5, 2022
f9f5dc0
merge: PR #22
SimonOsnes Mar 5, 2022
b5d101a
UML diagram added to report-group8.md (#2)
emilmyresten Mar 5, 2022
06715bf
docs: add UML-before refactoring to report (#2)
emilmyresten Mar 5, 2022
dd1eb0c
feat: updates Deleter to take in non-singleton twilio instance (#8)
joakim-olsson Mar 5, 2022
158fd06
test: update new method call signature to take in instance of twilio …
joakim-olsson Mar 5, 2022
a05cf77
docs: adds essence to repo report (#2)
joakim-olsson Mar 5, 2022
3559638
merge: PR #24: update Delete to take in Twilio (#8)
joakim-olsson Mar 5, 2022
ecb5421
feat: update methods in Reader and CallReaderExample to take in Twili…
jovaanchan Mar 5, 2022
8af7acc
test: change read tests to use Twilio instance (#11)
jovaanchan Mar 5, 2022
bc0d329
fix: renamed two testmethods that had been renamed faulty in refactor…
emilmyresten Mar 5, 2022
b90e615
test: remove @mocked for twilio class in tests (#23)
SimonOsnes Mar 6, 2022
f38ff24
chore: add logs before and after patch
SimonOsnes Mar 6, 2022
75b368c
docs: update javadocs where method params changed (#25)
SimonOsnes Mar 6, 2022
de96212
docs: git diff, overall experience, emils time(#2)
emilmyresten Mar 6, 2022
ae8a731
docs: update README.md to not use .init()
SimonOsnes Mar 6, 2022
739f3eb
merge: solving merge conflict
SimonOsnes Mar 6, 2022
64db532
docs: adds design pattern discussion to repo report (#2)
joakim-olsson Mar 6, 2022
e7c3192
test: adds additional RequestValidator test cases
joakim-olsson Mar 6, 2022
1be3fbf
merge: adds additional RequestValidator test cases
joakim-olsson Mar 6, 2022
613eba9
docs: Joakim effort spent (#2)
joakim-olsson Mar 6, 2022
c21efd8
docs: changed wrong effort spent (#2)
joakim-olsson Mar 6, 2022
f3b1b11
docs: add time spent to report (#2)
ViktorAryd Mar 6, 2022
4870932
docs: change some section of report, mostly looked ok (#2)
ViktorAryd Mar 6, 2022
63b07b6
docs: add time spent to report (#2)
jovaanchan Mar 7, 2022
33bccd9
docs: add mention of where to find test logs
ViktorAryd Mar 7, 2022
a539133
docs: add effort spent for Simon (#2)
SimonOsnes Mar 7, 2022
834aea3
docs: update report after presentation (#2)
SimonOsnes Mar 7, 2022
0634d45
chore: clean patch for PR
SimonOsnes Mar 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ release.properties
.project
.settings/
settings.json


*.DS_Store
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ If you run into trouble with local tests, use:
String accountSid = "ACXXXXXX";
String authToken = "XXXXXXXX";

Twilio.init(accountSid, authToken);
Twilio twilio = new Twilio(accountSid, authToken);
```

### Specify Region and/or Edge

To take advantage of Twilio's [Global Infrastructure](https://www.twilio.com/docs/global-infrastructure), specify the target Region and/or Edge for the client:

```java
Twilio.init(accountSid, authToken);
Twilio.setRegion("au1");
Twilio.setEdge("sydney");
Twilio twilio = new Twilio(accountSid, authToken);
twilio.setRegion("au1");
twilio.setEdge("sydney");
```

This will result in the `hostname` transforming from `api.twilio.com` to `api.sydney.au1.twilio.com`.
Expand All @@ -112,25 +112,29 @@ If using these variables, the above client initialization can be skipped.
### Send an SMS

```java
Twilio twilio = new Twilio(accountSid, authToken);

Message message = Message.creator(
new PhoneNumber("+15558881234"), // To number
new PhoneNumber("+15559994321"), // From number
"Hello world!" // SMS body
).create();
).create(twilio);

System.out.println(message.getSid());
```

### Make a Call

```java
Twilio twilio = new Twilio(accountSid, authToken);

Call call = Call.creator(
new PhoneNumber("+15558881234"), // To number
new PhoneNumber("+15559994321"), // From number

// Read TwiML at this URL when a call connects (hold music)
new URI("http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient")
).create();
).create(twilio);

System.out.println(call.getSid());
```
Expand All @@ -141,11 +145,13 @@ System.out.println(call.getSid());
import com.twilio.exception.ApiException;

try {
Twilio twilio = new Twilio(accountSid, authToken);

Message message = Message.creator(
new PhoneNumber("+15558881234"), // To number
new PhoneNumber("+15559994321"), // From number
"Hello world!" // SMS body
).create();
).create(twilio);

System.out.println(message.getSid());
} catch (final ApiException e) {
Expand Down
158 changes: 64 additions & 94 deletions src/main/java/com/twilio/Twilio.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,21 @@
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.io.File;


/**
* Singleton class to initialize Twilio environment.
* Class to initialize Twilio environment.
*/
public class Twilio {

public static final String VERSION = "8.27.0";
public static final String JAVA_VERSION = System.getProperty("java.version");

private static String username = System.getenv("TWILIO_ACCOUNT_SID");
private static String password = System.getenv("TWILIO_AUTH_TOKEN");
private static String accountSid; // username used if this is null
private static String region = System.getenv("TWILIO_REGION");
private static String edge = System.getenv("TWILIO_EDGE");
private static volatile TwilioRestClient restClient;
public class Twilio implements TwilioAPI {

private String username = System.getenv("TWILIO_ACCOUNT_SID");
private String password = System.getenv("TWILIO_AUTH_TOKEN");
private String accountSid; // username used if this is null
private String region = System.getenv("TWILIO_REGION");
private String edge = System.getenv("TWILIO_EDGE");
private volatile TwilioRestClient restClient;
private static volatile ExecutorService executorService;

private Twilio() {
}

/*
* Ensures that the ExecutorService is shutdown when the JVM exits.
*/
Expand All @@ -53,9 +47,9 @@ public void run() {
* @param username account to use
* @param password auth token for the account
*/
public static synchronized void init(final String username, final String password) {
Twilio.setUsername(username);
Twilio.setPassword(password);
public Twilio(final String username, final String password) {
setUsername(username);
setPassword(password);
}

/**
Expand All @@ -65,90 +59,78 @@ public static synchronized void init(final String username, final String passwor
* @param password auth token for the account
* @param accountSid account sid to use
*/
public static synchronized void init(final String username, final String password, final String accountSid) {
Twilio.setUsername(username);
Twilio.setPassword(password);
Twilio.setAccountSid(accountSid);
public Twilio(final String username, final String password, final String accountSid) {
setUsername(username);
setPassword(password);
setAccountSid(accountSid);
}


/**
* Set the username.
*
* @param username account to use
* @throws AuthenticationException if username is null
* {@inheritdoc}
*/
public static synchronized void setUsername(final String username) {
public synchronized void setUsername(final String username) {
if (username == null) {
throw new AuthenticationException("Username can not be null");
}

if (!username.equals(Twilio.username)) {
Twilio.invalidate();
if (!username.equals(this.username)) {
this.invalidate();
}

Twilio.username = username;
this.username = username;
}

/**
* Set the auth token.
*
* @param password auth token to use
* @throws AuthenticationException if password is null
* {@inheritdoc}
*/
public static synchronized void setPassword(final String password) {
public synchronized void setPassword(final String password) {
if (password == null) {
throw new AuthenticationException("Password can not be null");
}

if (!password.equals(Twilio.password)) {
Twilio.invalidate();
if (!password.equals(this.password)) {
this.invalidate();
}

Twilio.password = password;
this.password = password;
}

/**
* Set the account sid.
*
* @param accountSid account sid to use
* @throws AuthenticationException if account sid is null
* {@inheritdoc}
*/
public static synchronized void setAccountSid(final String accountSid) {
public synchronized void setAccountSid(final String accountSid) {
if (accountSid == null) {
throw new AuthenticationException("AccountSid can not be null");
}

if (!accountSid.equals(Twilio.accountSid)) {
Twilio.invalidate();
if (!accountSid.equals(this.accountSid)) {
this.invalidate();
}

Twilio.accountSid = accountSid;
this.accountSid = accountSid;
}

/**
* Set the region.
*
* @param region region to make request
* {@inheritdoc}
*/
public static synchronized void setRegion(final String region) {
if (!Objects.equals(region, Twilio.region)) {
Twilio.invalidate();
public synchronized void setRegion(final String region) {
if (!Objects.equals(region, this.region)) {
this.invalidate();
}

Twilio.region = region;
this.region = region;
}

/**
* Set the edge.
*
* @param edge edge to make request
* {@inheritdoc}
*/
public static synchronized void setEdge(final String edge) {
if (!Objects.equals(edge, Twilio.edge)) {
Twilio.invalidate();
public synchronized void setEdge(final String edge) {
if (!Objects.equals(edge, this.edge)) {
this.invalidate();
}

Twilio.edge = edge;
this.edge = edge;
}

/**
Expand All @@ -157,33 +139,29 @@ public static synchronized void setEdge(final String edge) {
* @return the Twilio Rest Client
* @throws AuthenticationException if initialization required and either accountSid or authToken is null
*/
public static TwilioRestClient getRestClient() {
if (Twilio.restClient == null) {
synchronized (Twilio.class) {
if (Twilio.restClient == null) {
Twilio.restClient = buildRestClient();
}
}
public synchronized TwilioRestClient getRestClient() {
if (restClient == null) {
restClient = buildRestClient();
}

return Twilio.restClient;
return restClient;
}

private static TwilioRestClient buildRestClient() {
if (Twilio.username == null || Twilio.password == null) {
private TwilioRestClient buildRestClient() {
if (this.username == null || this.password == null) {
throw new AuthenticationException(
"TwilioRestClient was used before AccountSid and AuthToken were set, please call Twilio.init()"
);
}

TwilioRestClient.Builder builder = new TwilioRestClient.Builder(Twilio.username, Twilio.password);
TwilioRestClient.Builder builder = new TwilioRestClient.Builder(this.username, this.password);

if (Twilio.accountSid != null) {
builder.accountSid(Twilio.accountSid);
if (this.accountSid != null) {
builder.accountSid(this.accountSid);
}

builder.region(Twilio.region);
builder.edge(Twilio.edge);
builder.region(this.region);
builder.edge(this.edge);

return builder.build();
}
Expand All @@ -193,10 +171,8 @@ private static TwilioRestClient buildRestClient() {
*
* @param restClient rest client to use
*/
public static void setRestClient(final TwilioRestClient restClient) {
synchronized (Twilio.class) {
Twilio.restClient = restClient;
}
public synchronized void setRestClient(final TwilioRestClient restClient) {
this.restClient = restClient;
}

/**
Expand All @@ -206,11 +182,7 @@ public static void setRestClient(final TwilioRestClient restClient) {
*/
public static ExecutorService getExecutorService() {
if (Twilio.executorService == null) {
synchronized (Twilio.class) {
if (Twilio.executorService == null) {
Twilio.executorService = Executors.newCachedThreadPool();
}
}
Twilio.executorService = Executors.newCachedThreadPool();
}
return Twilio.executorService;
}
Expand All @@ -221,9 +193,7 @@ public static ExecutorService getExecutorService() {
* @param executorService executor service to use
*/
public static void setExecutorService(final ExecutorService executorService) {
synchronized (Twilio.class) {
Twilio.executorService = executorService;
}
Twilio.executorService = executorService;
}

/**
Expand Down Expand Up @@ -251,16 +221,16 @@ public static void validateSslCertificate() {
/**
* Invalidates the volatile state held in the Twilio singleton.
*/
private static void invalidate() {
Twilio.restClient = null;
private void invalidate() {
this.restClient = null;
}

/**
* Attempts to gracefully shutdown the ExecutorService if it is present.
* {@inheritdoc}
*/
public static synchronized void destroy() {
if (executorService != null) {
executorService.shutdown();
public synchronized void destroy() {
if (Twilio.executorService != null) {
Twilio.executorService.shutdown();
}
}
}
Loading