Spring Rest API and Mongodb with GridFS

Reference: https://github.com/vishwakarmarhl

RDataServer

Hello draft of the Mongo DB based file data store service

GIT USAGE

There are two more branches apart from the master branch which have the file upload functionality

  1. Checkout the branch
  2. Pull the current branch
    git pull
  3. After making changes in any of them index and commit
    git add .
    git commit -m “Updated the code and added a message”
  4. Push changes to the github repository
    git push origin FileUploadGridFSSpring
    git push origin MultiPartFileUpload

SETUP & START MONGO DB WIN x64

Download : http://www.mongodb.org/dr/downloads.mongodb.org/win32/mongodb-win32-x86_64-2.4.5.zip/download

  1. Unzip the zip contents in C:\mongodb\
  2. create C:\data\db
  3. Execute the C:\mongodb\bin\mongod.exe –dbpath C:\data\db

CREATE SAMPLE DATA

//The following command simply pre-allocates a 2 gigabyte, uncapped collection named people. db.createCollection(“files”, { size: 2147483648 }) db.files.save( { fileId: ‘1235’, fileName: ‘V_XXX.EXE’, filePath: ‘/opt/storage/rhldata’, fileSizeInKB: 123342, fileExtensionType: ‘EXE’ })

PROJECT USAGE

  1. Start the mongod.exe standalone server
  2. Import the source as a maven project in Eclipse STS IDE
  3. MVN PACKAGE
  4. Deploy on a tomcat instance to see the data from mongodb
  5. An alternate plugin for tomcat enables maven based initialization. mvn tomcat7:run
  6. Open up http://localhost:8088/RDataServe to checkout the grid for file data

APPENDIX/REFERENCE

Mongo Shell Cmds

show dbs show collections

//This command creates a collection named file with a maximum size of 5 megabytes and a maximum of 5000 documents. db.createCollection(“files”, { capped : true, size : 5242880, max : 5000 } )

//The following command simply pre-allocates a 2 gigabyte, uncapped collection named people. db.createCollection(“files”, { size: 2147483648 })

//Drop a collection capped db.files.drop()

//Insert db.files.insert( { _id: 1, fileId: 1234, fileName: ‘R_XXX.EXE’, filePath: ‘/opt/storage/rhldata’, fileSizeInKB: 123412, fileExtensionType: ‘EXE’ })

db.files.save( { fileId: ‘1235’, fileName: ‘V_XXX.EXE’, filePath: ‘/opt/storage/rhldata’, fileSizeInKB: 123342, fileExtensionType: ‘EXE’ })

//Query db.files.find({fileId:1234})

AspectJ component for my services audit logger

This is a blog that will leverage the advantages of AspectJ aspect oriented programming concept in solving a very basic problem of auditing the visitors to your service and the response times. These parameters are very important when we want to do some operations around these.

Pre-requisite for AspectJ in your pom.xml

 <!-- Spring AspectJ -->
 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${org.springframework-version}</version>
    <scope>compile</scope>
 </dependency>
 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${org.aspectj-version}</version>
    <scope>compile</scope>
 </dependency>
 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${org.aspectj-version}</version>
 </dependency>
 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${org.springframework-version}</version>
 </dependency>
 <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
 </dependency>

In your application context add the configuration as per the standard aop proxy. You should also include component-scan and annotation-driven.

<!-- Aspects -->
 <aop:aspectj-autoproxy proxy-target-class="true"/>

Here is the actual code that will intercept around the public calls  in controller. It will print the service url, method, parameters and arguments. We can also obtain the user from the session context.

/**
 * @author Rahul Vishwakarma
 * This class will log all the service calls made to the Generic Application 
 * relying on the @Around advice
 * Ref: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies 
 */
@Component
@Aspect
public class GenericLoggerAspect {
 /** Logger for this class and subclasses */
 private static final Logger log = LoggerFactory.getLogger(RhlLoggerAspect.class);
 
 public static ConcurrentHashMap<String,RequestData> responseTime = new ConcurrentHashMap<String,RequestData>();
 
 @Autowired 
 Utility utility;
/**
 * Inner class for request info
 * @author Rahul
 *
 */
 public class RequestInfo{
 public int responseTimeMills = 0;
 public Date accessTime = null;
 public String urlPath;
 public String requestType;
 public String args;
 }
 /**
 * Inner class for Data capturing request information
 * @author Rahul
 *
 */
 public class RequestData {
   public RequestInfo requestInfo;
   public String api;
   public UserInfo userInfo;
 
   public RequestData(RequestInfo requestInfo,UserInfo userInfo,String methodSignature){
   this.requestInfo = requestInfo;
   this.userInfo = userInfo;
   this.api = methodSignature;
   }
 }

/**
 * User and client related info
 * @author Rahul
 *
 */
 public class UserInfo{
   public String userName;
   public int userId;
   public String sessionId;
   public String role;
   public String clientIp;
 }
 
