With my latest assignment I have started exploring Hadoop and related technologies. When exploring HDFS and playing with it, I came across these two syntaxes of querying HDFS:

> hadoop dfs
> hadoop fs

Initally could not differentiate between the two and keep wondering why we have two different syntaxes for a common purpose. I googled the web and found people too having the same question and below are there reasonings:

Per Chris explanation looks like there's no difference between the two syntaxes. If we look at the definitions of the two commands (hadoop fs and hadoop dfs) in $HADOOP_HOME/bin/hadoop
...
elif [ "$COMMAND" = "datanode" ] ; then
  CLASS='org.apache.hadoop.hdfs.server.datanode.DataNode'
  HADOOP_OPTS="$HADOOP_OPTS $HADOOP_DATANODE_OPTS"
elif [ "$COMMAND" = "fs" ] ; then
  CLASS=org.apache.hadoop.fs.FsShell
  HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
elif [ "$COMMAND" = "dfs" ] ; then
  CLASS=org.apache.hadoop.fs.FsShell
  HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
elif [ "$COMMAND" = "dfsadmin" ] ; then
  CLASS=org.apache.hadoop.hdfs.tools.DFSAdmin
  HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
...
that's his reasoning behind the difference.

I am not convinced with this, I looked out for a more convincing answer and here's are a few excerpts which made better sense to me:

FS relates to a generic file system which can point to any file systems like local, HDFS etc. But dfs is very specific to HDFS. So when we use FS it can perform operation with from/to local or hadoop distributed file system to destination . But specifying DFS operation relates to HDFS.

Below are the excerpts from hadoop documentation which describes these two as different shells.


FS Shell
The FileSystem (FS) shell is invoked by bin/hadoop fs . All the FS shell commands take path URIs as arguments. The URI format is scheme://autority/path. For HDFS the scheme is hdfs, and for the local filesystem the scheme is file. The scheme and authority are optional. If not specified, the default scheme specified in the configuration is used. An HDFS file or directory such as /parent/child can be specified as hdfs://namenodehost/parent/child or simply as /parent/child (given that your configuration is set to point to hdfs://namenodehost). Most of the commands in FS shell behave like corresponding Unix commands. 

DFShell
The HDFS shell is invoked by bin/hadoop dfs . All the HDFS shell commands take path URIs as arguments. The URI format is scheme://autority/path. For HDFS the scheme is hdfs, and for the local filesystem the scheme is file. The scheme and authority are optional. If not specified, the default scheme specified in the configuration is used. An HDFS file or directory such as /parent/child can be specified as hdfs://namenode:namenodeport/parent/child or simply as /parent/child (given that your configuration is set to point to namenode:namenodeport). Most of the commands in HDFS shell behave like corresponding Unix commands. 

So from the above it can be concluded that it all depends upon the scheme configure. When using this two command with absolute URI, i.e. scheme://a/b the behavior shall be identical. Only its the default configured scheme value for file and hdfs for fs and dfs respectively which is the cause for difference in behavior.
0

