20 October 2017

Spring Boot Exception Handling





Long time I couldn't have time to write in my blog. So this time I am going to talk about spring boot exception handling with error code mapping. This blog post helpful to who are new to spring boot. The first thing is define error codes.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum ApiError {
    
    INVALID_REQUEST_PARAMETERS("E10001", "Invalid request parameters"),

    PRODUCT_NOT_FOUND("E1001", "Product not found");

    private final String errorCode;
    private final String errorMessage;

    ApiError(String errorCode, String errorMessage) {
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }
}


The next task is write  runtime exception type based on HTTP status.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import io.apptizer.api.errors.codes.ApiError;

public class ResourceNotFoundException extends RuntimeException {

    private ApiError apiErrors;

    public ResourceNotFoundException(ApiError apiErrors, String message) {
        super(message);
        this.apiErrors = apiErrors;
    }

    public ApiErrors getApiErrors() {
        return apiErrors;
    }

    public void setApiErrors(ApiErrors apiErrors) {
        this.apiErrors = apiErrors;
    }
}

And final task is write exception mapper handler.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ExceptionMapperHandler {
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorInfo> resourceNotFound(ResourceNotFoundException ex) {
        ErrorInfo errorInfo = new ErrorInfo(ex.getApiErrors().getErrorCode(), ex.getMessage());
        return new ResponseEntity<>(errorInfo, HttpStatus.NOT_FOUND);
    }
}

then in your rest service you can throw exception with error code mapping.

throw new ResourceNotFoundException(ApiError.PRODUCT_NOT_FOUND, "Product Task Not Found");

And that is it.If you have any better way please put your comments.

12 August 2017

Android ADB Screenshot Automation



In this blog post  I will describe how to take screenshot from emulator or device in android. This commands only works for single adb device.I have write some shell script to take screenshots. Anyway android studio have feature to take screenshot .But if your not going with studio this might help.Hope this will help resolve your problems.

#!/bin/bash
adb shell am start -n juwelary.innova.dev.juwelarytemplate/juwelary.innova.dev.juwelarytemplate.MainActivity
sleep 5
name=screenshot.png
echo "Start to take screenshot"
adb shell screencap -p /sdcard/$name
adb pull /sdcard/$name
adb shell rm /sdcard/$name
curr_dir=pwd
echo "save to `pwd`/$name"

09 April 2017

Android Client Benchmark AsyncTask & Retrofit & OkHttp

Hi guys , After long time. Last couple of months I worked in android development so I thought share something i have discovered . This is a open thread so if you have any other opinions you can post here. What I am going to do is comparison between android client libraries. Here  I am going to do some benchmark on

  1. Retrofit  
  2. OkHttp 
  3. HttpURLConnection 


