Rest client design decisions - java

I'm developing a rest client using retrofit. The rest server is using oauth as authentication. For this task I don't have to care about the token expiring. So basically I first make a request for the token and then append that to all subsequent calls.
As of now I'm using two classes. One to get the access token and another one for everything else. I think I would like to merge these two... but I'm not sure how to do that.
The first class only has one method which takes a username and password and a retrofit callback interface. I like the simplicity of the callback but would like to somehow abstract it so I could easily change from retrofit to something else if needed.
public class RequestAccessToken implements IRequestAccessToken {
private String username;
private String password;
private IRestAPI client;
public RequestAccessToken()
{
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.ENDPOINT)
.build();
client = restAdapter.create(IRestAPI.class);
}
#Override
public void requestAccessToken(String username, String password, Callback callback) {
String grantType = Config.grantType;
String clientId = Config.clientId;
String clientSecret = Config.clientSecret;
client.getAccessToken(grantType, username, password, clientId, clientSecret, callback);
}
}
The second class takes the access token as constructor argument and appends it to all http requests.
public class RestClient implements IRestClient {
private static final String TAG = RestClient.class.getSimpleName();
private IRestAPI client;
public RestClient(final String accessToken)
{
RequestInterceptor requestInterceptor = new RequestInterceptor()
{
#Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", "Bearer " + accessToken);
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.ENDPOINT)
.setRequestInterceptor(requestInterceptor)
.build();
client = restAdapter.create(IRestAPI.class);
}
#Override
public List<User> requestUsers() {
return client.requestUsers();
}
#Override
public List<Soemthing> requestSomething() {
return client.requestSomething();
}
#Override
public List<SoemthingElse> requestSomethingElse() {
return client.requestSomethingElse();
}
}
I would love some input and suggestions on how to do this better and perhaps merge the two classes. I'm thinking of making the requestAccessToken method of the RequestAccessToken a static member of the RestClient class. At least that would merge the two class. But I'm using a factory to create the RestClient and if I declare a static method on it which I use throughout my code I get tight coupling... Suggestions?

After discussing this elsewhere I came up with the following code.
public class RestClient implements IRestClient {
private static final String TAG = RestClient.class.getSimpleName();
private IRestAPI client;
private String username;
private String password;
private RequestAccessToken requestAccessToken;
private Access access;
private static RestClient singleton;
// TODO: use Dagger
public static RestClient getInstance()
{
if(singleton == null)
{
singleton = new RestClient();
}
return singleton;
}
/**
* Access declared as private to prevent instantiation outside of this class.
*/
private RestClient()
{
requestAccessToken = new RequestAccessToken();
}
/**
* Request an access token.
*
* #return A string containing the access token
*/
public String requestAccessToken(String username, String password)
{
this.username = username;
this.password = password;
this.access = requestAccessToken.requestAccessToken(username, password);
return this.access.getAccess_token();
}
/**
* Wrapper for {#link #requestAccessToken(String, String) requestAccessToken}
* #return
*/
public String requestAccessToken()
{
if(username == null || password == null) {
throw new IllegalArgumentException("missing user/pass");
}
return requestAccessToken(username, password);
}
/**
* Eventually we want to look at Access.getExpires_in() and return weather or not the token is
* expired.
*
* #return value indicating weather or not the token is expired.
*/
public boolean isAccessTokenExpired()
{
return false;
}
/**
* Return an instance of a rest client with a valid access token.
* #return
*/
private IRestAPI getClient()
{
if(access == null)
{
requestAccessToken();
}
if(isAccessTokenExpired())
{
// Refresh token
}
if(client == null) {
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", "Bearer " + access.getAccess_token());
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.ENDPOINT)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setRequestInterceptor(requestInterceptor)
.build();
client = restAdapter.create(IRestAPI.class);
}
return client;
}
/**
* Login to the api and get a access token.
*
* #param username
* #param password
*/
#Override
public void login(String username, String password)
{
Log.d(TAG, "login");
requestAccessToken(username, password);
}
/**
* Request a list of organizations.
* #return List of Organizations.
*/
#Override
public List<Organization> requestOrganizations() {
Log.d(TAG, "requestOrganizations");
// TODO: error handling
// TODO: caching... which isn't http
return getClient().requestOrganizations();
}
// Add more api requests here
}

