Monday, February 9, 2015

Using SVN in Eclipse

Install SVN in Eclipse

Go Help -> Martketplace. Find: "SVN Team provider". Select "Subversive - SVN Team Provider" in result list. Click Install. Follow its instruction and restart Eclipse after finishing.

Go Window -> Perferences -> Team -> SVN. Choose SVN Connect tab. If SVN Connector select box is empty, click to install a new one. Choose SVNKit with latest build. Restart Eclipse if needed.

Using SVN

Checkout your SVN project to your local workspace. Import it to Eclipse. SVN Connector will automatically detect and connect to your SVN repository.

Happy coding!


Thursday, February 5, 2015

Custom JSON web service in Liferay

We can publish JSON webservice from Liferay portlet or hook by 2 ways (at my knowledge at the time of writing this code).

Way No. 1: using Service builder

 Specify "remote-service="true"" in service.xml like below:

<entity name="Employee" local-service="true" remote-service="true">

and run Liferay->SDK->build-service.
It automatically create some interfaces and classes named: *Service, *ServiceImpl or *ServiceUtil... (These files differ with *LocalService, * LocalServiceImpl or *LocalServiceUtil which are used internally JVM).

JSON service has not exposed yet until you write some functions in *ServiceImpl and run Liferay->SDK->build-service again.

The magic is clearly explained in the websites listed in References. Basically, it will add @JSONWebService to *Service interface. By this way, Liferay can scan and publish function in this interface as an API webservice.

Test it by type: http://your-ip:port/plugin-context-path/api/jsonws. It will list out all your service functions that has been published.

Way No. 2: write custom classes and register to Liferay manually

Liferay portal 6.1 is slightly different with Liferay portal 6.2 in the way it scans service functions.

  • In Liferay 6.1:
Create an interface in your package. One thing should be noted is that this interface has to be suffix by "Service" like: "HelloworldService" and mark with @JSONWebService at class scope definition.

@JSONWebService
public interface HelloworldService {
    public java.lang.String sayHello();
}


Corresponding to Service inferface, Liferay need a "Util" class. It will be the endpoint that catches the request from client.

public class HelloworldServiceUtil {
    public static String sayHello() {
        HelloworldServiceImpl service = null;
        if (service == null) {
            HelloworldServiceImpl invokableLocalService = (HelloworldServiceImpl) PortletBeanLocatorUtil.locate("ws-test-portlet", "hellworld");

            if (invokableLocalService != null) {
                service = invokableLocalService;
            }           
        }
       
        if(service != null) {
            return service.sayHello();
        } else {
            return "null";
        }
    }
}


In this case, HelloworldServiceImpl does not take part in directly to the process of  public JSON service. HelloworldService is used to create signature. HelloworldServiceUtil is to handle request. It calls HelloworldServiceImpl to do the business logic and return the result. As you can see, we can get an instance of "HelloworldServiceImpl" from ext-spring.xml through PortletBeanLocatorUtil.locate().
Noted that functions in HelloworldServiceUtil are all static.

Test it by type: http://your-ip:port/plugin-context-path/api/jsonws.

  • In Liferay 6.2:
Things become simpler. Just create an interface and name it any thing you like.

public interface Helloworld {
    public String sayHello();
}


Implement it by an implementation class:

@JSONWebService
public class HelloworldImpl implements Helloworld {

    @Override
    public String sayHello() {
        return "Hello baby";
    }
}


But put @JSONWebService at implementation class like above.
That's all you have to do.

Both in Liferay 6.1 and In Liferay 6.2, you need add JSON Service servlet in web.xml file:

<filter>
        <filter-name>Secure JSON Web Service Servlet Filter</filter-name>
        <filter-class>com.liferay.portal.kernel.servlet.PortalClassLoaderFilter</filter-class>
        <init-param>
            <param-name>filter-class</param-name>
            <param-value>com.liferay.portal.servlet.filters.secure.SecureFilter</param-value>
        </init-param>
        <init-param>
            <param-name>basic_auth</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>portal_property_prefix</param-name>
            <param-value>jsonws.servlet.</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Secure JSON Web Service Servlet Filter</filter-name>
        <url-pattern>/api/jsonws/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <servlet-class>com.liferay.portal.kernel.servlet.PortalClassLoaderServlet</servlet-class>
        <init-param>
            <param-name>servlet-class</param-name>
            <param-value>com.liferay.portal.jsonwebservice.JSONWebServiceServlet</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <url-pattern>/api/jsonws/*</url-pattern>
    </servlet-mapping>


Try and happy coding!
-----
References:

http://www.liferay.com/documentation/liferay-portal/6.1/development/-/ai/json-web-services

http://www.liferaysavvy.com/2013/11/liferay-custom-json-web-services-on.html

http://www.endeios.io/blog/-/blogs/leverage-liferay-service-s-structure-for-you-own-personal-back-end-s-service

Good Liferay and weblogic sharing

http://bestjavasolutions.blogspot.com/

Wednesday, February 4, 2015

Rest Template handle gzip format

Create new converter for Gzip format:

public class GzipMarshallingHttpMessageConverter extends
        AbstractHttpMessageConverter<DLSSeries> {

    public GzipMarshallingHttpMessageConverter() {
        super(new MediaType("application", "json"));
    }
   
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    protected DLSSeries readInternal(Class<? extends DLSSeries> clazz,
            HttpInputMessage inputMessage) throws IOException {
        boolean gzip = false;
        HttpHeaders headers = inputMessage.getHeaders();
        List<String> contentEncoding = headers.get("Content-Encoding");

        if (headers != null && contentEncoding != null) {
            for (String contentEncodingStr : contentEncoding) {
                if (contentEncodingStr != null
                        && "gzip".equalsIgnoreCase(contentEncodingStr.trim())) {
                    gzip = true;
                }
            }
        }

        if (gzip) {
            return parseGzipStream(inputMessage.getBody());
        }

        return null;
    }
   
    @Override
    protected void writeInternal(DLSSeries t, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        writeToResult(t, outputMessage.getHeaders(), new StreamResult(
                outputMessage.getBody()));

    }
   
    private DLSSeries parseGzipStream(InputStream inputStreamGzip) {
        DLSSeries series = new DLSSeries();
        List<DLSEpisode> episodeList = new ArrayList<DLSEpisode>();
        try {
            InputStream inputStream = new GZIPInputStream(inputStreamGzip);
            ObjectMapper mapper = new ObjectMapper();
            episodeList = mapper.readValue(inputStream, new TypeReference<ArrayList<DLSEpisode>>() {});
            series.setEpisodeList(episodeList);
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
        return series;
    }

    protected void writeToResult(Object o, HttpHeaders headers, Result result)
            throws IOException {
    }
   
    public String convertStreamToString(InputStream is) throws IOException {
        /*
         * To convert the InputStream to String we use the Reader.read(char[]
         * buffer) method. We iterate until the Reader return -1 which means
         * there's no more data to read. We use the StringWriter class to
         * produce the string.
         */
        if (is != null) {
            Writer writer = new StringWriter();

            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, n);
                }
            } finally {
                is.close();
            }
            return writer.toString();
        } else {
            return "";
        }
    }
   
    /** Tracking system operations. */
    private static final Logger LOGGER =  LoggerFactory.getLogger(GzipMarshallingHttpMessageConverter.class);
}


Create RestTemplate instance and add new converter to it:

        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> messageConverters = new         ArrayList<HttpMessageConverter<?>>();
        messageConverters.add(new GzipMarshallingHttpMessageConverter());

        restTemplate.setMessageConverters(messageConverters);


Using restTemplate, for example:

            ResponseEntity<DLSSeries> response = null;
           
            try {
                response = restTemplate.exchange(
                        url, HttpMethod.GET, entity, DLSSeries.class);
               
                if(response != null && response.getBody() != null) {
                    DLSSeries series = response.getBody();
                   //do more things here
                }
            } catch(RestClientException e) {
                LOGGER.error(e.getMessage());
            }


P/s: change some example classes to your own classes.