Third-party API wrapper in Java: how to design - java

Suppose, there's a site that provides an API, such as this:
Users post questions, answers on that site
You can make GET and
POST calls
There are two types of authentication: weak (only gives
read rights) and strong (gives both read and write rights)
Right now, I'd like to read users' questions and answers (only need weak access) and send them messages or post my own questions in the future (would need strong access)
The site's API has actions both to do with users (e.g. send a message) and with site in general (see about, see most popular questions)
What I have right now looks like this:
public class Wrapper {
private AccessToken accessToken;
public Wrapper(...) {
//does some stuff
//gets access token:
getAccessToken(...);
}
public AccessToken getAccessToken(...) {
AccessToken result;
//gets access token, given the auth info provided as arguments
accessToken = result;
return result;
}
public ArrayList<Question> getQuestions(User user) {
//gets user's questions, using the accessToken field
//set's a user's questions field to the result and returns the result
}
public ArrayList<Answer> getAnswers(User user) {
//same as previous
}
public boolean sendMessage(User user) {
//sends a message, if current accessToken is strong
}
}
and User class:
class User {
private String username;
private ArrayList<Question> questions;
private ArrayList<Answer> answers;
public User(String username) {this.username=username;}
//getters and setters
}
So, to use it you would use something like this:
public class Main {
public static void main(String[] args) {
Wrapper wrapper = new Wrapper(...);
ArrayList<Question> questions = wrapper.getQuestions(new User("username"));
wrapper.sendMessage(new User("username2"));
}
}
I have issues with this.
First of all, class User feels superfluous, since all the functionality is inside the Wrapper class.
Second, I wonder if what my methods do is wright - from the design's perspective: in getAccessToken I both return AccessToken and set a Wrapper's field accessToken to the result. Is this the right approach? Or should the method only return access token and then that result should be assigned to a class' field explicitly? Same goes for the getQuestions and getAnswers methods: they both get the ArrayLists, return them and assign a User's field to the result - all inside the single method.
I would like for a User class to have some meaning. I thought of doing it something like that:
Wrapper wrapper = new Wrapper(...);
User user = new User("username");
user.getQuestions(wrapper.getAccessToken());
user.sendMessage(wrapper.getAccessToken());
So, the Wrapper class would only serve as a place to get an access token from, which doesn't feel right as well. I could place the access token functionality inside the User class and use it like this:
User user = new User("username", ...);
user.getQuestions();
user.sendMessage();
The User's constructor would take both username and auth data, would get access token and store it inside a user and then use it when getting questions/answers or sending messages. I could make the accessToken field inside User class static so that all users shared the same token.
However, there are actions the site API provides, that aren't obviously connected with users: for instance, getting the site's most popular questions. It feels right to use a generic Wrapper class for that purpose which contradicts with the previous approach.
I'm new to this and only know a couple design patterns. Perhaps, there are widespread patterns that are to be used for this type of problem? Any help/advice is appreciated.