Add a comment

  1. You might be seeking for the option to profile (capturing method's execution time) your spring application. Spring provides different ways to profile the application. Profiling should be treated as a separate concern and spring AOP facilitates easy approach to separate this concern. With the help of spring AOP you can profile your methods without making any changes in actual classes. 

    You just need to perform pretty simple steps to configure your application for profiling using spring AOP:


    • In application context file, add below tag to enable the load time weaving
             context:load-time-weaver      
             With this configuration, AspectJ's Load-time weaver is registered with current class loader. All classes
             (which are matched with the pattern in below aspect class) are weaved at load time as per defined 
             aspect class file.

    • Create the aspect class for profiling       
    d                   This aspect is written to capture method's execution time. You can configure your required  
                    classes (which need to be profiled) with @Around advice.

    package com.xxx.profiling.aspect;

    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;

    /**
     * Aspect class to profile the configured methods
     */
    @Component
    @Aspect
    public class ProfilingAspect {
       
        /** Logger instance. */
       
        private static final Logger LOGGER = Logger.getLogger(ProfilingAspect.class);
       
        public LoggingAspect(){
            
             LOGGER.info("Initializing Profiling Aspect... ");
        }
       
        public void profileMethods(ProceedingJoinPoint thisJoinPoint) throws Throwable {
          
            StringBuffer logMessage = new StringBuffer();       
            logMessage.append(thisJoinPoint.getSignature().getName());
            logMessage.append("(");
           
            // Collect arguments value
            Object[] args = thisJoinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                logMessage.append(args[i]).append(",");
            }
            if (args.length > 0) {
                logMessage.deleteCharAt(logMessage.length() - 1);
            }
           
            logMessage.append(") ");
           
            // Calculate method's elapsed time
            logMessage.append(" [Elapsed Time: " );
            long start = System.currentTimeMillis();
            thisJoinPoint.proceed();
            long end = System.currentTimeMillis();
            logMessage.append(end - start);
           
            logMessage.append(" ms]");
           
            LOGGER.debug("Profiling : "+thisJoinPoint.getTarget().getClass().getName() +"."+logMessage.toString());
        }
       
         // All public methods which are in services package will be weaved for profiling   
        @Around("execution(public * com.xxx.services..*.*(..))")
           public void profileServiceMethods(ProceedingJoinPoint thisJoinPoint) throws Throwable {
            profileMethods(thisJoinPoint);
        }
       
        // All public methods which are in dao package will be weaved for profiling
        @Around("execution(public * com.xxx.dao..*.*(..))")
        public void profileDAOMethods(ProceedingJoinPoint thisJoinPoint) throws Throwable {
            profileMethods(thisJoinPoint);
          
        }
    }

    • Scan package in application context file to initialize this aspect class
              
    • Download the spring-agent.jar file from here
    • Configure JAVA VM parameter by providing spring agent jar path 
              -javaagent:C:/yourpath/spring-agent-2.5.6.jar

    You are done with required configuration and implementation. Now it's time to see your execution statistics.

    Start your application server and verify whether aspect class is initialized successfully. You should get below log message in application log file. If this log message comes, that means your aspect is initialized properly.

    Initializing Profiling Aspect...

    Whenever your service or data access class methods are executed you must see below logs.


    Profiling :com.xxx.evolution.dao.MyDAO.getUserDetail(123)  [ Elapsed Time :  246 ms ]
    Profiling :com.xxx.evolution.services.MyService.getUserDetail(123)   [ Elapsed Time :  247 ms ]


    Apart from this approach you can also go for other options for profiling:

    0

    Add a comment

  2. Why Locking is required?
    When two concurrent users try to update database row simultaneously, there are absolute chances of losing data integrity. Locking comes in picture to avoid simultaneous updates and ensure data integrity.

    Types of Locking
    There are two types of locking, Optimistic and Pessimistic. In this post optimistic locking is described with example.
    • Optimistic Locking: This locking is applied on transaction commit. If entity (database row) is fetched by two users simultaneously, where first user updates and commits the row and second user tries to update row with an old version. Second user will get an exception as row is already updated and version is changed. A version number is associated for entity object to achieve optimistic locking. Whenever entity object is changed, its version number is automatically increased by one. If any transaction is performed on older version of entity, transaction is failed and application throws optimistic locking exception.
    • Pessimistic Locking: Pessimistic locking explicitly restricts shared resources until the transaction completed. To get more detail about pessimistic locking refer this link .  

    How to Implement Optimistic Locking in JPA 2.0?
    Let’s take an example; there are two Tables Company and Employee:

    Table: Company
    Company_Id
    Company_Name
    Address
    Survey_Sequnce
    Version
    1
    AirTel
    xyz
    110
    20
    2
    Reliance
    abc
    230
    40

    Table: Employee
    Employee_Id
    Employee_Name
    Company_Id
    Employee_Survey_No
    1
    Narendra
    1
    111
    2
    Vinay
    1
    112
    3
    Ranu
    2
    231
    4
    Rinku
    2
    232

    Whenever, employee joins the company, he is asked to complete the company specific survey and after completion of the survey, 'Employee_Survey_No' is generated based on SURVEY_SEQUENCE from company table. To generate the survey number below steps are performed:
    1.     Read the current sequence value specific to a company from company table   (SURVEY_SEQUENCE)
    2.       Increment the sequence count by 1
    3.       Update the incremented value in company table (SURVEY_SEQUENCE)
    4.       Use this incremented value to log into employee table as 'Employee_Survey_No'
    In this use case, there are absolute chances that two parallel surveys are performed for same company, where two employees get same survey sequence. This scenario can be handled with the help of optimistic locking in JPA to avoid sequence update collisions for same company.
    Let’s implement optimistic locking in JPA. JPA facilitates easy approach to handle optimistic locking.
    Define the company entity and annotate version column property with @Version to enable optimistic locking for entity.
    @Entity
    @Table(name = "Company", schema = "MyDB")
    public class Company {
        private Long companyId;
        private String companyName;
        private String address;
        private Long surveySequence;
        private Long version;
       
        @Id
        @SequenceGenerator(name = "company_seq", sequenceName = "MyDB.company_seq")
        @GeneratedValue(generator = "company_seq")
        @Column(name = "COMPANY_ID", unique = true, nullable = false)
        public Long getCompanyId() {   
            return companyId;
        }   
        public void setCompanyId(Long companyId) {   
            this.companyId = companyId;
        }   
        @Column(name = "COMPANY_NAME")
        public String getCompanyName() {   
            return companyName;
        }   
        public void setCompanyName(String companyName) {   
            this.companyName = companyName;
        }   
        @Column(name = "ADDRESS")
        public String getAddress() {
            return address;
        }   
        public void setAddress(String address) {   
            this.address = address;
        }   
        @Column(name = "SURVEY_SEQUENCE")
        public Long getSurveySequence() {   
            return surveySequence;
        }   
        public void setSurveySequence(Long surveySequence) {   
            this.surveySequence = surveySequence;
        }   
        @Column(name = "VERSION_ID")
        @Version
        public Long getVersion() {   
            return version;
        }   
        public void setVersion(Long version) {   
            this.version = version;
        }  
       
    }

    Now create the company DAO method to fetch the survey sequence, increment and update incremented value into company table. As we have annotated company table column with @Version, JPA will take care about parallel company entity updates. If any transaction is performed with older version of entity, transaction is failed and this method will throw ‘OptimisticLockingFailureException’.

    @Repository("companyDAO")
    public class CompanyDAO {

        public Long getNextSurveySequence(Long companyId) {
          
            //Fetch company entity based on companyId
            Company company = entityManager.findById(Company.class, companyId);
          
            // Get the current survey sequence
            Long surveySeq = company.getSurveySequence();
          
            // Increment current sequence by 1
            Long nextSurveySeq = surveySeq + 1L;

            // Set new sequence in company entity
            Company.setSurveySequence(nextSurveySeq)
           
            // Update company with new sequence
            entityManager.merge(company);

            return nextSurveySeq;
      }
    }

    Now create the service method to fetch the next survey sequence and handle optimistic locking exception. With maximum try count approach, you can retry data base operation for failed transactions.

    @Service("companyService")
    public class CompanyService {

       public Long getNextSequenceForOrgId(Long companyId) throws Exception {
       
        Long surveySequence = null;

       //If any transaction is performed on older version of entity,
       //transaction is failed and application throws the ‘OptimisticLockException’

        for (int maxTryCount = 1; maxTryCount <= 3; maxTryCount++)
        {
           try {
                 surveySequence = companyDao.getNextSurveySequence(companyId);

                 break;

           } catch (OptimisticLockingFailureException e) {
               
               if (maxTryCount == 3) {                       
                    throw new Exception(e);
                }
           }
         }
        return surveySequence;
       }
    }

    I hope this post helps you understanding and implementing optimistic locking with JPA. If you would like to share your thoughts on it, you are most welcome to comment.
    0

    Add a comment



  3. Liferay is one of the portal frameworks based on java. You can create portlets in Liferay using Spring MVC framework. The main purpose of this post is to provide the complete solution for free text search integration with Liferay Spring MVC portlets. If I say in short, this post may be the answer of below questions:
    • How to create a portlet in Liferay using Spring MVC
    • How to use Liferay free text search functionality in custom portlet
    Liferay uses Apache Lucene to provide the free text search functionality. Apache Lucene is a high-performance, full-featured text search engine library written entirely in Java.
    Example Portlets

    I have created below Spring MVC portlets in Liferay to understand that how we can use free text search functionality in our web site.
    1.       Add Search Content Portlet: This portlet adds the content which needs to be searched. For demo purpose, I have used three fields to add the search contents: ID, Title and Description. If you see in below screen shots, I have added three hotel names as search content in terms of ID (hotel id), Title (hotel name) and Description (about hotel).
    2.       Search Portlet: This portlet provides search field from where you can search the content
    which have been added using above portlet ‘Add Search Content Portlet’.  For example if you want
    to search keyword like ‘Kapila Resort’.  It will search this text in all added contents and will return 
    the result with matching score (Ratings).

    Source Code for Example Portlets

    This section provides source code for example portlets. To create the spring MVC portlet in Liferay you need to consider below points:
          Adding configuration in portlet.xml, liferay-display.xml and liferay-portlet.xml
          View Render Servlet configuration in web.xml
          Spring context configuration for View Resolver and Controller
          Creating Value Objects
          Creating Controller
          Creating JSP

    For more detail about creating Spring MVC portlets in Liferay please refer.
          To create ‘Add Search Content Portlet’, follow below steps:

    1.       Configure ‘applicationContext.xml’ file:

    xml version='1.0' encoding='UTF-8'?>
    <beans xmlns="http://www.springframework.org/schema/beans"
                xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:context="http://www.springframework.org/schema/context"
                xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
         <import resource="classpath:spring/applicationContext-core.xml" />
          <context:annotation-config />
          <context:component-scan base-package="your.package.mvc" />
                
                <bean id="messageSource"
                           class="org.springframework.context.support.ResourceBundleMessageSource">
                            <property name="basenames">
                                        <list>
                                                    <value>messages</value>
                                        </list>
                            </property>
                </bean>
                <bean id="viewResolver"
                            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                            <property name="viewClass"
                                        value="org.springframework.web.servlet.view.JstlView" />
                            <property name="prefix" value="/WEB-INF/jsp/" />
                            <property name="suffix" value=".jsp" />
                </bean>
             <bean id="parameterMappingInterceptor"      
    class="org.springframework.web.portlet.handler.ParameterMappingInterceptor"/>
                <bean id="portletMultipartResolver"class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver"/>
    </beans>

    2.       Add below configuration in ‘portlet.xml’ file:

    <portlet>
                <portlet-name>addSearchContent</portlet-name>
                <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
                <init-param>
                            <name>contextConfigLocation</name>
                            <value>/WEB-INF/context/addSearchContent.xml</value>
                </init-param>
                <supports>
                            <mime-type>text/html</mime-type>
                            <portlet-mode>view</portlet-mode>
                </supports>
                <resource-bundle>content.messages</resource-bundle>
                <portlet-info>
                            <title>Add Search Contents Portlet</title>
                </portlet-info>
    </portlet>

    3.       Add below configuration in ‘liferay-portlet.xml’ file:

    <portlet>
                <portlet-name>addSearchContent</portlet-name>
                <instanceable>true</instanceable>
    </portlet>    
                 

    4.       Add below configuration in ‘liferay-display.xml’ file:

    <category name="SearchPOC">
              <portlet id="addSearchContent"/>
    </category>            

    5.       Create ‘addSearchContent.xml’ file with below contents:

    xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
                xmlns:context="http://www.springframework.org/schema/context"
                xmlns:util="http://www.springframework.org/schema/util"
                xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-3.0.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
                                                              
    <bean class=
    "org.springframework.web.portlet.mvc.annotation
    .AnnotationMethodHandlerAdapter"/>
       
    <bean id="portletModeHandlerMapping"class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
                            <property name="portletModeMap">
                                        <map>
                                                    <entry key="view">
                                                    <ref bean="addSearchContentController" />
                                        </entry>
                                        </map>
                            </property>
        </bean>
    </beans>

    6.       Create value objects:

          SearchContentVO Object

    package your.package.mvc.vo;
    import java.util.List;
    /**
     * The Class SearchContentVO.
     *
     * @author nverma
     */
    public class SearchContentVO {
        /** The id. */
        private Long id;
        /** The title. */
        private String title;
        /** The description. */
        private String description;
        /** The search contents. */
        private List searchContents;
        /**
         * Gets the id.
         *
         * @return the id
         */
        public Long getId() {
            return id;
        }
        /**
         * Sets the id.
         *
         * @param id the new id
         */
        public void setId(Long id) {
            this.id = id;
        }
        /**
         * Gets the title.
         *
         * @return the title
         */
        public String getTitle() {
            return title;
        }
        /**
         * Sets the title.
         *
         * @param title the title to set
         */
        public void setTitle(String title) {
            this.title = title;
        }
        /**
         * Gets the description.
         *
         * @return the description
         */
        public String getDescription() {
            return description;
        }
        /**
         * Sets the description.
         *
         * @param description the description to set
         */
        public void setDescription(String description) {
            this.description = description;
        }
        /**
         * Gets the search contents.
         *
         * @return the search contents
         */                                                                                                          
        public List getSearchContents() {
            return searchContents;
        }
        /**
         * Sets the search contents.
         *
         * @param searchContents the new search contents
         */
        public void setSearchContents(List searchContents) {
            this.searchContents = searchContents;
        }
    }

          ‘SearchResult’ Object

    package your.package.mvc.vo;
    /**
     * The Class SearchResult.
     * @author nverma
     */
    public class SearchResult {
        /** The id. */
        private Long id;
        /** The title. */
        private String title;
        /** The description. */
        private String description;
        /** The search score. */
        private float searchScore;
        /**
         * Gets the id.
         * @return the id
         */
        public Long getId() {
            return id;
        }
        /**
         * Sets the id.
         * @param id the new id
         */
        public void setId(Long id) {
            this.id = id;
        }
        /**
         * Gets the title.
         * @return the title
         */
        public String getTitle() {
            return title;
        }
        /**
         * Sets the title.
         * @param title the title to set
         */
        public void setTitle(String title) {
            this.title = title;
        }
        /**
         * Gets the description.
         * @return the description
         */
        public String getDescription() {
            return description;
        }
        /**
         * Sets the description.
         * @param description the description to set
         */
        public void setDescription(String description) {
            this.description = description;
        }
        /**
         * Gets the search score.
         * @return the searchScore
         */
        public float getSearchScore() {
            return searchScore;
        }
        /**
         * Sets the search score.
         * @param searchScore the searchScore to set
         */
        public void setSearchScore(float searchScore) {
            this.searchScore = searchScore;
        }
    } 

    7.       Create controller ‘AddSearchContentController’:

    package your.package.mvc.controller;
    import java.util.ArrayList;
    import java.util.List;
    import javax.portlet.ActionRequest;
    import javax.portlet.ActionResponse;
    import javax.portlet.PortletRequest;
    import javax.portlet.RenderResponse;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.portlet.bind.annotation.ActionMapping;
    import org.springframework.web.portlet.bind.annotation.RenderMapping;
    import your.package.mvc.vo.SearchContentVO;
    import your.package.mvc.vo.SearchResult;
    import com.liferay.portal.kernel.search.BooleanQuery;
    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
    import com.liferay.portal.kernel.search.Document;
    import com.liferay.portal.kernel.search.DocumentImpl;
    import com.liferay.portal.kernel.search.Field;
    import com.liferay.portal.kernel.search.Hits;
    import com.liferay.portal.kernel.search.ParseException;
    import com.liferay.portal.kernel.search.SearchEngineUtil;
    import com.liferay.portal.kernel.search.SearchException;
    /**
     * Controller to add search contents.
     * @author nverma
     */
    @Controller("addSearchContentController")
    @RequestMapping(value = "VIEW")
    public class AddSearchContentController extends AbstractBaseController {
        /** The Constant LOG. */
        private static final Log LOG = LogFactory
                .getLog(AddSearchContentController.class);
        /** The Constant MODEL_ATT. */
        private static final String MODEL_ATT = "searchContentVO";
        /**
         * Inits the command object.
         *
         * @param portletRequest the portlet request
         * @return the search content vo
         */
        @ModelAttribute(MODEL_ATT)
        public SearchContentVO initCommandObject(final PortletRequest portletRequest) {
            LOG.debug("Initializing command object 'SearchContentVO'...");
            SearchContentVO searchContentVO = new SearchContentVO();
           searchContentVO.setSearchContents(allItems());
            return searchContentVO;
        }
        /**
         * Render add search content.
         *
         * @param renderResponse the render response
         * @return the string
         */
        @RenderMapping
        public String renderAddSearchContent(final RenderResponse renderResponse) {
            return "addSearchContent";
        }
        /**
         * Adds the search contents.
         *
         * @param searchContentVO the search content vo
         * @param actionRequest the action request
         * @param actionResponse the action response
         */
        @ActionMapping(params = "action=addSearchContent")
        public void addSearchContents(@ModelAttribute SearchContentVO searchContentVO,
                final ActionRequest actionRequest, ActionResponse actionResponse) {

            // Adding Search Document
            Document document = new DocumentImpl();
            document.addKeyword("Id", searchContentVO.getId());
            document.addText(Field.TITLE, searchContentVO.getTitle());
            document.addText("description", searchContentVO.getDescription());
            document.addText("AllDocuments""OnlyMyItems");
            try {
                LOG.info("Adding search content...");
                SearchEngineUtil.addDocument(10132, document);    
                LOG.info("Added search content successfully.");
            } catch (SearchException e) {

                LOG.error("Failed to add search document." +e.getMessage());
            }
            searchContentVO.setSearchContents(allItems());
         }
        /**
         * Delete search contents.
         *
         * @param searchContentVO the search content vo
         * @param actionRequest the action request
         * @param actionResponse the action response
         */
        @ActionMapping(params = "action=deleteSearchContent")
        public void deleteSearchContents(@ModelAttribute SearchContentVO searchContentVO,
                final ActionRequest actionRequest, ActionResponse actionResponse) {
            try {
                LOG.info("Deleting search content...["+searchContentVO.getId()+"]");
                SearchEngineUtil.deleteDocument(10132, String.valueOf(searchContentVO.getId()));
                LOG.info("Deleted search content successfully.");
            } catch (SearchException e) {
                LOG.error("Failed to deleted search document." +e.getMessage());
            }
            searchContentVO.setSearchContents(allItems());
        }
       /**
        * All items.
        *
        * @return the list
        */
       public  List allItems(){
           List searchResultLst = new ArrayList();
           Document[] resultDocs = null;
           Hits hits = null;
           BooleanQuery searchQuery = BooleanQueryFactoryUtil.create();
           try {
               searchQuery.addTerm("AllDocuments""OnlyMyItems");
               hits = SearchEngineUtil.search(10132, searchQuery, -1, -1);
               resultDocs = hits.getDocs();
           } catch (ParseException e) {
               LOG.error("Failed to parse while searching. " + e.getMessage());
               e.printStackTrace();
           } catch (SearchException e) {

               LOG.error("Failed to search. " + e.getMessage());
               e.printStackTrace();
           }
           for (int i = 0; i < resultDocs.length; i++) {
               LOG.debug("Searched Items: ");
               SearchResult searchResult = new SearchResult();
               LOG.debug("Document ->  " + hits.doc(i).get(Field.UID));
             String idStr = hits.doc(i).get("Id");
               if(idStr==null || idStr.isEmpty()){
                   continue;
               }
               Long  id = Long.parseLong(idStr);
               LOG.debug("Id ->  " + id);
               searchResult.setId(id);

               String title = hits.doc(i).get(Field.TITLE);
               if(title==null || title.isEmpty()){
                   continue;
               }
               LOG.debug("Title ->  " + title);
               searchResult.setTitle(title);
               String description = hits.doc(i).get("description");
               LOG.debug("Description ->  " + description);
               searchResult.setDescription(description);
               //Add search result into list
               searchResultLst.add(searchResult);
           }
           return searchResultLst;
       }
    }

    The API marked under arrow lines is responsible to fetch the search content from JSP and then add them into search content repository.

    8.       Create JSP with name ‘addSearchContent.jsp’:

    <%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0"%>
    <%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
    <%@ page contentType="text/html" isELIgnored="false"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

    <portlet:actionURL var="addSearchContent">
        <portlet:param name="action" value="addSearchContent" />
    </portlet:actionURL>
    <c:if test="${searchContentVO != null}">
             
    <table>
                <tr>
                            <td>
                                <h1>Add Search Contents in Search Repository</h1>
                            </td>
                </tr>
                <tr>
                  <td>
                <form:form id="search" commandName="searchContentVO" action="${addSearchContent}">
                            <table border=0>
                <tr>
                            <td width=80 align = "center"><b>ID</b></td>
                            <td width=80 align = "center"><b>Title</b></td>
                            <td width=80 align = "center"><b>Description</b></td>                                             </tr>
                <tr>
                <td width=80 align = "center"><form:input path="id" class="textfield disabledfield"/></td>
                <td width=80 align = "center"><form:input path="title" class="textfield disabledfield"/></td>
                <td width=80 align = "center"><form:input path="description" class="textfield disabledfield"/></td>                
                </tr>                                                                                        
                </table>
                            <br/>
                            <input type="submit" value="Add Search Contents"/>                        
                </form:form>
                </td>
                            <td width=150>
                            </td>
                            <td>
                            <form:form id="search2" commandName="searchContentVO" action="${addSearchContent}">
                                        <table border=0>
                                                    <tr>
                                                                <td width=80 align = "center"><b>ID</b></td>
                                                    </tr>
                                                   
                                                    <tr>
                            <td width=80 align = "center"><form:input path="id" class="textfield disabledfield"/></td>
                                        </tr>                            
                                                             
                            </table>
                                        <br/>
                                        <input type="submit" value="Delete Search Content"/>                     
                                        </form:form>
                                        </td>
                            </table>
                            <br/>
                            <br/>
                            <h1>Added Search Contents in Search Repository</h1>
                           
                            <table border=1>
                                                    <tr height=50>
                                                                <td width=80 align = "center"><b>ID</b></td>
                                                                <td width=250 align = "center"><b>Title</b></td>
                                                                <td width=480 align = "center"><b>Description</b></td>                                          
                                                    </tr>
                                        <c:forEach var="searchContent" items="${searchContentVO.searchContents}">
                           
                                                    <tr height=50>
                                                    <td width=80 align = "center">${searchContent.id}</td>
                                                    <td width=250 align = "center">${searchContent.title}</td>
                                                    <td width=480>${searchContent.description}</td>                    
                                                    </tr>
                    
                                        </c:forEach>
                            </table>                                                 
    </c:if> 
    <br/>           

          To create ‘Search Portlet’, follow below steps:
    1.       Add below configuration in ‘portlet.xml’ file:

    <portlet>
                            <portlet-name>search</portlet-name>
                            <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
                            <init-param>
                                        <name>contextConfigLocation</name>
                                        <value>/WEB-INF/context/search.xml</value>
                            </init-param>
                            <supports>
                                        <mime-type>text/html</mime-type>
                                        <portlet-mode>view</portlet-mode>
                            </supports>
                            <resource-bundle>content.messages</resource-bundle>
                            <portlet-info>
                                        <title>Search Portlet</title>
                            </portlet-info>
    </portlet>

    2.       Add below configuration in ‘liferay-portlet.xml’ file:

    <portlet>
                <portlet-name>search</portlet-name>
                <instanceable>true</instanceable>
    </portlet>
                 

    3.       Add below configuration in ‘liferay-display.xml’ file

    <category name="SearchPOC">
              <portlet id="search"/>
    </category>            

    4.       Create ‘search.xml’ file with below contents

    xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
                xmlns:context="http://www.springframework.org/schema/context"
                xmlns:util="http://www.springframework.org/schema/util"
                xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-3.0.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
                                                              
    <beanclass="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
       
    <bean id="portletModeHandlerMapping"class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
                            <property name="portletModeMap">
                                        <map>
                                                    <entry key="view">
                                                    <ref bean="searchController" />
                                        </entry>
                                        </map>
                            </property>
        </bean>
    </beans>

    5.       Create Value object ‘SearchVO’:

    package your.package.mvc.vo;
    import java.util.List;
    /**
     * The Class SearchVO.
     *
     * @author nverma
     */
    public class SearchVO {
        /** The no search result flag. */
        private boolean noSearchResultFlag;
        /** The search words. */
        private String searchWords;

        /** The hotel search result. */
        private List searchResult;
        /**
         * Checks if is no search result flag.
         *
         * @return true, if is no search result flag
         */
        public boolean isNoSearchResultFlag() {
            return noSearchResultFlag;
        }
        /**
         * Sets the no search result flag.
         *
         * @param noSearchResultFlag the new no search result flag
         */
        public void setNoSearchResultFlag(boolean noSearchResultFlag) {
            this.noSearchResultFlag = noSearchResultFlag;
        }
        /**
         * Gets the search words.
         *
         * @return the search words
         */
        public String getSearchWords() {
            return searchWords;
        }
        /**
         * Sets the search words.
         *
         * @param searchWords the new search words
         */
        public void setSearchWords(String searchWords) {
            this.searchWords = searchWords;
        }

        /**
         * Gets the search result.
         *
         * @return the search result
         */
        public List getSearchResult() {
            return searchResult;
        }

        /**
         * Sets the search result.
         *
         * @param searchResult the new search result
         */
        public void setSearchResult(List searchResult) {
            this.searchResult = searchResult;
        }
    }

    6.       Create controller  ‘SearchController’:

    package your.package.controller;
    import java.util.ArrayList;
    import java.util.List;
    import javax.portlet.ActionRequest;
    import javax.portlet.ActionResponse;
    import javax.portlet.PortletRequest;
    import javax.portlet.RenderResponse;
    import com.liferay.portal.kernel.search.BooleanQuery;
    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
    import com.liferay.portal.kernel.search.Document;
    import com.liferay.portal.kernel.search.Field;
    import com.liferay.portal.kernel.search.Hits;
    import com.liferay.portal.kernel.search.ParseException;
    import com.liferay.portal.kernel.search.SearchEngineUtil;
    import com.liferay.portal.kernel.search.SearchException;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.portlet.bind.annotation.ActionMapping;
    import org.springframework.web.portlet.bind.annotation.RenderMapping;
    import your.package.mvc.vo.SearchResult;
    import your.package.mvc.vo.SearchVO;
    /**
     * Controller for free text search.
     *
     * @author nverma
     */
    @Controller("searchController")
    @RequestMapping(value = "VIEW")
    public class SearchController extends AbstractBaseController {
        /** The Constant LOG. */
        private static final Log LOG = LogFactory
                .getLog(SearchController.class);
        /** The Constant MODEL_ATT. */
        private static final String MODEL_ATT = "searchVO";
        /**
         * Inits the command object.
         *
         * @param portletRequest the portlet request
         * @return the search vo
         */
        @ModelAttribute(MODEL_ATT)
        public SearchVO initCommandObject(final PortletRequest portletRequest) {
            LOG.debug("Initializing command object 'SearchVO'...");
           return new SearchVO();
        }
        /**
         * Render search.
         *
         * @param renderResponse the render response
         * @return the string
         */
        @RenderMapping
        public String renderSearch(final RenderResponse renderResponse) {
            return "search";
        }
        /**
         * Render search result.
         *
         * @param renderResponse the render response
         * @return the string
         */
        @RenderMapping(params = "action=showSearchResult")
        public String renderSearchResult(final RenderResponse renderResponse) {
            return "search";
        }

        /**
         * Search.
         *
         * @param searchVO the search vo
         * @param actionRequest the action request
         * @param actionResponse the action response
         */
        @ActionMapping(params = "action=search")
        public void search(@ModelAttribute SearchVO searchVO,
                final ActionRequest actionRequest, ActionResponse actionResponse) {
            List searchResultLst = new ArrayList();
            String searchKeyWords = searchVO.getSearchWords();

            LOG.debug("Searing for input keywords  [" + searchKeyWords + "]...");
            Document[] resultDocs = null;
            Hits hits = null;
            BooleanQuery searchQuery = BooleanQueryFactoryUtil.create();

            try {
                searchQuery.addTerm(Field.TITLE, searchKeyWords);
                searchQuery.addTerm("description", searchKeyWords);
                hits = SearchEngineUtil.search(10132, searchQuery, -1, -1);
                resultDocs = hits.getDocs();
            } catch (ParseException e) {
                LOG.error("Failed to parse while searching. " + e.getMessage());
                e.printStackTrace();
            } catch (SearchException e) {
                LOG.error("Failed to search. " + e.getMessage());
                e.printStackTrace();
            }
            if(hits == null || hits.getLength() == 0){
                searchVO.setNoSearchResultFlag(true);
            }
            LOG.debug(" Search result count [" + hits.getLength() + "]");
           for (int i = 0; i < resultDocs.length; i++) {
                LOG.debug("Searched Items: ");
                SearchResult searchResult = new SearchResult();
                LOG.debug("Document ->  " + hits.doc(i).get(Field.UID));
               String onlyForHotel = hits.doc(i).get("AllDocuments");
                if(!"OnlyMyItems".equals(onlyForHotel)){
                    continue;
                }
           String idStr = hits.doc(i).get("Id");
                if(idStr==null || idStr.isEmpty()){
                    continue;
                }
                Long  id = Long.parseLong(idStr);
                LOG.debug("Id ->  " + id);
                searchResult.setId(id);
                String title = hits.doc(i).get(Field.TITLE);
                LOG.debug("Title ->  " + title);
                searchResult.setTitle(title);

                String description = hits.doc(i).get("description");
                LOG.debug("Description ->  " + description);
                searchResult.setDescription(description);
                float score = hits.score(i);
                LOG.debug("Result Score ->  " + hits.score(i));
                searchResult.setSearchScore(score);
                //Add search result into list
                searchResultLst.add(searchResult);
            }

            searchVO.setSearchResult(searchResultLst);
           actionResponse.setRenderParameter("action""showSearchResult");

            LOG.debug("Searched successfully.");
        }
    }

    The API marked under arrow lines is responsible to perform the search for given keywords and set the result into VO to render on JSP.

    7.       Create JSP ‘search.jsp’:

    <%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0"%>
    <%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
    <%@ page contentType="text/html" isELIgnored="false"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
    <portlet:actionURL var="searchUrl">
        <portlet:param name="action" value="search" />
    </portlet:actionURL>
    <c:if test="${searchVO != null}">
       <form:form id="search" commandName="searchVO" action="${searchUrl}">
                <form:input path="searchWords"  value = "Search Words" class="textfield disabledfield"/>
           <input type="submit" value="Search"/>
       </form:form>     
    </c:if> 
    <br/><br/>
    <h3>Search Result</h3>
    <form:form id="searchItem" commandName="searchVO">
    <table border=1>
                            <tr height=50>
                                        <td width=80 align = "center"><b>Search ID</b></td>
                                        <td width=250 align = "center"><b>Title</b></td>
                                        <td width=480 align = "center"><b>Description</b></td>
                                        <td width=140 align = "center"><b>Ratings</b></td>
                            </tr>
                <c:forEach var="searchResult" items="${searchVO.searchResult}">
                <tr height=50>
                                        <td width=80 align = "center"><a href="/search?searchId=${searchResult.id}">${searchResult.id}</a></td>
                                        <td width=250 align = "center"><a href="/search?searchId=${searchResult.id}">${searchResult.title}</a></td>
                                        <td width=480 align = "center"><a href="/search?searchId=${searchResult.id}">${searchResult.description}</a></td>
                                   <td width=140 align = "center"><liferay-ui:ratings-score score="${searchResult.searchScore*5}"/></td>
                            </tr>
               
                </c:forEach>
    </table>                                   
    </form:form>

    After configuring all above source files you can build your application and then deploy on application server. Access your Liferay server, add a test page and drop both portlets on test page and test the search functionality by adding search content first and then searching keywords.

                                       :) Happy Free Text Searching :)

    See More:                                           


    0

    Add a comment

  4. I have got a problem in coding contest. In this post, I would like to share the approach to solve this problem. I would definitely not say that I have invented something new and I am not trying to reinvent the wheel again. I just described the end to end approach that I followed to solve this problem.It was really a great brain storming activity.

    Problem Statement

    "Arrange numbers from 1 to 71 in a sequence such that sum of any 2 adjacent numbers in the sequence adds up to a prime number.”For e.g :  7,6,5,2,1,4,3 is a valid one sequence for arranging 1..7"

    Theory: How to Solve?
    Let's take an example, we want to find all series from 1 to 5 such that sum of any 2 adjacent numbers in the sequence adds up to a prime number. For this problem, there should be below no. of possible combinations.

    1, 4, 3, 2, 5 
    3, 4, 1, 2, 5 
    5, 2, 1, 4, 3 
    5, 2, 3, 4, 1

    Now, lets talk about the approach to get above combinations.

    Firstly, for each number, check all possible numbers in series such that sum should be a prime number. Let's take number 1 and check which sum is prime number out of 1+2 = 3, 1+3=4, 1+4=5, 1+5=6.  So, we have got 1+2 = 3 and 1+4=5 that means 2 and 4 are the numbers that can be connected to number 1. Now apply the same logic for all remaining numbers 2,3,4,5. You will get below combinations.

    1 --> 2, 4
    2 --> 1, 3, 5
    3 --> 2, 4
    4 --> 1, 3
    5 --> 2

    Secondly, create a graph that connects any two numbers that has a prime number sum. If you take number 1 that is connected to 2 and 4, the graph must represent link between connected numbers. In graph's terminology you can say each number is a vertex and each connected link is an edge.



    Till now you are half done. You have graph that connects any two numbers that has a prime number sum.

    Thirdly, you just need to  find out the unique cycles in graph where you visit each vertex exactly once. This is kind of calculating Hamiltonian Paths (also refer this).

    Below graph shows two cycles:

    1, 4, 3, 2, 5
    5, 2, 3, 4, 1


    Below graph shows two cycles:

    3, 4, 1, 2, 5 
    5, 2, 1, 4, 3 


    Theoretically you have achieved what you wanted to achieve. You have all 4 possible combinations for given problem from 1 to 5.

    Now let's convert above theory into computer programming and ask computer to get combinations for any given number. Well, I am a Java Techie and I would prefer to convert this theory into java programming. If you are a Java Techie you have prepared solution in your hand. In case you are not, you have to struggle converting my program into your required language.

    Programmatic Approach: How to Solve?
    Before jumping into the programming, you have to think first how you can represent this graph in java. This is bit complex. But once you get into this; it will be easy to understand and apply.

    I took help from here to represent the graph in two dimensional array. To get combinations from 1 to 5, graph can be represented in 2D array like below:


    Action Points for Programming
    • Populate 2D array that connects any two numbers that has a prime number sum.
    • Implement recursive function to find out the path that visits each connected number exactly once 
    • Print the path if path length equals to the provided last number
    Solution in Java

    Program
    import java.io.File;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;

    import org.apache.log4j.Appender;
    import org.apache.log4j.DailyRollingFileAppender;
    import org.apache.log4j.FileAppender;
    import org.apache.log4j.Level;
    import org.apache.log4j.Logger;
    import org.apache.log4j.PatternLayout;
    /**
     * Coding Contest
     *
     * Arrange numbers from 1 to n in a sequence such that sum of any 2 adjacent
     * numbers in the sequence adds up to a prime number. For e.g : 7,6,5,2,1,4,3 is
     * a valid one sequence for arranging 1..7
     *
     *
     * @author NarendraVerma
     */
    public class PrimeSequenceSolution {

                   /** The pattern layout. */
                   private static PatternLayout patternLayout = new PatternLayout("%d{ISO8601}\t%m%n");
                   /** Logger instance. */
                   private static final Logger LOGGER = Logger
                                                 .getLogger(PrimeSequenceSolution.class);              
                   /** Matrix that holds all prime numbers . */
                   public static int[][] inputArray = null;
                   /** Matrix[1][n] hat connects any two numbers that has a prime number sum. */
                   public static int inputArrayLength;
                   /** The total sequences count. */
                   public static int totalCount = 0;
                   /** Maximum Sequences to be generated. */
                   public static long maxSeq = 1000000000;
                   /** The start time. */
                   public static long startTime;
                   /** The logging flag. */
                   public static boolean loggingFlag = true;
                   /**
                    * Find prime sequences.
                    */
                   public static void findPrimeSequences() {

                                  traverseVertexes(new ArrayList());
                   }            

                   /**
                    * Populate 2D array that connects any two numbers that has a prime number sum
                    *
                    * For example: If last No is 5, matrix is populated like below
                    * 
                    *        1 --> 2, 4
                    *        2 --> 1, 3, 5
                    *        3 --> 2, 4
                    *        4 --> 1, 3
                    *        5 --> 2
                    *
                    * 2D Array:
                    *
                    *      1  2  3  4  5
                    *   1 [0  1  0  1  0]             
                    *   2 [1  0  1  0  1]
                    *   3 [0  1  0  1  0]
                    *   4 [1  0  1  0  0]
                    *   5 [0  1  0  0  0]
                    * 
                    * @param lastNo the last no
                    */
                   public static void prepareInputMatrix(int lastNo) {

                                  inputArray = new int[lastNo][lastNo];

                                  for (int i = 1; i <= lastNo; i++) {

                                                 for (int j = 1; j <= lastNo; j++) {
                                                                if (i == j) {
                                                                               inputArray[i - 1][j - 1] = 0;
                                                                } else {                                                               
                                                                               inputArray[i - 1][j - 1] = isPrime(i + j);
                                                                }
                                                 }
                                  }
                   }
                   /**
                    * Checks if given number is prime number
                    *
                    * @param number
                    *            the number
                    * @return the int
                    */
                   public static int isPrime(int number) {
                                  // check if number is a multiple of 2
                                  if (number % 2 == 0)
                                                 return 0;
                                  // if not, then check the odd numbers
                                  for (int i = 3; i * i <= number; i += 2) {
                                                 if (number % i == 0)
                                                                return 0;
                                  }
                                  return 1;
                   }
                   /**
                    * The recursive function to find out the path that visits each connected number exactly once  
                    *
                    * @param traversedPath
                    *            the path so far
                    * @return the list
                    */
                   public static List traverseVertexes(List traversedPath) {

                                  inputArrayLength = inputArray.length;
                                  if (traversedPath.size() == inputArrayLength) {
                                                 printSequences(traversedPath);
                                                 return traversedPath;
                                  } else if (traversedPath.size() == 0) {
                                                 for (int i = 0; i < inputArrayLength; i++) {
                                                                traversedPath.add(i);
                                                                traverseVertexes(traversedPath);
                                                                traversedPath.remove(traversedPath.size() - 1);
                                                 }
                                  } else {
                                                 int currentNode = traversedPath.get(traversedPath.size() - 1);
                                                 for (int i = 0; i < inputArrayLength; i++) {
                                                                if (!traversedPath.contains(i) && inputArray[currentNode][i] != 0) {
                                                                               System.out.println("I am here:" + traversedPath );
                                                                               traversedPath.add(i);
                                                                               traverseVertexes(traversedPath);
                                                                               traversedPath.remove(traversedPath.size() - 1);
                                                                }             
                                                 }
                                  }
                                  return traversedPath;

                   }
                   /**
                    * Prints the solution.
                    *
                    * @param pathSoFar
                    *            the path so far
                    */
                   public static void printSequences(List pathSoFar) {

                                  StringBuffer sb = new StringBuffer(" -- ");
                                  sb.append(" [ SeqNo."+ (totalCount + 1 )+"] -- ");
                                 
                                  for (Integer number : pathSoFar) {

                                                 sb.append(number + 1).append(", ");

                                  }
                                 
                                  if (loggingFlag)
                                                 LOGGER.info(sb.toString());
                                  else
                                                 System.out.println(new Date() + " " + sb.toString());

                                  if (maxSeq != 1000000000 && totalCount > maxSeq - 2) {
                                                
                                                 LOGGER.info("Maximum sequences [ " + maxSeq
                                                                               + " ]  have been generated.  Total-Elapsed-Time [ "
                                                                               + (System.currentTimeMillis() - startTime) + " msec ] ");
                                 
                                                 System.out.println("Maximum sequences [ "
                                                                               + maxSeq
                                                                               + " ] have been generated.  Total-Elapsed-Time [ "
                                                                               + (System.currentTimeMillis() - startTime) + " msec ]");

                                                 System.exit(1);
                                  }

                                  ++totalCount;
                   }              
                   /**
                    * Creates the log file.
                    *
                    * @param lastNo the last no
                    * @return the file
                    */
                   public static File createLogFile(int lastNo){
                                 
                                  String fileName = "271962_primeseq_"+ lastNo + ".log";
                                 
                                  File logFile = new File(fileName);
                                 
                                  //Remove existing log file if exists
                                  if(logFile.exists()){
                                                 logFile.delete();
                                  }
                                 
                                  FileAppender fa = new FileAppender();
                                  fa.setName("FileOut");
                                  fa.setFile(fileName);
                                  fa.setLayout(patternLayout);
                                  fa.setThreshold(Level.DEBUG);
                                  fa.setAppend(true);
                                  fa.activateOptions();
                                  LOGGER.setAdditivity(false);

                                  LOGGER.addAppender(fa);
                                 
                                  return new File(fileName);
                   }
                   /**
                    * The main method.
                    *
                    * @param args the arguments
                    * @throws Exception the exception
                    */
                   public static void main(String[] args) throws Exception{
                                 
                                  int lastNo = 0;
                                 
                                  try{
                                                 // Fetch second argument - The number for which the sequence to be generated
                                                 lastNo = Integer.parseInt(args[1]);
                                                
                                                 // Create log file
                                                 File logFile = createLogFile(lastNo);
                                                
                                                 startTime = System.currentTimeMillis();
                  
                                                 // Fetch first argument - This is log level flag. If 'true' write to log
                                                 // file, if false print to the console
                                                 String logFlag = args[0];
                                                 if ("true".equals(logFlag)){
                                                                System.out.println("\nPrime sequences are logged into file ["+logFile.getAbsolutePath()+"]");
                                                                loggingFlag = true;
                                                 }             
                                                 else
                                                                loggingFlag = false;
                                                               
                                                 // Fetch third argument - Maximum Sequences to be generated
                                                 if(args.length == 3){
                                                                maxSeq = Integer.parseInt(args[2]);             
                                                 }
                                  }             
                                  catch(Exception e ){
                                                 String errorMessage = "\nPlease provide the valid arguments \n"   
                                                 System.out.println(errorMessage);
                                                 System.exit(1);
                                  }
                                 
                                  // Populate 2D array
                                  prepareInputMatrix(lastNo);

                                  // Find the sequences
                                  findPrimeSequences();

                                  long end = System.currentTimeMillis();
                                  long totalTimeSpend = end - startTime;
                                 
                                  if(!loggingFlag){
                                                 LOGGER.info("Since logging flag (first argument of the program) is false, sequences are printed to the console");
                                  }
                                 
                                  LOGGER.info("Total-Elapsed-Time [ " + totalTimeSpend
                                                                + " msec ] and No. of total sequences found [ "
                                                                + totalCount + " ]");
                  
                                  System.out.println("\nCongratulations !! Sequences generated successfully.");

                                  System.out.println("\nTotal sequences generated [ "+ totalCount + " ],  Elapsed Time [ " + totalTimeSpend + " msec ]");

                   }

    }

    How to Execute Program?

    You can use below command to execute above program.
    java PrimeSequenceSolution
    • First Argument: If 'true' sequences are written in log file, if 'false' print to the console
    • Second Argument : The number for which the sequence to be generated
    • Third Argument: Maximum sequences to be generated. This is optional. Default value is 1000000000
    Example:

    java PrimeSequenceSolution  true 5 
    java PrimeSequenceSolution  false 7 
    java PrimeSequenceSolution  false 15 100000


    Execution Statistics
    Below are the captured execution statistics for above program.


    Conclusion

    Above approach is one of the solutions for given problem. I am sure there can be other approaches to solve this problem. If you notice the execution statistics, execution time is drastically increased if last number is increased. What about if program gets executed to get combinations for last number say 70, 80, 100 etc. This program will definitely take much time to get combinations or it may prompt out of memory error. Generating the expected result can be one of the aspect to solve a problem but efficiency is most important aspect that must not be ignored. Though, this solution gives the expected result but it may not be considered as efficient as it should be. This execution statistics left me thinking about what can be the other approaches? How can I make this program efficient? Can I think about parallel processing? etc.Well, I still need to get answers of my own questions. If you know the better solution; your comments are greatly appreciated.

    Additionally, I tried to find out the real world problems related to this mathematical problem. TSP is one of the most famous problems. This is just to get into the real world problems. isn't it?

    0

    Add a comment

  5. Jenkins is an open source tool which provides continuous integration services for software development. If you want to get more detail about Jenkins and it's history I would suggest refer this link. This post will help you installing and configuring Jenkins and creating jobs to trigger maven builds. Setting up Jenkins is not a rocket science indeed. In this post, I would like to concise the installation and configuration steps.


    Installing Jenkins
    Though, there are different ways to install Jenkins. But, in this post I will be describing Jenkins installation with Apache Tomcat server. Hence, please make sure that you have Apache Tomcat.   
    1. Download Jenkins war file from here and copy war into tomcat's 'webapp' folder
    2. Start the tomcat server and access Jenkins

    Jenkins installation is done. Wow, so easy. Isn't it?

    Configuring Jenkins
    Now you have Jenkins running with you. It's time to configure the project specific environment for continuous and automatic build. This section describes about JDK, Maven and Mail Notification configuration.

    Go to 'Manage Jenkins

     

    Go to 'Manage Jenkins > Configure System'




    Creating Job for Automatic and Continuous Build
    So far you installed and configured  the Jenkins. It's time to create new job. Before creating new job ensure that your project is a maven project. Now, go to 'New Job' and fill the required information. believe, below screen shot tells everything which is required to configure a job. You just need to configure below points at minimum:
    • Source Code Management
    • Build Triggers
    • Build
    • Notifications





    Once you save the job you will see below screen


      Now click on 'Build Now' and see the 'Build History'. You will get new entry in 'Build History'. To see the build logs just hover the mouse on this build and click on 'Console Output', you can see the build logs.



      Congratulations! you are done with setting up continuous build system.If you want to see that how effectively it works, just check-in any file in your project and see whether your build is triggered automatically. In case, build is failed with latest checked-in changes you must get a notification mail saying build is failed.

      Last but not least, you should agree with my point 'Setting up Jenkins is not a rocket science indeed'. Shouldn't you? :) :) :)
      0

      Add a comment

    1. Peer code review is an important activity to find and fix mistakes which are overlooked during development. It helps improving both software quality as well as developers skills. Though, it’s a good process for quality improvement. But this process becomes tedious if you need to share files and send review comments through mails, you need to organize formal meetings and you need to communicate peers who are in different time zone. Here is the point, why to do this manually? Why to follow this tedious process? There are so many automated code review tools which ensure fast, easy and collaborative code review process.

      My team used to follow this tedious process manually. By following this process almost for one year we realized that we should go for automated code review tools. I explored free tool for code review. After evaluating so many tools I found pretty interesting tool called CodeReviewer. I would say this is an ‘Awesome’ tool with rich documentation and great community support.

      CodeReviewer is free code review tool from SmartBear. This tool provides basic features which are required in code review process like Subversion integration, user administration, event based email notification, real time chat with peers etc. If you want advanced feature you can go for commercial version ‘CodeReviewer Pro’. The yearly price for ‘CodeReviewer Pro’ subscription is around 257$ per enabled user.

      'CodeReviewer' and 'CodeReviewer Pro' Features Comparison Matrix


      Installation and Configuration Insights
      Installing CodeReviewer is very easy task. You just download the windows setup from here and install on your system by following this link. Once installation is done follow link1 and link2 to configure CodeReviewer.

      Environment Specific Configuration
      Once CodeReviewer is ready to use, now it’s time to configure Server URLintegrate mail serverintegrate Subversion and  configure users.

      Review Life Cycle
      As far as CodeReviewer’s life cycle concern, there are 5 phases of review life cycle. Below diagram describes each and every phase and there processing order. 

      How to Use CodeReviewer 
      You can start review by creating new review request. Note that you can't create review as admin user. You need to log-in as different user. If you want to upload file from Subversion you can follow this. Once the review request is created and sent for review, reviewers need to perform review and communicate with other participants who are participating in this review.

      Questions and Answers
      • Which database is used in CodeReviewer by default?
      The server installer comes with support for the hypersonic embedded database. 
      • Can I integrate my own database with CodeReviewer?
      Yes. MySQL Server can be integrated with CodeReviewer. For Oracle and SQL Server you need to go for Pro. Refer this link for more detail. 
      • Can I integrate my own Bug Tracking system with CodeReviewer?
      Code Reviewer can be integrated with external issue-tracking systems such as TeamTrack, Bugzilla, JIRA, and FogBugz. Refer this link for more detail. 
      • Can I take CodeReviewer Backup?
      Yes. Admin user can take the CodeReviewer backup by following 'System Status > Server Backup'. Refer this link for more detail. 
      • How to restore backup?
      CodeReviewer backup can be restored. Refer this link for more detail.

      Others
      You might face below issue while configuring SVN Repository. 


      Install the SVN client from here and configure svn.exe path in 'svn Executable' text box.

      At last I would suggest you to have a look into this video. This tells about how to use CodeReviewer in Eclipse IDE itself. 

      WOW, I have very nice experience with CodeReviewer. I liked this one. You will like this.Won't you?

      0

      Add a comment

    2. If you are using Hudson as continuous integration server and you might feel lazy about accessing Hudson explicitly to check the build status or checking Hudson build status mails, there is an option to monitor Hudson build and perform build activities in Eclipse IDE itself. This post describes about installing, configuring and using the Hudson in Eclipse IDE.

      Installing Hudson Plugin in Eclipse
      Use below URL To install the Hudson Eclipse plugin using Eclipse Update Manager:

      In case you face issue while installing plugin using update manager you can directly drop dk.contix.eclipse.hudson_x.x.x.jar  in Eclipse's plugins directory and restart IDE.

      Configuring Hudson 
      Once the installation is done, configure the Hudson URL from Preferences > Hudson to connect to Hudson server. Also remember to Check URL whether URL is valid or not. Your Hudson server must be running at this point of time.


      How to Use Hudson in Eclipse?
      In eclipse, go to Windows > Show View > Hudson. You will see below tab is added in your Eclipse. Also, When the plugin is running, a health icon is displayed at the bottom of the Eclipse window. The icon is red on build failure and green on success.  Please note that due to limitations in Eclipse, the Hudson view must be active before the icon is displayed.


      In case build is failed, you will get below pop-up in your eclipse IDE. 


      You can also perform below activities using this Eclipse Hudson plugin.For this, select any project listed in Hudson tab and right click.
      1. Schedule New Build
      2. View Console Output
      3. Open Hudson in Browser


      If you want to  access the Hudson admin console in your Eclipse,  go to 'Hudson' tab and double click on your project.


      Limitations 
      If your Hudson server requires authentication and authorization for access, then you can not see the list of jobs in Eclipse. Because Eclipse Hudson plugin does not facilitates interface to provide Hudson web credentials. To overcome this limitation, you have to give read permission to Anonymous user for jobs in Hudson which need to be displayed in Eclipse. 

      Common Issues
      •  Issue in configuring Hudson base URL which is SSL enabled
      Solution: This issue is due to missing required certificates. Refer below links to generate the certificate. Once the certificate is generated you can copy those into $JAVA_HOME/lib/security for the JRE that eclipse is using for its run time. It will trust that cert and then you can access configured HTTPS URL successfully.

      • Once your certificate issue is resolved then you may face below issue
                   java.security.cert.CertificateException: No subject alternative names present

                  Solution: Configure HTTPS URL using DNS rather giving IP address.           
      • While refreshing build status you get below pop-up 

                  Solution: Ensure that your job's name in Hudson does not contain white spaces.

      References
      Below are the references for more detail about Hudson Eclipse plugin.
      0

      Add a comment

    3. As a developer or architect, you always need to draw some sequence diagrams to demonstrate or document your functionality. And of course, if you do this manually you have to spare much time for this activity. Just think about, what If sequence diagram is generated automatically and you get it free of cost. Your reaction would be ‘wow’ this is great. But the next question will be ‘how’. So, I am publishing this post to describe ‘how’.There are different open source tools which help to generate UML sequence diagrams. ‘Java Call Trace’ is such a tool that can generate UML sequence diagram based on reverse engineering.

      Install ‘Java Call Trace’
      • Download the java call trace from here.
      • Extract its contents at your desired location.
      How to Use ‘Java Call Trace’ to Generate Sequence Diagram

           To generate sequence diagram using Java call trace you have to follow below steps:
      • Define your filter in \Calltracer\filters.txt
      This will generate sequence diagram only for the classes which are defined under given package. For example: com.mycom.example 
      • Capture call trace (as xml) of your application/program 
      For JDK 1.5 and above, add the following JVM options and  execute your application/program.

      Syntax:
      -agentpath:[full-path-to-folder-of-dll]\calltracer5.dll=traceFile-[trace-file],filterFile-[filter-file],filterList-[filter|filter|...],outputType-[xml or text],usage-[controlled, uncontrolled] -Djava.library.path=[full-path-to-folder-of-dll] -Dcalltracerlib=calltracer5 

      Example
      -agentpath:C:\calltracer\jvmti\calltracer5.dll=traceFile-C:\calltracer\call.trace,filterFile-C:\calltracer\filters.txt,outputType-xml,usage-uncontrolled -Djava.library.path=C:\calltracer\jvmti -Dcalltracerlib=calltracer5
      • Generate sequence diagram using call trace captured in first step
      Once you have generated a XML output using the ‘calltracer’ tool you can use the ‘Calltrace2Seq tool’ to generate UML sequence diagram.

      \Calltrace2Seq\src\Calltrace2Seq.java INPUTXMLFILE-C:\\test.xml OUTPUTFOLDER-C:\\ OUTPUTFILENAME-mysequencediagram

      INPUTXMLFILE: This is the generated xml location.
      OUTPUTFILENAME: This is the location where generated sequence diagram image is copied.

      Note: The output generated by the ‘calltracer’ tool has some header information that will not allow it (the output) to be used directly as an input to the ‘Calltrace2Seq’ tool. You can remove the header from the output xml file and then use this file as input argument for ‘Calltrace2Seq’.

      Generated Sample Sequence Diagram

      Below diagram is generated using Java Call Trace. There are three classes ManagedBean, Service and DataAccess. Where ManagedBean is having main method which is calling Service's method and Service is calling DataAccess's method.


      Java Call Trace Details

      If you are interested to get more details about Java Call Trace you can refer below:

      javacalltracer\readme.txt
      javacalltracer\calltracer\readme.txt
      javacalltracer\Calltrace2Seq\readme.txt
      0

      Add a comment

    4. If you are using JPA 2.0 with Hibernate and you want to do audit logging from middle-ware itself, I believe you landed up on the exact place where you should be. You can try audit logging in your local environment by following this post.

      Required JPA/Hibernate Maven Dependencies

      <dependency>
               <groupId>org.hibernate.java-persistence</groupId>
               <artifactId>jpa-api</artifactId>
               <version>2.0-cr-1</version>
      </dependency>
      <dependency>
               <groupId>org.hibernate</groupId>
               <artifactId>hibernate-entitymanager</artifactId>
               <version>3.6.4.Final</version>   
      </dependency>
      <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-orm</artifactId>
               <version>3.0.5.RELEASE</version>
      </dependency>

      JPA Configuration in Spring Application Context File

      For JPA/Hibernate, you need to configure entity manager, transaction, data source and JPA vendor in your ‘applicationContext.xml’ file.

      <bean id="dataSource"
               class="org.springframework.jdbc.datasource.DriverManagerDataSource"
               p:driverClassName="oracle.jdbc.driver.OracleDriver"  
              p:url="jdbc:oracle:thin:@127.0.0.1:1521/mydbservice "
               p:username="nvuser" p:password="nvpass" />

      <bean id="entityManagerFactory"
               class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
               p:persistenceUnitName="MyPersistentUnit"
               p:persistenceXmlLocation="classpath*:persistence.xml"
               p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
               <property name="loadTimeWeaver">
                  <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
               </property>              
      </bean>

      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
                       p:entityManagerFactory-ref="entityManagerFactory" />

      <bean id="jpaAdapter"
                       class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
                       p:database="ORACLE" p:showSql="true" />

      Persistence.xml Configuration

      To enable the hibernate audit logging you need to configure a property called ‘hibernate.ejb.interceptor’ in persistence.xml file and the value of this property should be the class that extends the Hibernate ‘EmptyInterceptor’.

      xml version=”1.0” encoding=”UTF-8”?>
      <persistence version="2.0"
               xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
               <persistence-unit name=" MyPersistentUnit ">
                     
                       //JPA Entity classes are configured here
                       <class>com.mycom.entities.User </class>           

                       <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

                       <properties>
                       <property name="hibernate.ejb.interceptor"
                                value=" com.xxx.entities.baseentity.AuditLogInterceptor" />
                       </properties>
               </persistence-unit>
      </persistence>

      Audit Logging Interceptor Implementation

      Hibernate provides an interceptor called ‘EmptyInterceptor’ with following methods.
      • onSave() – This method is called when an entity is saved, but the entity is not saved into database yet.
      • onFlushDirty() – This method is called when an entity is updated, the entity is not update into database yet.
      • onDelete () – This method is called when an entity is deleted , the entity is not deleted into database yet.
      • preFlush() – This method is called before the saved, updated or deleted entities are not committed to the database.
      • postFlush() – This method is called after the saved, updated or deleted entities are committed to database. This is called after preFlush() method.

      To perform audit logging follow below steps:
      • Create data base table to capture audit data:
                Audit [ Row_ID, Old_Value, New_Value, Column_Changed, Table_Changed,
                          Transaction_Id, User_ID]
      • Create JPA entity say ‘AuditTrail’ for above table
      • Create class ‘AuditLogInterceptor’ by extending Hibernate ‘EmptyInterceptor’.
      • Override interceptor methods in ‘AuditLogInterceptor’
      • Capture required information into AuditTrail entity and save entity

      public class AuditLogInterceptor extends EmptyInterceptor {  
          
        private List auditTrailList = new ArrayList();    
         . . . . . .
       
         // First Parameter: The entity which is being updated     
         // Second Parameter: The primary key of updated row 
         // Third Parameter:  Current states of updated entity    
         // Fourth Parameter: Previous states of updated entity  
         // Fifth Parameter: The variable names of updated entity
         // Sixth Parameter: The type of variables of updated entity
         @Override
         public boolean onFlushDirty(Object entity, Serializable id,
                  Object[] currentState, Object[] previousState,
                  String[] propertyNames, Type[] types) {                     
                 // Get the table name from entity
                 Class entityClass = entity.getClass();
                  Table tableAnnotation = entityClass.getAnnotation(Table.class);            
                
                 for (int i = 0; i < currentState.length; i++) {              
                      // Track changes only for the states which are String or Number types           
                       if(!(previousState[i] instanceof String
                           || previousState[i] instanceof Long
                           || previousState[i] instanceof BigDecimal
                            || previousState[i] instanceof Integer
                           || previousState[i] instanceof Timestamp))
                       {
                                continue;
                       }                
                      // Check whether column value is updated
                       if(previousState[i] != null && currentState[i] != null
                         &&  !(previousState[i].equals(currentState[i]))){
                               
                        AuditTrail auditTrail = new AuditTrail();               
                       // Set updated table name
                       auditTrail.setTableName(tableAnnotation.name());
                       // Set updated column name
                       Column col = null;
                       try {                                                  
                              String filedName = propertyNames[i];
                              if(filedName.contains("serialVersionUID")){
                                        continue;
                              }
                              Character firstChar =  filedName.charAt(0);
                              firstChar = Character.toUpperCase(firstChar);
                              String filedNameSubStr = filedName.substring(1);
                              String methodName = "get"+ firstChar+filedNameSubStr;                       
                              Class[] parameterTypes = new Class[]{};                           
                              Method ueMethod = entityClass.getDeclaredMethod(methodName, parameterTypes);
                              col = ueMethod.getAnnotation(Column.class);                         
                          } catch (Exception e) {
                                      e.printStackTrace();
                          }
                         if(col != null){
                                auditTrail.setColumnIdentifier(col.name());       
                         }               
                      // Set old value
                      if(previousState[i] != null){
                                auditTrail.setOldValue(previousState[i] == null  ? null :
                               previousState[i].toString());    
                      }               
                      // Set new value
                      if(currentState[i] != null){
                                auditTrail.setNewValue(currentState[i] == null ? null:
                               currentState[i].toString());
                      }               
                      // Set database operation
                      auditTrail.setOperation("Update");               
                      // Set row primary key value
                      auditTrail.setRowIdentifier(id.toString());  
                      auditTrailList.add(auditTrail);                         
                    }           
                  }                        
              return false;
          }        
          @Override  
          public void postFlush(Iterator iterator) throws CallbackException {            
                 // Set unique transaction id in all AuditTrail Entities
                 String transId = UUID.randomUUID().toString();     
                 for (AuditTrail auditTrailauditTrailList) {
                     auditTrail.setTransactionId(transId);
                 }   
                // Save ‘AuditTrail’ entity list into database using AuditDAO 
          }  
       } 

      Audit logging Sequence Flow




      Issue Faced during Audit Logging Implementation

      I faced an interesting issue during audit logging implementation in my application. Let me share that issue detail.

      I had ‘AbstractDAO’ and 'AuditDAO' class extends this ‘AbstractDAO’. 'AuditDAO' class is configured as ‘@Repository’. AbstractDAO’ has entity manager property which is initialized by Spring Container. I wanted to auto wire ‘AuditDAO’ in ‘AuditLogInterceptor’ to save ‘AuditTrail’ entity. But, this couldn’t happen. The reason was ‘AuditLogInterceptor’ was not defined as spring resource (like @Component or @Resources or @Repository). Hence's its property can’t be auto wired. Since, this interceptor is initialized by hibernate, I couldn't configure this as spring resource. Finally, I had to take the help of StackOverflow and got the idea to solve this problem. You can refer this issue and solution from here.
      0

      Add a comment

    5. If any issue is observed in production, there are two major aspects related to providing the solution. First ‘how quick you can analyze the root cause’ and second ‘how quick you can fix the issue’. Story starts from analyzing the root cause. Unless, you find the root cause, you can’t even think about providing solution for that. Can you?

      Now let’s think about actual production environment. There may be multiple JVMs where application is deployed.  For the developer or support team, it’s very tedious to go to each box, download the log files and start analyzing the log files to know the root cause of the problem.

      To get rid of this tedious task, ‘Splunk’ can help you. Splunk is a product that turns machine data into valuable insights. Splunk can index application logs at centralized location and provides rich user interface on top of indexed logs. With the help of this user interface you can look for data patterns that you might be interested in. Splunk is an Agent-Sever based platform where agents are responsible to collect and index any machine data from various resources at real time.



      Licensing Aspects

      Splunk charges it’s customer on the basis of how may GB data per day collected/indexed. If you want to try this and you download this first time you will get all of the Enterprise features of Splunk for 60 days and you can index up to 500 megabytes of data per day.

      Features
      • Fast data search and analysis 
      • Facilitates custom dashboards
      • Graphical representation
      • Access Control
      • Monitor and Alert
      • Distributed Search
      • Reports

      Do you want to play with Splunk?

      If yes, you can follow pretty simple step-by-step instructions from here to install it. First try to install it as ‘Local System User’. Once you install and log-in to the Splunk Web you will get below page:


      Click on ‘Add Data’ link.


      Click on ‘A file or directory of files’ link and provide your log file location. Once you provide your log file location and save data successfully you will see below page:


      Now ready to search and click on ‘Start Searching’ link. In search box you can provide your data pattern to search in the log files. You can save your search pattern and result with actions provided with ‘Save’ button. Also, you can create reports and alerts using ‘Create’ button.



      Isn't it so easy and interesting? Of course yes. I was really impressed when I tried in my local environment and used different features. 

      Alternative

      There are many open sources in market which also provides centralized logging. For more detail refer this  link.

      0

      Add a comment

    Loading