The main thing is developers wonder which library is fit for their android application. Here I will post the code examples and results. And one more thing here I am not taking volley because it is old library now.If you need to access code this is the url [https://github.com/sajith4u/android-client-benchmark ]. Feel free to commit your changes.

Code Samples

Retrofit

public interface RetrofitClient {

    @GET("benchmark/")
    Call<Example> getBenchmarkResults();
}


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package app.innova.dev.cleintbenchmark.beans;


import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Example {

    @SerializedName("text")
    @Expose
    private String text;
    @SerializedName("to_user_id")
    @Expose
    private int toUserId;
    @SerializedName("to_user")
    @Expose
    private String toUser;
    @SerializedName("from_user")
    @Expose
    private String fromUser;
    @SerializedName("result_type")
    @Expose
    private String resultType;
    @SerializedName("recent_retweets")
    @Expose
    private int recentRetweets;
    @SerializedName("id")
    @Expose
    private int id;
    @SerializedName("from_user_id")
    @Expose
    private int fromUserId;
    @SerializedName("iso_language_code")
    @Expose
    private String isoLanguageCode;
    @SerializedName("source")
    @Expose
    private String source;
    @SerializedName("profile_image_url")
    @Expose
    private String profileImageUrl;
    @SerializedName("created_at")
    @Expose
    private String createdAt;
    @SerializedName("since_id")
    @Expose
    private int sinceId;
    @SerializedName("max_id")
    @Expose
    private int maxId;
    @SerializedName("refresh_url")
    @Expose
    private String refreshUrl;
    @SerializedName("results_per_page")
    @Expose
    private int resultsPerPage;
    @SerializedName("next_page")
    @Expose
    private String nextPage;
    @SerializedName("completed_in")
    @Expose
    private float completedIn;
    @SerializedName("page")
    @Expose
    private int page;
    @SerializedName("query")
    @Expose
    private String query;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public int getToUserId() {
        return toUserId;
    }

    public void setToUserId(int toUserId) {
        this.toUserId = toUserId;
    }

    public String getToUser() {
        return toUser;
    }

    public void setToUser(String toUser) {
        this.toUser = toUser;
    }

    public String getFromUser() {
        return fromUser;
    }

    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }

    public int getRecentRetweets() {
        return recentRetweets;
    }

    public void setRecentRetweets(int recentRetweets) {
        this.recentRetweets = recentRetweets;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getFromUserId() {
        return fromUserId;
    }

    public void setFromUserId(int fromUserId) {
        this.fromUserId = fromUserId;
    }

    public String getIsoLanguageCode() {
        return isoLanguageCode;
    }

    public void setIsoLanguageCode(String isoLanguageCode) {
        this.isoLanguageCode = isoLanguageCode;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getProfileImageUrl() {
        return profileImageUrl;
    }

    public void setProfileImageUrl(String profileImageUrl) {
        this.profileImageUrl = profileImageUrl;
    }

    public String getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(String createdAt) {
        this.createdAt = createdAt;
    }

    public int getSinceId() {
        return sinceId;
    }

    public void setSinceId(int sinceId) {
        this.sinceId = sinceId;
    }

    public int getMaxId() {
        return maxId;
    }

    public void setMaxId(int maxId) {
        this.maxId = maxId;
    }

    public String getRefreshUrl() {
        return refreshUrl;
    }

    public void setRefreshUrl(String refreshUrl) {
        this.refreshUrl = refreshUrl;
    }

    public int getResultsPerPage() {
        return resultsPerPage;
    }

    public void setResultsPerPage(int resultsPerPage) {
        this.resultsPerPage = resultsPerPage;
    }

    public String getNextPage() {
        return nextPage;
    }

    public void setNextPage(String nextPage) {
        this.nextPage = nextPage;
    }

    public float getCompletedIn() {
        return completedIn;
    }

    public void setCompletedIn(float completedIn) {
        this.completedIn = completedIn;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    public String toString() {
        return "Example{" +
                "text='" + text + '\'' +
                ", toUserId=" + toUserId +
                ", toUser='" + toUser + '\'' +
                ", fromUser='" + fromUser + '\'' +
                ", resultType='" + resultType + '\'' +
                ", recentRetweets=" + recentRetweets +
                ", id=" + id +
                ", fromUserId=" + fromUserId +
                ", isoLanguageCode='" + isoLanguageCode + '\'' +
                ", source='" + source + '\'' +
                ", profileImageUrl='" + profileImageUrl + '\'' +
                ", createdAt='" + createdAt + '\'' +
                ", sinceId=" + sinceId +
                ", maxId=" + maxId +
                ", refreshUrl='" + refreshUrl + '\'' +
                ", resultsPerPage=" + resultsPerPage +
                ", nextPage='" + nextPage + '\'' +
                ", completedIn=" + completedIn +
                ", page=" + page +
                ", query='" + query + '\'' +
                '}';
    }
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
final RetrofitClient apiService =
                RestClient.getClient().create(RetrofitClient.class);
        retrofit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String format = sdf.format(new Date());
                Log.d(TAG, "Date : " + format);
                Call<Example> call = apiService.getBenchmarkResults();
                call.enqueue(new Callback<Example>() {
                    @Override
                    public void onResponse(Call<Example> call, Response<Example> response) {
                        Log.d(TAG, String.valueOf(response.code()));
                        Example movies = response.body();
                        if (movies != null) {
                            String format = sdf.format(new Date());
                            Log.d(TAG, "Date : " + format);
                            System.out.println("Data :" + movies.toString());
                            Log.d(TAG, "Data :" + movies.toString());
                        }

                    }

                    @Override
                    public void onFailure(Call<Example> call, Throwable t) {
                        Log.e(TAG, t.toString());
                    }
                });
            }
        });

Android AsyncTask



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class AsyncClient {

    private String MOCK_URL = "http://demo7836701.mockable.io/benchmark";

    private static String GET = "GET";

    private static int CONNECTION_TIMEOUT = 75000;

    private static int READ_TIMEOUT = 60000;

    public Object getObject(Class successClass) throws Exception {
        Object getResponse;
        StringBuilder responseString = new StringBuilder();
        String fullUrl = (MOCK_URL).replace(" ", "%20");
        URL requestUrl = new URL(fullUrl);
        HttpURLConnection conn = getConnection(GET, requestUrl);
        conn.connect();
        InputStream connectionInputStream = getConnectionInputStream(conn);
        BufferedReader reader = new BufferedReader(new InputStreamReader(connectionInputStream));
        String line;
        while ((line = reader.readLine()) != null) {
            responseString.append(line);
        }
        Log.d("GET Response >>", responseString.toString());
        if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 299) {
            getResponse = new Gson().fromJson(responseString.toString(), successClass);
        } else {
            getResponse = new Gson().fromJson(responseString.toString(), String.class);
        }
        conn.disconnect();
        return getResponse;
    }

    private HttpURLConnection getConnection(String type, URL requestUrl) throws Exception {
        HttpURLConnection conn = (HttpURLConnection) requestUrl.openConnection();
        conn.setDoOutput(false);
        conn.setRequestMethod(type);
        conn.setReadTimeout(READ_TIMEOUT);
        conn.setConnectTimeout(CONNECTION_TIMEOUT);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
        conn.setRequestProperty("accept", "application/json");
        return conn;
    }

    private InputStream getConnectionInputStream(HttpURLConnection httpURLConnection) throws Exception {
        InputStream in;
        if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() <= 299) {
            in = new BufferedInputStream(httpURLConnection.getInputStream());
        } else {
            in = new BufferedInputStream(httpURLConnection.getErrorStream());
        }
        return in;
    }
}