There are a few alternatives that you can do to solve your problem, but there is likely not one that is better than all others. The solution you choose will depend on the trade-offs and how you want your system to operate. The following are two common solutions to this type of problem.
Have the Wrapper generate a User: Instead of generating a User object separate from the Wrapper, you can have the Wrapper generate the User object. This allows the Wrapper to embed the AccessToken within the User without any outside client knowing that a user has an AccessToken. For example, you can use the following Wrapper and User definitions:
public class Wrapper {
public Wrapper(...) {
// ... does some stuff, but DOES NOT get an access token ...
}
private AccessToken getAccessToken(...) {
AccessToken result;
// ... gets access token, given the auth info provided as arguments ...
return result;
}
public User findUser(String username, ...) {
return new User(username, getAccessToken(...));
}
}
class User {
private String username;
private final AccessToken token;
public User(String username, AccessToken token) {
this.user = user;
this.token = token;
}
// ... getters and setters ...
}
Note that getAccessToken is now private, as no other client needs to access this method. All of the methods of Wrapper continue to accept a User argument, but they now should obtain the access token by calling getToken on the User object, rather than using a stored AccessToken in Wrapper.
Also note that the token field is final, since the access token associated with a User should not change over the life of a User object.
Embed the Wrapper in User: This technique is similar to (1), but it also embeds the Wrapper object in the User object. This allows the User class to act as a live object, which can be queried for questions and answers and can be used to send messages. Since all of the methods of Wrapper accept a User argument, this is a good sign that the methods should be moved to User. The following is a halfway point to refactor the Wrapper methods into User:
public class Wrapper {
public Wrapper(...) {
// ... does some stuff, but DOES NOT get an access token ...
}
private AccessToken getAccessToken(...) {
AccessToken result;
// ... gets access token, given the auth info provided as arguments ...
return result;
}
public User findUser(String username, ...) {
return new User(username, getAccessToken(...));
}
public ArrayList<Question> getQuestions(User user) {
//gets user's questions, using the accessToken field
//set's a user's questions field to the result and returns the result
}
public ArrayList<Answer> getAnswers(User user) {
//same as previous
}
public boolean sendMessage(User user) {
//sends a message, if current accessToken is strong
}
}
class User {
private String username;
private final AccessToken token;
private final Wrapper wrapper;
public User(String username, AccessToken token, Wrapper wrapper) {
this.user = user;
this.token = token;
this.wrapper = wrapper;
}
public List<Question> findQuestions() {
return wrapper.getQuestions(this);
}
public ArrayList<Answer> findAnswers() {
return wrapper.getAnswers(this);
}
public boolean sendMessage() {
return wrapper.sendMessage(this);
}
// ... getters and setters ...
}
Using this technique, clients can now directly get questions and answers from a User object. Note that the findQuestions and findAnswers methods start with find. This tips off clients that this call may be a long call (as opposed to getQuestions or getAnswers, which would make a client assume that it is a simple getter and the method would return nearly-instantly). The fact that these methods execute a remote call should also be documented in the Java-docs for the methods. If the call takes a long time, the methods should return a Future (or a similar object) and be made asynchronously.
If you want to go all-in on the refactor, you can move all of the implementation details from the Wrapper class to the User class:
public class Wrapper {
public Wrapper(...) {
// ... does some stuff, but DOES NOT get an access token ...
}
private AccessToken getAccessToken(...) {
AccessToken result;
// ... gets access token, given the auth info provided as arguments ...
return result;
}
public User findUser(String username, ...) {
return new User(username, getAccessToken(...));
}
}
class User {
private String username;
private final AccessToken token;
private final Wrapper wrapper;
public User(String username, AccessToken token, Wrapper wrapper) {
this.user = user;
this.token = token;
this.wrapper = wrapper;
}
public List<Question> findQuestions() {
// ... find the questions remotely ...
}
public ArrayList<Answer> findAnswers() {
// ... find the answers remotely ...
}
public boolean sendMessage() {
// ... send message remotely ...
}
// ... getters and setters ...
}
This may not be the best approach, as it may be a better idea to keep the details of accessing the remote API abstracted in the Wrapper class. This is a judgment call that will depend on the nature of your specific application.
There are numerous other techniques that you can do, but the above two are common approaches to the problem you are trying to solve.

Related

How can I print different logs for different user roles in Java?