Related

How to get the body from a POST call. JAX-RS

I make a POST-type call in eclipse / java using JAX-RS
I can not handle the return in the method predictCid
This method sends the textToPredict parameter and receives a return string, how can I get this value and set it to the variable, textPredicted?
#Path("Predicao")
public class PredicaoCIDResource extends BaseResource {
#POST
#Path("predicaoCid")
public RetornoGenerico<PredicaoCidVo> predizerCid(PredicaoCidVo predicaoVo) {
System.out.print("\nentrou no método java");
RetornoGenerico<PredicaoCidVo> retorno = new RetornoGenerico<PredicaoCidVo>();
String nomeMetodo = "predicaoCid";
super.criarRetornoSucesso(nomeMetodo, retorno);
System.out.print("passou pelo super");
try {
System.out.print("\nentrou no try");
PredicaoCidVo predicaoCidVo = new PredicaoCidVo();
Response retornoPred = predictCid(predicaoVo.getTextToPredict());
System.out.print("retornou do método predict");
predicaoCidVo.setTextPredicted(retornoPred.getEntity().toString());
retorno.setRetorno(predicaoCidVo);
} catch (Exception e) {
super.trataExececao(retorno, e, nomeMetodo);
}
return retorno;
}
#POST
#Path("http://127.0.0.1:5000/predict")
#Consumes("application/x-www-form-urlencoded")
private Response predictCid(#FormParam("textToPredict") String predicaoVo) throws IOException {
System.out.print("\nentrou no método predict");
//How get te return ??? String
}
PredicaoVo:
#XmlRootElement
public class PredicaoCidVo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2471424108047814793L;
private String textToPredict;
private String textPredicted;
public String getTextToPredict() {
return textToPredict;
}
public void setTextToPredict(String textToPredict) {
this.textToPredict = textToPredict;
}
public String getTextPredicted() {
return textPredicted;
}
public void setTextPredicted(String textPredicted) {
this.textPredicted = textPredicted;
}
}
The call is made correctly (predictCid), returns with status 200 (OK).
But, I can not return in one of the class variables PredicaoVo.
How do I make this return by filling in, for example, the textPredicted object?
The return, within the method that does the POST, is a simple string
Below, the feedback I have on SOAPUI testing:
<Response xmlns="http://app-homolog/overcare-ws/rest/Profissional/predicaoCid">
<retorno>
<textPredicted>predicao.PredicaoCidVo#3372c9d7</textPredicted>
<textToPredict null="true"/>
</retorno>
<retornoMensagem>
<dsMensagem>predicaoCid.sucesso</dsMensagem>
<dsStackTrace null="true"/>
<dsTitulo>predicaoCid.titulo</dsTitulo>
<idCamada>SUCESSO</idCamada>
</retornoMensagem>
</Response>
Who sends the return to the soapUI is the method predizerCid

Retrofit and Rxjava "Unable to create converter for java.util.List<Model>"