Benchmark Results

This is first step to do the benchmark. Before doing this benchmark There are assumptions I have made.First one is both scenarios  same internet connection in the device.

To Complete One Request

Retrofit 2017-04-09 04:38:22.285 2017-04-09 04:38:22.731 [ 446 mili seconds]
Android 2017-04-09 04:39:19.321 2017-04-09 04:39:19.648 [ 321 mili seconds ]


04 February 2017

Spring Security with Google SignIn



It is long time again. This time I think write some article about Spring security  with Google Signing .Last week I have worked project which need to integrate spring security with google sign in. So I think this would help developers who using spring security. This diagram shows  the use case I am going to resolve.




First step is to Add Google login button to web site. For that this documentation shows how to add google login button to website. If you follow up this steps you can add this kind of button to your website.


After you sign-in using gmail account it will return google auth token to browser.


In your sign.jsp page you need to add  web form with hidden  token field.
 

1
2
3
4
<form id="custom_form"
                  action="<c:url value='j_spring_security_check' />" method='POST'>
                <input id="formToken" type='hidden' name='token'/>
 </form>

Next spring-security.xml should be configured this way.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<sec:http auto-config="true" use-expressions="true">
        <sec:intercept-url pattern="/login" access="isAnonymous()" />
        <sec:intercept-url pattern="/logout" access="isAnonymous()" />
        <sec:intercept-url pattern="/access_denied" access="isAnonymous()" />
        <sec:intercept-url pattern="/*" access="hasRole('ROLE_ADMIN')" />

        <sec:form-login login-page="/login" authentication-failure-url="/access_denied"
                        password-parameter="token"
                        default-target-url="/home"/>

        <sec:logout logout-success-url="/logout"/>

</sec:http>

<sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref="authenticationProvider">
        </sec:authentication-provider>
</sec:authentication-manager>


<bean class="dev.innova.util.CustomAuthenticationProvider" id="authenticationProvider"/>


Then the last part is create custom Authentication Provider to validate user google auth token.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import dev.innova.util.UserAccount;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Value("${client.id}")
    private String CLIENT_ID;

    private static final Logger logger = LogManager.getLogger(CustomAuthenticationProvider.class.getName());

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String token = (String) authentication.getCredentials();
        NetHttpTransport transport = new NetHttpTransport();
        JsonFactory mJFactory = new GsonFactory();
        boolean validUser = false;
        GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, mJFactory)
                .setAudience(Collections.singletonList(CLIENT_ID))
                .build();

        UserAccount userAccount = new UserAccount();
        try {
            GoogleIdToken idToken = verifier.verify(token);
            if (idToken != null) {
                GoogleIdToken.Payload payload = idToken.getPayload();
                String email = payload.getEmail();
                String pictureUrl = (String) payload.get("picture");
                String userId = payload.getSubject();
                boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
                String name = (String) payload.get("name");
                userAccount.setEmailAddress(email);
                userAccount.setImageUrl(pictureUrl);
                validUser = isValidUser(email);
                logger.info("User Details Name [{}]",name);
                logger.info("User Details userId [{}]",userId);
                logger.info("User Details emailVerified [{}]",emailVerified);
                logger.info("User Details Image [{}]",pictureUrl);
            } else {
                logger.info("Invalid ID token.");
            }
        }catch (IllegalArgumentException exx){
            logger.error("Failed {}", exx.getMessage());
        }catch (GeneralSecurityException e) {
            logger.error("Failed {}", e.getMessage());
        } catch (IOException e) {
            logger.error("Failed {}", e.getMessage());
        }
        if (validUser) {
            logger.info("Valid User Found");
            List<GrantedAuthority> grantedAuths = new ArrayList<>();
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            Authentication auth = new UsernamePasswordAuthenticationToken(userAccount, token, grantedAuths);
            return auth;
        } else {
            throw new BadCredentialsException("Username not found.");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        boolean equals = authentication.equals(UsernamePasswordAuthenticationToken.class);
        return equals;
    }

    /**
     *  Mock Method to validate Email
     * @param email
     * @return
     */
    private boolean isValidUser(String email){
        return true;
    }

}

Then we can create Custom UserDetails to save custom data. Which will help you to save user image, extra details.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

public class UserAccount implements UserDetails {

    private String userId;

    private String userName;

    private String emailAddress;

    private String imageUrl;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return userName;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }
}

And that is it.You  can login user with google account and use spring security to secure.