 private String getRepresentation(Object [] params){
   StringBuilder sb = new StringBuilder();
   if(params!=null)
   {
   String value = null;
   for(int i=0;i<params.length;i++){
   value = params[i] + ",";
   if(value.contains("@")){
     value = "";
   }
     sb.append(value);
   }
    if(sb.length()>0)
    return sb.substring(0, sb.length() - 1);
  }
  return sb.toString();
}
enum IssueType{
  ISSUE_URL_SUFFIX, OTHER, NONE
}
@Around("execution(@*..RequestMapping * * (..))")
public Object log_around(ProceedingJoinPoint pjp) throws Throwable {
 
  Object obj = null;
  IssueType issueType = IssueType.NONE;
  String error="Error in GenericLoggerAspect";
 try{
  String methodSignature = pjp.getSignature()+"";
  StringBuffer args = new StringBuffer(); //getRepresentation(pjp.getArgs());
 
  //Append arguments
  Object[] arg = pjp.getArgs();
  for (int i = 0; i < arg.length; i++) {
    args.append(arg[i]).append(",");
  }
  if (arg.length > 0) {
   args.deleteCharAt(args.length() - 1);
  }
 
  log.info("\tSTART {}-{}", methodSignature+" ["+Thread.currentThread().getId()+"]",args.toString());

  UserInfo userInfo = new UserInfo();
  UserSecure userSecure = utility.getUserInfo();
  if(userSecure != null){
   userInfo.clientIp = userSecure.getClientIp();
   userInfo.userId = userSecure.getUserId();
   userInfo.sessionId = userSecure.getSessionId();
   userInfo.userName = userSecure.getUserName();
  if(userSecure.getAuthorities() != null)
   userInfo.role = userSecure.getAuthorities().toString();
  }
  ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
 
  String urlPath=""; 
 
  if(sra!=null){
    HttpServletRequest req = sra.getRequest();
  if(req!=null){
    urlPath = req.getServletPath();
  } 
 }
 
 long start = System.currentTimeMillis();
 if(urlPath.endsWith("/")){
   issueType = IssueType.ISSUE_URL_SUFFIX;
   obj = null;
 } else
   obj = pjp.proceed();
 
 String requestType = "ajax";
 if(obj!=null && (obj instanceof ModelAndView)){
   requestType = "page";
 }
 int elapsedTime = (int) (System.currentTimeMillis() - start); 
 RequestInfo requestInfo = new RequestInfo();
 requestInfo.accessTime = (new Date());
 requestInfo.requestType = (requestType);
 requestInfo.responseTimeMills = (elapsedTime);
 requestInfo.urlPath = (urlPath);
 requestInfo.args = (args.toString());
 
 RequestData requestData = new RequestData(requestInfo, userInfo,methodSignature);
 log.info(userInfo.sessionId + " REQ {} by "+userInfo.userName +"@"+userInfo.clientIp+", time {} mills; args ["+args.toString()+"]", urlPath + " ["+ Thread.currentThread().getId() +"]" , elapsedTime);
 responseTime.put(methodSignature, requestData);
 //addUserAuditLog(requestData);
 
 }catch(Exception ex){
 issueType = IssueType.OTHER;
 error = ex.getMessage();
 log.error(ex.getMessage());
}
 
if(obj==null){
 switch(issueType){
 case ISSUE_URL_SUFFIX:throw new GenericException("URL ends with trailling /"); 
 case OTHER:throw new GenericException(error);
 default:
 break;
 } 
}
return obj;
}
/**
 * @param exceptions
 */
@AfterThrowing(pointcut="execution(public * com.generic.controller.*.*(..))",throwing="ex") 
 public void MethodError(Exception ex){ 
   log.error("@Exception {}", ex.toString()); 
 } 
 
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {
   log.info("Testing the public Execution call");
 }
}

When we run the system we expect the following response in the log which does the @Around joint point.

Before:

17-Jul-2014 03:25:35,067-INFO – GenericLoggerAspect:130 –       START List com.humesis.generic.controller.UserController.getUsers(HttpServletResponse)

After:

17-Jul-2014 03:25:35,106-INFO – GenericLoggerAspect:176 – 674B8F114926E5A3BB143E7126D828C7 REQ /users [28] by rahul@0:0:0:0:0:0:0:1, time 36 mills; args [HttpSessionSecurityContextRepository]

This data can Asynchronously be audited or logged for generating the access pattern or hotspots in the service access.

 

OAuth 2.0 based Facebook authentication. Spring framework Java authentication client

This tutorial will cover how to obtain user data from the facebook secure server and use that to authenticate a user based on the credentials obtained.

Image

First Step is to prepare a Facebook App that will allow for this authentication:

1. Go to Apps and Create a new app
2. Add the display name and namespace
3. Obtain the APP_ID and SECRET_KEY for this app
4. Setup the website that will authenticate through this app
Go to settings and select Add Platform in the Basic settings configuration add website URL(localhost:8080) for your test.

The facebook app is ready for integration with your web service. I have a spring service which is called by a controller call which is described below:

 