Have been having issue using Rxjava with Retrofit in my android app, everything seems ok and fine in the code implementation but app crashes whenever i navigate to the activity/fragment with the error message below.
//Error message
java.lang.IllegalArgumentException: Unable to create converter for java.util.List<com.thebestprice.bestprice.model.SearchRequestModel>
for method DataEndpointService.getStarredRepositories
//Endpoint Declaration
#GET("users/{user}/starred")
Observable<List<SearchRequestModel>> getStarredRepositories(#Path("user") String username);
//Client Class
public class DataClient {
private static final String PROJECT_BASE_URL = "https://api.github.com/";
private static DataClient instance;
private DataEndpointService queryResultService;
private DataClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
final Retrofit retrofit = new Retrofit.Builder().baseUrl(PROJECT_BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
queryResultService = retrofit.create(DataEndpointService.class);
}
public static DataClient getInstance(){
if (instance == null){
instance = new DataClient();
}
return instance;
}
public io.reactivex.Observable<List<SearchRequestModel>> queryForUserRepo(#NonNull String searchRequestModel){
return queryResultService.getStarredRepositories(searchRequestModel);
}
}
//Fragment to display the list
private void queryResultForSearchData(String userName){
disposable = DataClient.getInstance().
queryForUserRepo(userName).
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Consumer<List<SearchRequestModel>>() {
#Override
public void accept(List<SearchRequestModel> searchRequestModels) throws Exception {
mShimmerFrameLayout.stopShimmerAnimation();
rvAllSearch.setAdapter(new ResultAdapter(getActivity(), searchRequestModels));
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
}`enter code here`
});
}
//gradle dependencies for rxjava and retrofit with compileSdk27
//For using RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
//logging interceptor
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
//For Using retrofit to do network request
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
Don't know what i'm doing wrong, have search for similar issues but doesn't seems to help in my own case
Main point of using RxJava it's avoid loops and list, to invoke Subscriptions and map functions, cause Observables managed all items. So you don't neet to return List.
Just one of example of using Github API with Rx.
public interface GithubService {
String SERVICE_ENDPOINT = "https://api.github.com";
#GET("/users/{login}")
Observable<Github> getUserRx(#Path("login") String login);
#GET("/users/{login}")
Github getUser(#Path("login") String login);
}
.
public class ServiceFactory {
/**
* Creates a retrofit service from an arbitrary class (clazz)
* #param clazz Java interface of the retrofit service
* #param endPoint REST endpoint url
* #return retrofit service with defined endpoint
*/
public static <T> T createRetrofitService(final Class<T> tClass, final String endPoint) {
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(endPoint)
.build();
T service = restAdapter.create(tClass);
return service;
}
}
.
// .....
service = ServiceFactory.createRetrofitService(
GithubService.class, GithubService.SERVICE_ENDPOINT);
service.getUserRx(login)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.cache()
.subscribe(new Subscriber<Github>() {
#Override
public final void onCompleted() {
}
#Override
public final void onError(Throwable e) {
Log.e(LOG, " ErrorRx Default Github" + e.getMessage());
}
#Override
public final void onNext(Github response) {
mCardAdapter.addData(response);
}
});
// .....
As you add GsonConverterFactory.
Check out the Bean you use (SearchRequestModel) the variable or the #SerializedName(alternate) annotation does not have the same variable name.
it work for me

Retrofit single value / array

this is my web service for sending and receiving a string value with key
Urls.class
public class Urls {
public static final String MAIN_URL="example.com";
}
API.class
public interface API {
#POST("user.php")
Call<MainResponse> registerUser(#Body User user);
#POST("user.php")
Call<MainResponse>loginUser(#Body User user);
#POST("contact.php")
Call<MainResponse>checkNumber(#Body Phone phone);
}
WebService.class
public class WebService {
private static WebService instance;
private API api;
public WebService() {
OkHttpClient client = new OkHttpClient.Builder().build();
Retrofit retrofit = new Retrofit.Builder().client(client)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(Urls.MAIN_URL)
.build();
api = retrofit.create(API.class);
}
public static WebService getInstance() {
if (instance == null) {
instance = new WebService();
}
return instance;
}
public API getApi() {
return api;
}
}
MainResponse.class
public class MainResponse {
#SerializedName("status")
public int status;
#SerializedName("message")
public String message;
}
Phone.class
public class Phone {
#SerializedName("phone")
public String phone;
}
MainActivity.class
Phone phone=new Phone();
phone.phone=contactsString[0];
WebService.getInstance().getApi().checkNumber(phone).enqueue(new Callback<MainResponse>() {
#Override
public void onResponse(Call<MainResponse> call, Response<MainResponse> response) {
if (response.body().status==1){
//do something
}
}
#Override
public void onFailure(Call<MainResponse> call, Throwable t) {
}
});
My question is how to edit this to send an array filled with values contactsString[] and receive another array
Your service is in a way to get single request and give you back single response, you should change server side service if you are the backend developer of the service, to get a list of request and give back a list of result
this is your current service:
#POST("contact.php")
Call<MainResponse>checkNumber(#Body Phone phone);
server side developer should change service for you to be able to send in body an object like this for Phones:
public class Phones {
#SerializedName("phones")
public List<String> phones;
}
and in your response you should get list of status and messages with request phones
response like this:
public class MainResponse {
public List<PhoneStatusResponse> phonesStatusList;
}
public class PhoneStatusResponse {
#SerializedName("status")
public int status;
#SerializedName("message")
public String message;
#SerializedName("phoneRequest")
public String phone;
}

Java, Storing JSON Array to class and calling it from other class

I am trying to pull data from class in another class and populate a JPanel with the data, but it is not working for some reason.
Here is the full restConnector class where I pull the JSON data.
As far as I know this works fine.
public class restConnector {
private static final Logger LOGGER = LoggerFactory.getLogger(restConnector.class);
private static final restConnector INSTANCE = new restConnector();
public static restConnector getInstance() {
return restConnector.INSTANCE;
}
private restConnector(){
}
private static String user = "ss";
private static String pwd = "ee
public static String encode(String user, String pwd) {
final String credentials = user+":"+pwd;
BASE64Encoder encoder = new sun.misc.BASE64Encoder();
return encoder.encode(credentials.getBytes());
}
//Open REST connection
public static void init() {
restConnector.LOGGER.info("Starting REST connection...");
try {
Client client = Client.create();
client.addFilter(new LoggingFilter(System.out));
WebResource webResource = client.resource("https://somewebpage.com/
String url = "activepersonal";
ClientResponse response = webResource
.path("api/alerts/")
.queryParam("filter", ""+url)
.header("Authorization", "Basic "+encode(user, pwd))
.header("x-api-version", "1")
.accept("Application/json")
.get(ClientResponse.class);
if (response.getStatus() != 200) {
}else{
restConnector.LOGGER.info("REST connection STARTED.");
}
String output = response.getEntity(String.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new MyNameStrategy());
try {
List<Alert> alert = mapper.readValue(output, new TypeReference<List<Alert>>(){});
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() {
}
}
However, when I try to pull the data in another class it gives me just null values from the system.out.print inside refreshData() method. Here is the code that is supposed to print the data
public class Application{
Alert alerts = new Alert();
public Application() {
refreshData();
}
private void initComponents() {
restConnector.init();
refreshData();
}
private void refreshData() {
System.out.println("appalertList: "+alerts.getComponentAt(0));
}
}
Here is my Alert class
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_EMPTY)
public class Alert {
private int pasID;
private String status;
private boolean shared;
private String header;
private String desc;
public int getPasID() {
return pasID;
}
public void setPasID(int pasID) {
this.pasID = pasID;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public boolean isShared() {
return shared;
}
public void setShared(boolean shared) {
this.shared = shared;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("\n***** Alert Details *****\n");
sb.append("PasID="+getPasID()+"\n");
sb.append("Status="+getStatus()+"\n");
sb.append("Shared="+isShared()+"\n");
sb.append("Header="+getHeader()+"\n");
sb.append("Description="+getDesc()+"\n");
sb.append("*****************************");
return sb.toString();
}
public String getComponentAt(int i) {
return toString();
}
}
I'm kind a lost with this and been stuck here for a couple of days already so all help would be really appreciated. Thanks for the help in advance.
Edit: Formatted the code a bit and removed the NullPointerException as it was not happening anymore.
As stated in comments:
Me: In your first bit of code you have this try { List<Alert> alert.., but you do absolutely nothing with the newly declared alert List<Alert>. It this where the data is supposed to be coming from?
OP: I'm under the impression that that bit of code is the one that pushes the JSON Array to the Alert.class. Is there something I'm missing there?
Me: And what makes you think it does that? All it does is read the json, and the Alert.class argument is the class type argument, so the mapper know the results should be mapped to the Alert attributes when it creates the Alert objects. That's how doing List<Alert> is possible, because passing Alert.class decribes T in List<T>. The List<Alert> is what's returned from the reading, but you have to determine what to actually do with the list. And currently, you do absolutely nothing with it
You maybe want to change the class just a bit.
Now this is in no way a good design, just an example of how you can get it to work. I would take some time to sit and think about how you want the restConnector to be fully utilized
That being said, you can have a List<Alert> alerts; class member in the restConnector class. And have a getter for it
public class restConnector {
private List<Alert> alerts;
public List<Alert> getAlerts() {
return alerts;
}
...
}
Then when deserializing with the mapper, assign the value to private List<Alert> alerts. What you are doing is declaring a new locally scoped list. So instead of
try {
List<Alert> alert = mapper.readValue...
do this instead
try {
alerts = mapper.readValue
Now the class member is assigned a value. So in the Application class you can do something like
public class Application {
List<Alert> alerts;
restConnector connect;
public Application() {
initComponents();
}
private void initComponents() {
connector = restConnector.getInstance();
connector.init();
alerts = connector.getAlerts();
refreshData();
}
private void refreshData() {
StringBuilder sb = new StringBuilder();
for (Alert alert : alerts) {
sb.append(alert.toString()).append("\n");
}
System.out.println("appalertList: "+ sb.toString());
}
}
Now you have access to the Alerts in the list.
But let me reiterate: THIS IS A HORRIBLE DESIGN. For one you are limiting the init method to one single call, in which it is only able to obtain one and only one resource. What if the rest service needs to access a different resource? You have made the request set in stone, so you cant.
Take some time to think of some good OOP designs where the class can be used for different scenarios.

Robospice + Retrofit + ORMLite

I'm using Robospice with Retrofit ans ORMLite modules. Retrofit part working good. I have City model for Retrofit:
City.java:
public class City {
public int city_id;
public String name;
#SuppressWarnings("serial")
public static class List extends ArrayList<City> {
}
}
I'm taking this model from server by GET-request:
MyApi.java
public interface MyAPI {
#GET("/cities")
City.List getCities();
}
This part works fine by calling this method:
getSpiceManager().execute(mRequestCity, "city", DurationInMillis.ONE_MINUTE, new ListCityRequestListener());
and listener:
public final class ListCityRequestListener implements RequestListener<City.List> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Toast.makeText(RegisterActivity.this, "failure", Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestSuccess(final City.List result) {
Toast.makeText(RegisterActivity.this, "success", Toast.LENGTH_SHORT).show();
updateCities(result);
}
}
At this time i want to download city list once from server and store this list into sqlitedb by ORMLite module. I've created ORMLite model:
City.java
#DatabaseTable(tableName = "city")
public class City {
public final static String DB_CITY_ID_FIELD_NAME = "id";
public final static String DB_CITY_NAME_FIELD_NAME = "name";
#DatabaseField(canBeNull = false, dataType = DataType.INTEGER, columnName = DB_CITY_ID_FIELD_NAME)
int id;
#DatabaseField(canBeNull = false, dataType = DataType.STRING, columnName = DB_CITY_NAME_FIELD_NAME)
private String name;
public City() {
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("id = ").append(id);
sb.append(", ").append("name = ").append(name);
return sb.toString();
}
}
My RetrofitSpiceService.java looks like this:
public class RetrofitSpiceService extends RetrofitGsonSpiceService {
private final static String BASE_URL = "http://example.com/api/v1";
private final static UserFunctions userFunctions = new UserFunctions();
#Override
public CacheManager createCacheManager(Application application) throws CacheCreationException {
CacheManager cacheManager = new CacheManager();
List< Class< ? >> classCollection = new ArrayList< Class< ? >>();
// add persisted classes to class collection
classCollection.add( City.class );
// init
RoboSpiceDatabaseHelper databaseHelper = new RoboSpiceDatabaseHelper( application, "sample_database.db", 1 );
InDatabaseObjectPersisterFactory inDatabaseObjectPersisterFactory = new InDatabaseObjectPersisterFactory( application, databaseHelper, classCollection );
cacheManager.addPersister( inDatabaseObjectPersisterFactory );
return cacheManager;
}
#Override
protected Builder createRestAdapterBuilder() {
Builder mBuilder = super.createRestAdapterBuilder();
mBuilder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
if (userFunctions.isUserLoggedIn()) {
request.addHeader("Authorization", userFunctions.getToken());
}
}
});
return mBuilder;
}
#Override
public void onCreate() {
super.onCreate();
addRetrofitInterface(MyAPI.class);
}
#Override
protected String getServerUrl() {
return BASE_URL;
}
}
I can't understand how can i store and read data from my City database? How do i need to change RetrofitSpiceService? I want download data by Retrofit and store it to database by ORMLite. My CacheManager is correct, i.e. will work properly? Maybe I misunderstand how the module Robospice-ORMLite works?
Thanks a lot!
When you make execute() call with cache key and duration Robospice will store your response into database.
getSpiceManager().execute(mRequestCity, "city", DurationInMillis.ONE_MINUTE, new ListCityRequestListener());
All following requests during one minute will get data from this cache, and then it makes network call. If you want to get data only from cache take a look on getSpiceManager().getFromCache() method. I think it's what you are looking for.

Categories

Resources