I'm trying to do logging for different user roles (admin, developers, end users, etc.), and I want to show a different/filtered log for end-users. How can I achieve that?
How can I approach this problem?
You could probably tackle this problem with using something like a ThreadLocal variable in your logging filter. That is set by the code that does the authentication. And based on that do your different logging.
A simple example, in your filter class you could have something like this:
private static final ThreadLocal<String> ROLE = new ThreadLocal<>();
public void doTheFiltering() {
String role = ROLE.get();
if (role == null) {
// not authenticated...
} else if (role.equals("ADMIN") {
// filter based on admin privileges
} else ...
}
public static void setRole(String role) {
ROLE.set(role);
}
public static void clearRole() {
ROLE.remove();
}
And in your authentication code:
try {
String role = ... // find out role
MyFilter.setRole(role);
// continue with whatever you're doing
} finally {
// clean up the role on this thread.
// this is especially needed when you're using a thread pool that handles requests.
MyFilter.clearRole();
}

Can't mock the methods inside the constructor

Here is my first class where the constructor has an object calling methods in other class.
Class Search{
public Search(String username, JSONObject accounts) throws Exception {
Credentials credentials = new Credentials(username);
String Uid = credentials.getUserName();
String Pwd = new String(credentials.getCredentials().getPassword());
}
public getDOB(){
--------------------
-------------
}
}
Class Credentaials:
import javax.resource.spi.security.PasswordCredential;
Public class Credentials{
public Credentials(String name){
}
public PasswordCredential getCredentials(){
return passwordCredential;
}
public String getUserName(){
PasswordCredential localCredential = getCredentials();
return localCredential.getUsername();
}
}
Class test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Search.class, Credentials.class})
public class JunitTest {
#Test
public void playTest() {
PasswordCredential pwdCreds = new PasswordCredential();
pwdCreds.setPassword("test");
Credentials credentials = new Credentials("user");
Credentials credentials = Mockito.spy(credentials);
Mockito.doReturn(pwdCreds).when(credentials).getCredentials();
Mockito.doReturn("cmApptest").when(credentials).getUserName();
Search search = new Search("username", jsonobject);
search.getDOB();
}
}
Whenever I debug the test class, it is executing the getCredentials and getUserName methods even after I mocked them. I was expecting the actual methods not to execute, instead it should return the values as I mentioned in the JunitTest class.
You aren't replacing the real version of Credentials that is being used in your Search class with a mock. Rather, you're clearly creating and using a real Credentials object inside of your Search object's constructor. For mocking to work, you have to actually replace the credentials object in your Search object with a mock. Just creating a mock of the same type somewhere in your code doesn't cause it to replace instances of the real object somewhere else in your code.
Often, dependency injection is used to introduce mocking, like with Spring. Here's a simple way to do what you want. Redefine your Search constructor like this:
class Search {
Search(String username, JSONObject accounts, Credentials creds) throws Exception {
Credentials credentials = creds? creds : new Credentials(username);
String Uid = credentials.getUserName();
String Pwd = new String(credentials.getCredentials().getPassword());
}
Search(String username, JSONObject accounts) throws Exception {
this(username, accounts, null);
}
}
The behavior of your production code will not be affected, but you can optionally construct Search with a mock.
Credentials credentials = new Credentials("user");
Credentials credentials1 = Mockito.spy(credentials);
Mockito.doReturn(pwdCreds).when(credentials1).getCredentials();
Mockito.doReturn("cmApptest").when(credentials1).getUserName();
Search search = new Search("username", jsonobject, credentials1);
search.getDOB();
There's no magic in terms of your code using a mock rather than the real object. Mocking frameworks just let you easily create stand-in objects that act in very specific ways for testing. You still have to cause those objects to be used by your code.
Also, you don't really need/want a spy here. You really want a mock, because you're defining the behavior of all of the methods in Credentials. With a mock, you wouldn't need to instantiate your Credentials object at all. So the first lines of the test code I gave above could could be:
Credentials credentials1 = Mockito.mock(Credentials.class);
(or something like this. I'm not actually trying this code)

How to keep user information after login in JavaFX Desktop Application

I have an application with login screen, after the user is authenticated some "data" is retrieved from a database (username and privileges), until here everything is well.
After the login process I need to access to the privileges to generate some menus across different JavaFX scenes, this all throughout the entire application in any moment, but I donĀ“t know how to do it.
What I am looking for is a behavior such as SESSION variable in PHP (yep, I come from web development), which keeps information alive and accesible during a certain period of time (usually while user is logged in).
The information I have found about this topic is unclear and outdated, I mean, solutions that do not apply for JavaFX 2 or solutions with old design patterns.
I have created an image because in other forums I have found the same question but that is misunderstood, so I hope this could help.
Thanks to everyone.
You can use singleton design patter. For example:
public final class UserSession {
private static UserSession instance;
private String userName;
private Set<String> privileges;
private UserSession(String userName, Set<String> privileges) {
this.userName = userName;
this.privileges = privileges;
}
public static UserSession getInstace(String userName, Set<String> privileges) {
if(instance == null) {
instance = new UserSession(userName, privileges);
}
return instance;
}
public String getUserName() {
return userName;
}
public Set<String> getPrivileges() {
return privileges;
}
public void cleanUserSession() {
userName = "";// or null
privileges = new HashSet<>();// or null
}
#Override
public String toString() {
return "UserSession{" +
"userName='" + userName + '\'' +
", privileges=" + privileges +
'}';
}
}
and use the UserSession whenever you need. When you do login you just call: UserSession.getInstace(userName, privileges) and when you do log out: UserSession.cleanUserSession()
You can use the Java Preferences. At the first successful authentication, you need to write information about user in the Preferences like this:
Preferences userPreferences = Preferences.userRoot();
userPreferences.put(key,value);
And then take the data from the Preferences:
Preferences userPreferences = Preferences.userRoot();
String info = userPreferences.get(key,value);
you can put the user data into a local cache, such as Guava Cache.
for example, we use HashMap to store the user data:
class Session{ private HashMap<String,Object> store = new HashMap(); public static final Session INSTANCE = Session(); public void put(String key, Object value){ this.store.put(key,value); } public Object get(String key) { return this.store.get(key); } } in your controller: // call server side API to retrieve user User user = userApi.login(username,password); if(user != null){ Session.INSTANCE.put("USER",user); } // in other class User user = Session.INSTANCE.get("USER");

Accessing objects that have already been created of the same class Android

So I am creating a chat app for android and I'm using Java and I need some help wrapping my head around some things. Whenever the user first registers, I am creating a new object of a class named User. When they enter the next layout, I need to access that objects data.
public class User {
public String username;
public User() {}
public User(String username) {
this.username = username;
}
public String getUsername(){
return username;
}
}
This is my User class. When they send a message, I need to be able to grab their username from this User object from an entirely different method without passing the object through a parameter. I can't seem to wrap my head around how to access their information and none of my methods seem to work. Any help is appreciated
If you do
User myUser = new User();
the variable myUser contains a reference to the newly created object. You must keep this reference around in order to later access the object. How exactly you do this depends on the logic of your program. Sometimes you would keep it in a field of another object or pass it around as parameter. For example
un = myUser.getUsername();
or
void myMethod(User theUser) {
...
String un = theUser.getUsername();
}
...
// call the method passing the user reference
myMethod(myUser);
in the main class make the data object... static
public static Model obj;
obj= new Model();
then from other class access it with your class name
example
main.obj;
I solved this issue by just using SharedPreferences. I stored the username associated with the key of each user. This way, I can always search the username for each user.

SFTP server multiple user authentication

I am trying to extend the user authentication example, which is also presented here, so that multiple users can login to the server. I would also like to assign a different home directory for each user. So far, I haven't been able to find any such utility provided by the Apache SSHD API, so I have tried the following workaround, by using the utilities provided by the Apache FtpServer.
What I attempt to do is:
create a UserManager object to manage a list of users and store their information in a property file, in a way similar to this example
create a PasswordAuthenticator that makes use of the UserManager in its authenticate method, as follows:
public class MyPasswordAuthenticator implements PasswordAuthenticator {
private UserManager userManager;
public MyPasswordAuthenticator(){
this.userManager=null;
}
public MyPasswordAuthenticator(UserManager manager) {
this.userManager=manager;
}
#Override
public boolean authenticate(String username, String password, ServerSession session) throws PasswordChangeRequiredException {
if (this.userManager==null) return false;
User usr=null;
try {
usr = userManager.getUserByName(username);
} catch (FtpException e) {
e.printStackTrace();
}
if (usr==null) return false;
else{
String pass=usr.getPassword();
return password.equals(pass);
}
}
}
However, the usr.getPassword() returns null, even though a) the password fields in the property file do have values b) I have checked the functions getName() and getHomeDirectory() and they return their respective String values.
My question is, why does this happen and what should be done to fix this?
I have found a way to make it work, it is:
usr = this.userManager.authenticate(new UsernamePasswordAuthentication(username, password));

Categories