/**
* @author Rahul Vishwakarma
*
* This service allows the authentication with the facebook authorization and authentication server
* An access token is obtained from the facebook server which is used to access the users data
*
*/
@Service
public class OAuthFacebookService {
/** Logger for this class and subclasses */
private static final Logger log = LoggerFactory.getLogger(OAuthSocialService.class);
@Autowired
private ConfigurationManager configurationManager;

/**TODO: Move all these to configuration and access through COnfiguration Manager**/
private static final String[] SCOPE = new String[] {"publish_stream",
"email",
"user_about_me",
"offline_access",
"user_birthday",
"read_friendlists"};

private static final String REDIRECT_URI = "http://localhost:8080/GenesisService/register/social/facebook/callback";
private static final String CLIENT_ID = "1428804717404113";
private static final String APP_SECRET = "b979e221dd8a778df420644c77c9ea00";
private static final String DIALOG_OAUTH = "https://graph.facebook.com/oauth/authorize";
private static final String ACCESS_TOKEN = "https://graph.facebook.com/oauth/access_token";

public String getLoginRedirectURL(String type) {
String url = DIALOG_OAUTH+"?client_id=" +
CLIENT_ID + "&display=page&redirect_uri=" +
REDIRECT_URI+"&scope="+StringUtils.arrayToCommaDelimitedString(SCOPE);
log.info("URL:"+url);
return url;
}
public String getAuthURL(String authCode) {
String url = ACCESS_TOKEN+"?client_id=" +
CLIENT_ID+"&redirect_uri=" +
REDIRECT_URI+"&client_secret="+APP_SECRET+"&code="+authCode;
log.info("URL:"+url);
return url;
}

public String getFacebookAccessToken(String code){
String token = null;
if (code != null && ! "".equals(code)) {
String newUrl = getAuthURL(code);
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
HttpGet httpget = new HttpGet(newUrl);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpget, responseHandler);
log.info("ResponseBody: "+responseBody);
token = org.apache.commons.lang.StringUtils.removeEnd
(org.apache.commons.lang.StringUtils.removeStart(responseBody, "access_token=") ,"&expires=5180795");
log.info("Token: "+token);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
httpclient.getConnectionManager().shutdown();
}
}
return token;
}

public String getUserData(String accessToken) {
String email = null;
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
if (accessToken != null && ! "".equals(accessToken)) {
String newUrl = "https://graph.facebook.com/me?access_token=" + accessToken;
httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(newUrl);
System.out.println("Get info from face --> executing request: "
+ httpget.getURI());
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpget, responseHandler);
log.info("Obtained response from FB: "+responseBody);
ObjectMapper mapper = new ObjectMapper();
FBUserSocialData fb = mapper.readValue(responseBody, FBUserSocialData.class);
log.info("Facebook Data: "+fb.toString());
/**
* Do anythign with this user data
* 1. Create the user details data and register this user
* 2. Create a session for this user and allow for authenticated login to the system
*/

} else {
System.err.println("Token for facebook is null");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
httpclient.getConnectionManager().shutdown();
}
return email;
}
}

This service will be accessed by two sets of REST controller calls.

/facebook/signin  –  call will connect with Facebook and ask you to enter the credentials for authentication. A successful authentication generates a code contained in the URL

/facebook/callback – This service will take the code and obtain and access token. This access token is used to retrieve the final user data.

@RequestMapping(value = "/register/social/facebook/signin", method = RequestMethod.GET)
public void signin(HttpServletRequest request, HttpServletResponse response)
throws Exception {
try {
response.sendRedirect(oAuthSocialService.getLoginRedirectURL(""));
} catch (Exception e) {
e.printStackTrace();
}
}
@RequestMapping(value = "/register/social/facebook/callback", params = "code", method = RequestMethod.GET)
@ResponseBody
public void accessCode(@RequestParam("code") String code, HttpServletRequest request,
HttpServletResponse response) throws Exception {
try {
//Obtain an access token
String accessToken = oAuthSocialService.getAccessToken(code);
//Access the user data based on this token
oAuthSocialService.getUserData(accessToken);
} catch (Exception e) {
e.printStackTrace();
}
}

 

This is all you need to setup a basic authentication with the Facebook server. In order to use this in live environment we will have to make the facebook app public after submitting for review by facebook.

A sample response data procured by using this authentication is as below:

22-Jun-2014 02:30:00,435-INFO - OAuthSocialService:110 - 
Facebook Data: FBUserSocialData [id=268491463338606, birthday=66/66/6666, email=rahul_32346@dohoo.com, first_name=Rahul, gender=male,last_name=Vishwakarma,
link=https://www.facebook.com/app_scoped_user_id/268491463338606/, locale=en_US,
name=Rahul Vishwakarma, timezone=5.5, updated_time=2014-06-21T08:39:39+0000, verified=true]

 

 

Going International with Spring i18n Internationalization or say Localization

This will cover the springframework internationalization feature in order to be able to show various messages using the language resource bundle file.

I had taken help from MKYONG blog and packaged a good working example to demonstrate it. This app even transitions pages and shows  that locale is maintained even when we navigate away from the page where locale was modified.

The WebTestJEEi18n project covers most of these configuration comprehensively.

Link to the GITHUB project: https://github.com/vishwakarmarhl/SpringInternational.git

Will be adding more on springs technology progressively.