Tuesday, February 28, 2017

How to block the login for the Management Console of WSO2 IoT Server

Put the following config to <IoTS_HOME>/core/repository/conf/tomcat/carbon/WEB-INF/web.xml.


<security-constraint>
    <display-name>Restrict direct access to certain folders</display-name>
    <web-resource-collection>
        <web-resource-name>Restricted folders</web-resource-name>
        <url-pattern>/carbon/*</url-pattern>
    </web-resource-collection>
    <auth-constraint />
</security-constraint>

Then restart the server.


Wednesday, January 18, 2017

[WSO2 IoT] How to Self-unsubscribe from Mobile Apps.

In default WSO2 IoT server, you can't uninstall mobile apps from the apps store. But, you can self-unsubscribe from mobile apps by changing a config. For that, you have to set "EnableSelfUnsubscriptionas true in <IoT_HOME>/core/repository/conf/app-manager.xml


        <Config name="EnableSelfUnsubscription">true</Config>

Then, restart the server.

Login to store and click on "My Apps" tab. Click on the button (with 3 dots) in the bottom right corner of the app and click on "Uninstall".


That's all. :)

Saturday, December 31, 2016

How to Use log4jdbc with WSO2 Products

log4jdbc is a Java JDBC driver that can log JDBC calls. There are some steps to use it in WSO2 products.

Let's see how to use log4jdbc with WSO2 API Manager.

First, download log4jdbc driver from here. Then, copy it into <APIM_HOME>/repository/components/lib directory.

Then, change JDBC <url> and <driverClassName> of master-datasource.xml in <APIM_HOME>/repository/conf/datasources directory as shown below. Change the every datasource that you want to log. Here, I'm changing datasource of "WSO2AM_DB".

<datasource>
            <name>WSO2AM_DB</name>
            <description>The datasource used for API Manager database</description>
            <jndiConfig>
                <name>jdbc/WSO2AM_DB</name>
            </jndiConfig>
            <definition type="RDBMS">
                <configuration>
                    <url>jdbc:log4jdbc:h2:repository/database/WSO2AM_DB;DB_CLOSE_ON_EXIT=FALSE</url>
                    <username>wso2carbon</username>
                    <password>wso2carbon</password>
                    <defaultAutoCommit>false</defaultAutoCommit>
                    <driverClassName>net.sf.log4jdbc.DriverSpy</driverClassName>
                    <maxActive>50</maxActive>
                    <maxWait>60000</maxWait>
                    <testOnBorrow>true</testOnBorrow>
                    <validationQuery>SELECT 1</validationQuery>
                    <validationInterval>30000</validationInterval>
                </configuration>
            </definition>
        </datasource>

Note: When you are changing JDBC url, you have to add "log4jdbc" part to the url.

Then, you can add logging options to log4j.properties file of <APIM_HOME>/repository/conf directory. There are several logging options.

i. jdbc.sqlonly

If we use this log, it logs all the SQLs executed by Java Code.

If you want to enable that logs of your server, add below line to log4.properties file.

log4j.logger.jdbc.sqlonly=INFO

Then restart the server and you will see logs like below.

[2016-12-31 23:26:35,099]  INFO - JMSListener Started to listen on destination : throttleData of type topic for listener Siddhi-JMS-Consumer#throttleData
[2016-12-31 23:26:55,502]  INFO - CarbonEventManagementService Starting polling event receivers
[2016-12-31 23:27:16,213]  INFO - sqlonly SELECT 1 

[2016-12-31 23:27:16,214]  INFO - sqlonly select * from AM_BLOCK_CONDITIONS 

[2016-12-31 23:27:16,214]  INFO - sqlonly SELECT KEY_TEMPLATE FROM AM_POLICY_GLOBAL 

[2016-12-31 23:37:24,224]  INFO - PermissionUpdater Permission cache updated for tenant -1234
[2016-12-31 23:37:24,316]  INFO - CarbonAuthenticationUtil 'admin@carbon.super [-1234]' logged in at [2016-12-31 23:37:24,316+0530]
[2016-12-31 23:37:24,587]  INFO - sqlonly SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 

[2016-12-31 23:37:24,589]  INFO - sqlonly SELECT CAST( SUM(RATING) AS DECIMAL)/COUNT(RATING) AS RATING FROM AM_API_RATINGS WHERE API_ID 
=2 GROUP BY API_ID 

[2016-12-31 23:37:24,590]  INFO - sqlonly SELECT * FROM AM_API WHERE API_ID = 2 

[2016-12-31 23:37:24,590]  INFO - sqlonly SELECT NAME FROM AM_POLICY_SUBSCRIPTION WHERE TENANT_ID =-1234 

[2016-12-31 23:37:24,593]  INFO - sqlonly SELECT grp.CONDITION_GROUP_ID ,AUM.HTTP_METHOD,AUM.AUTH_SCHEME, pol.APPLICABLE_LEVEL, AUM.URL_PATTERN,AUM.THROTTLING_TIER,AUM.MEDIATION_SCRIPT,AUM.URL_MAPPING_ID 
FROM AM_API_URL_MAPPING AUM INNER JOIN AM_API API ON AUM.API_ID = API.API_ID LEFT OUTER JOIN 
AM_API_THROTTLE_POLICY pol ON AUM.THROTTLING_TIER = pol.NAME LEFT OUTER JOIN AM_CONDITION_GROUP 
grp ON pol.POLICY_ID = grp.POLICY_ID where API.CONTEXT= '/pizzashack/1.0.0' AND API.API_VERSION 
= '1.0.0' ORDER BY AUM.URL_MAPPING_ID 

[2016-12-31 23:37:24,596]  INFO - sqlonly SELECT DISTINCT SB.USER_ID, SB.DATE_SUBSCRIBED FROM AM_SUBSCRIBER SB, AM_SUBSCRIPTION SP, AM_APPLICATION 
APP, AM_API API WHERE API.API_PROVIDER='admin' AND API.API_NAME='PizzaShackAPI' AND API.API_VERSION='1.0.0' 
AND SP.APPLICATION_ID=APP.APPLICATION_ID AND APP.SUBSCRIBER_ID=SB.SUBSCRIBER_ID AND API.API_ID 
= SP.API_ID AND SP.SUBS_CREATE_STATE = 'SUBSCRIBE' 

[2016-12-31 23:37:31,323]  INFO - sqlonly SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 

[2016-12-31 23:37:31,327]  INFO - sqlonly SELECT CAST( SUM(RATING) AS DECIMAL)/COUNT(RATING) AS RATING FROM AM_API_RATINGS WHERE API_ID 
=2 GROUP BY API_ID 

[2016-12-31 23:37:31,327]  INFO - sqlonly SELECT * FROM AM_API WHERE API_ID = 2 

[2016-12-31 23:37:31,327]  INFO - sqlonly SELECT NAME FROM AM_POLICY_SUBSCRIPTION WHERE TENANT_ID =-1234 



ii. jdbc.sqltiming

If we use this log,  it logs time taken by each JDBC call.

If you want to enable that logs of your server, add below line to log4.properties file.

log4j.logger.jdbc.sqltiming=INFO

Then restart the server and you will see logs like below.

[2016-12-31 23:42:02,597]  INFO - PermissionUpdater Permission cache updated for tenant -1234
[2016-12-31 23:42:02,682]  INFO - CarbonAuthenticationUtil 'admin@carbon.super [-1234]' logged in at [2016-12-31 23:42:02,682+0530]
[2016-12-31 23:42:02,912]  INFO - sqltiming SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 
 {executed in 1 msec}
[2016-12-31 23:42:02,913]  INFO - sqltiming SELECT CAST( SUM(RATING) AS DECIMAL)/COUNT(RATING) AS RATING FROM AM_API_RATINGS WHERE API_ID 
=2 GROUP BY API_ID 
 {executed in 0 msec}
[2016-12-31 23:42:02,913]  INFO - sqltiming SELECT * FROM AM_API WHERE API_ID = 2 
 {executed in 0 msec}
[2016-12-31 23:42:02,914]  INFO - sqltiming SELECT NAME FROM AM_POLICY_SUBSCRIPTION WHERE TENANT_ID =-1234 
 {executed in 0 msec}
[2016-12-31 23:42:02,917]  INFO - sqltiming SELECT grp.CONDITION_GROUP_ID ,AUM.HTTP_METHOD,AUM.AUTH_SCHEME, pol.APPLICABLE_LEVEL, AUM.URL_PATTERN,AUM.THROTTLING_TIER,AUM.MEDIATION_SCRIPT,AUM.URL_MAPPING_ID 
FROM AM_API_URL_MAPPING AUM INNER JOIN AM_API API ON AUM.API_ID = API.API_ID LEFT OUTER JOIN 
AM_API_THROTTLE_POLICY pol ON AUM.THROTTLING_TIER = pol.NAME LEFT OUTER JOIN AM_CONDITION_GROUP 
grp ON pol.POLICY_ID = grp.POLICY_ID where API.CONTEXT= '/pizzashack/1.0.0' AND API.API_VERSION 
= '1.0.0' ORDER BY AUM.URL_MAPPING_ID 
 {executed in 0 msec}
[2016-12-31 23:42:02,920]  INFO - sqltiming SELECT DISTINCT SB.USER_ID, SB.DATE_SUBSCRIBED FROM AM_SUBSCRIBER SB, AM_SUBSCRIPTION SP, AM_APPLICATION 
APP, AM_API API WHERE API.API_PROVIDER='admin' AND API.API_NAME='PizzaShackAPI' AND API.API_VERSION='1.0.0' 
AND SP.APPLICATION_ID=APP.APPLICATION_ID AND APP.SUBSCRIBER_ID=SB.SUBSCRIBER_ID AND API.API_ID 
= SP.API_ID AND SP.SUBS_CREATE_STATE = 'SUBSCRIBE' 
 {executed in 0 msec}
[2016-12-31 23:42:12,871]  INFO - sqltiming SELECT 1 
 {executed in 0 msec}
[2016-12-31 23:42:12,872]  INFO - sqltiming SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 
 {executed in 0 msec}
[2016-12-31 23:42:12,872]  INFO - sqltiming SELECT CAST( SUM(RATING) AS DECIMAL)/COUNT(RATING) AS RATING FROM AM_API_RATINGS WHERE API_ID 
=2 GROUP BY API_ID 
 {executed in 0 msec}
[2016-12-31 23:42:12,873]  INFO - sqltiming SELECT * FROM AM_API WHERE API_ID = 2 
 {executed in 0 msec}
[2016-12-31 23:42:12,873]  INFO - sqltiming SELECT * FROM AM_POLICY_SUBSCRIPTION WHERE TENANT_ID =-1234 
 {executed in 0 msec}
[2016-12-31 23:42:12,874]  INFO - sqltiming SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 
 {executed in 0 msec}
[2016-12-31 23:42:12,875]  INFO - sqltiming SELECT A.SCOPE_ID, A.SCOPE_KEY, A.NAME, A.DESCRIPTION, A.ROLES FROM IDN_OAUTH2_SCOPE AS A INNER 
JOIN AM_API_SCOPES AS B ON A.SCOPE_ID = B.SCOPE_ID WHERE B.API_ID = 2 
 {executed in 0 msec}
[2016-12-31 23:42:12,875]  INFO - sqltiming SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 
 {executed in 0 msec}
[2016-12-31 23:42:12,875]  INFO - sqltiming SELECT URL_PATTERN, HTTP_METHOD, AUTH_SCHEME, THROTTLING_TIER, MEDIATION_SCRIPT FROM AM_API_URL_MAPPING 
WHERE API_ID = 2 ORDER BY URL_MAPPING_ID ASC 
 {executed in 0 msec}
[2016-12-31 23:42:12,876]  INFO - sqltiming SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = 'admin' AND API.API_NAME = 'PizzaShackAPI' 
AND API.API_VERSION = '1.0.0' 
 {executed in 0 msec}
[2016-12-31 23:42:12,876]  INFO - sqltiming SELECT RS.RESOURCE_PATH, S.SCOPE_KEY FROM IDN_OAUTH2_RESOURCE_SCOPE RS INNER JOIN IDN_OAUTH2_SCOPE 
S ON S.SCOPE_ID = RS.SCOPE_ID INNER JOIN AM_API_SCOPES A ON A.SCOPE_ID = RS.SCOPE_ID WHERE 
A.API_ID = 2 
 {executed in 0 msec}


iii. jdbc.audit

If we use this log,  it logs all the activities of the JDBC call.

If you want to enable that logs of your server, add below line to log4.properties file.

log4j.logger.jdbc.audit=ON

Then restart the server and you will see logs like below.

[2016-12-31 23:44:55,631]  INFO - CarbonAuthenticationUtil 'admin@carbon.super [-1234]' logged in at [2016-12-31 23:44:55,631+0530]
[2016-12-31 23:44:55,828] DEBUG - audit 2. Statement.new Statement returned   org.apache.tomcat.jdbc.pool.PooledConnection.validate(PooledConnection.java:454)
[2016-12-31 23:44:55,829] DEBUG - audit 2. Connection.createStatement() returned net.sf.log4jdbc.StatementSpy@44c41ca9  org.apache.tomcat.jdbc.pool.PooledConnection.validate(PooledConnection.java:454)
[2016-12-31 23:44:55,829] DEBUG - audit 2. Statement.execute(SELECT 1) returned true  org.apache.tomcat.jdbc.pool.PooledConnection.validate(PooledConnection.java:461)
[2016-12-31 23:44:55,830] DEBUG - audit 2. Statement.close() returned   org.apache.tomcat.jdbc.pool.PooledConnection.validate(PooledConnection.java:462)
[2016-12-31 23:44:55,830] DEBUG - audit 2. PreparedStatement.new PreparedStatement returned   sun.reflect.GeneratedMethodAccessor31.invoke(null:-1)
[2016-12-31 23:44:55,830] DEBUG - audit 2. Connection.prepareStatement(SELECT API.API_ID FROM AM_API API WHERE API.API_PROVIDER = ? AND API.API_NAME = ? AND API.API_VERSION = ?) returned net.sf.log4jdbc.PreparedStatementSpy@396ee038  sun.reflect.GeneratedMethodAccessor31.invoke(null:-1)
[2016-12-31 23:44:55,831] DEBUG - audit 2. PreparedStatement.setString(1, "admin") returned   org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO.getAPIID(ApiMgtDAO.java:6217)
[2016-12-31 23:44:55,831] DEBUG - audit 2. PreparedStatement.setString(2, "PizzaShackAPI") returned   org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO.getAPIID(ApiMgtDAO.java:6218)
[2016-12-31 23:44:55,831] DEBUG - audit 2. PreparedStatement.setString(3, "1.0.0") returned   org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO.getAPIID(ApiMgtDAO.java:6219)
[2016-12-31 23:44:55,831] DEBUG - audit 2. PreparedStatement.executeQuery() returned net.sf.log4jdbc.ResultSetSpy@1e4299fd  org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO.getAPIID(ApiMgtDAO.java:6220)
[2016-12-31 23:44:55,831] DEBUG - audit 2. PreparedStatement.close() returned   org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer.closeInvoked(StatementFinalizer.java:57)
[2016-12-31 23:44:55,832] DEBUG - audit 2. Connection.getAutoCommit() returned false  org.wso2.carbon.ndatasource.rdbms.ConnectionRollbackOnReturnInterceptor.invoke(ConnectionRollbackOnReturnInterceptor.java:44)
[2016-12-31 23:44:55,832] DEBUG - audit 2. Connection.rollback() returned   org.wso2.carbon.ndatasource.rdbms.ConnectionRollbackOnReturnInterceptor.invoke(ConnectionRollbackOnReturnInterceptor.java:45)
[2016-12-31 23:44:55,832] DEBUG - audit 2. PreparedStatement.close() returned   org.wso2.carbon.apimgt.impl.utils.APIMgtDBUtil.closeStatement(APIMgtDBUtil.java:175)
[2016-12-31 23:44:55,833] DEBUG - audit 2. Connection.setAutoCommit(false) returned   sun.reflect.GeneratedMethodAccessor32.invoke(null:-1)
[2016-12-31 23:44:55,834] DEBUG - audit 2. PreparedStatement.new PreparedStatement returned   sun.reflect.GeneratedMethodAccessor31.invoke(null:-1)
[2016-12-31 23:44:55,834] DEBUG - audit 2. Connection.prepareStatement( SELECT    CAST( SUM(RATING) AS DECIMAL)/COUNT(RATING) AS RATING  FROM    AM_API_RATINGS  WHERE    API_ID =?  GROUP BY    API_ID ) returned net.sf.log4jdbc.PreparedStatementSpy@70a2e307  sun.reflect.GeneratedMethodAccessor31.invoke(null:-1)
[2016-12-31 23:44:55,834] DEBUG - audit 2. PreparedStatement.setInt(1, 2) returned   org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO.getAverageRating(ApiMgtDAO.java:3969)

iv. jdbc.resultset


If we use this log,  it logs the result set of each JDBC call.

If you want to enable that logs of your server, add below line to log4.properties file.

log4j.logger.jdbc.resultset=INFO

Then restart the server and you will see logs like below.

[2016-12-31 23:47:41,386]  INFO - PermissionUpdater Permission cache updated for tenant -1234
[2016-12-31 23:47:41,478]  INFO - CarbonAuthenticationUtil 'admin@carbon.super [-1234]' logged in at [2016-12-31 23:47:41,478+0530]
[2016-12-31 23:47:41,683]  INFO - resultset 2. ResultSet.new ResultSet returned 
[2016-12-31 23:47:41,684]  INFO - resultset 2. ResultSet.next() returned true
[2016-12-31 23:47:41,684]  INFO - resultset 2. ResultSet.getInt(API_ID) returned 2
[2016-12-31 23:47:41,685]  INFO - resultset 2. ResultSet.close() returned 
[2016-12-31 23:47:41,686]  INFO - resultset 2. ResultSet.new ResultSet returned 
[2016-12-31 23:47:41,686]  INFO - resultset 2. ResultSet.next() returned false
[2016-12-31 23:47:41,686]  INFO - resultset 2. ResultSet.close() returned 
[2016-12-31 23:47:41,688]  INFO - resultset 2. ResultSet.new ResultSet returned 
[2016-12-31 23:47:41,688]  INFO - resultset 2. ResultSet.next() returned true
[2016-12-31 23:47:41,688]  INFO - resultset 2. ResultSet.getString(API_TIER) returned null
[2016-12-31 23:47:41,688]  INFO - resultset 2. ResultSet.next() returned false
[2016-12-31 23:47:41,688]  INFO - resultset 2. ResultSet.close() returned 
[2016-12-31 23:47:41,689]  INFO - resultset 2. ResultSet.new ResultSet returned 
[2016-12-31 23:47:41,689]  INFO - resultset 2. ResultSet.next() returned true
[2016-12-31 23:47:41,689]  INFO - resultset 2. ResultSet.getString(NAME) returned Gold
[2016-12-31 23:47:41,689]  INFO - resultset 2. ResultSet.getString(QUOTA_TYPE) returned requestCount
[2016-12-31 23:47:41,689]  INFO - resultset 2. ResultSet.getString(QUOTA_TYPE) returned requestCount
[2016-12-31 23:47:41,689]  INFO - resultset 2. ResultSet.getInt(UNIT_TIME) returned 1
[2016-12-31 23:47:41,690]  INFO - resultset 2. ResultSet.getString(TIME_UNIT) returned min
[2016-12-31 23:47:41,690]  INFO - resultset 2. ResultSet.getInt(QUOTA) returned 5000
[2016-12-31 23:47:41,690]  INFO - resultset 2. ResultSet.getString(UUID) returned e4eee273-4eb0-4d8c-9f6d-f503b58c7dd0

v. jdbc.connection

If we use this log,  it logs connection details like opening and closing the database connections.

If you want to enable that logs of your server, add below line to log4.properties file.

log4j.logger.jdbc.connection=INFO

Then restart the server and you will see logs like below.


[2016-12-31 23:55:47,521]  INFO - connection 2. Connection closed
[2016-12-31 23:55:47,523]  INFO - connection 4. Connection closed
[2016-12-31 23:55:54,447]  INFO - EmbeddedRegistryService Configured Registry in 0ms
[2016-12-31 23:55:54,708]  INFO - connection 5. Connection opened



Friday, December 30, 2016

Disable Chunking in WSO2 API Manager

By default in WSO2 API Manager, chunking is enabled. You can check it by enabling wire logs in API Manager. If you send a "PUT" or "POST" request, you will see "Transfer-Encoding: chunked" header like below in outgoing request.

[2016-12-30 11:57:27,125] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "POST /am/sample/pizzashack/v1/api/order HTTP/1.1[\r][\n]"
[2016-12-30 11:57:27,125] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Accept-Language: en-US,en;q=0.8[\r][\n]"
[2016-12-30 11:57:27,125] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Accept-Encoding: gzip, deflate[\r][\n]"
[2016-12-30 11:57:27,125] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Origin: https://localhost:9443[\r][\n]"
[2016-12-30 11:57:27,125] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Content-Type: application/json; charset=UTF-8[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Accept: application/json[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Transfer-Encoding: chunked[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Host: localhost:9443[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Connection: Keep-Alive[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "User-Agent: Synapse-PT-HttpComponents-NIO[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "a4[\r][\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "{[\n]"
[2016-12-30 11:57:27,126] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "customerName": "string",[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "delivered": true,[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "address": "string",[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "pizzaType": "string",[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "creditCardNumber": "string",[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "quantity": 0,[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "  "orderId": 0[\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "}[\r][\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "0[\r][\n]"
[2016-12-30 11:57:27,127] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "[\r][\n]"

But sometimes backends don't support chunking. In such cases you have to disable chunking. For that there are two ways.

Method 01 :

If you want to disable chunking in all APIs,  you can add highlighted line to <inSequence> of velocity.xml in <APIM_HOME>/repository/resources/api_templates/

<inSequence>

## check and set response caching
#if($responseCacheEnabled)
<cache scope="per-host" collector="false" hashGenerator="org.wso2.caching.digest.REQUESTHASHGenerator" timeout="$!responseCacheTimeOut">
    <implementation type="memory" maxSize="500"/>
</cache>
#end
<property name="api.ut.backendRequestTime" expression="get-property('SYSTEM_TIME')"/>
##############  define the filter based on environment type production only, sandbox only , hybrid ############

#if(($environmentType == 'sandbox') || ($environmentType =='hybrid' && !$endpoint_config.get("production_endpoints") ))
#set( $filterRegex = "SANDBOX" )
#else
#set( $filterRegex = "PRODUCTION" )
#end
<property name="DISABLE_CHUNKING" value="true" scope="axis2"/> 
#if($apiStatus != 'PROTOTYPED')
<filter source="$ctx:AM_KEY_TYPE" regex="$filterRegex">
    <then>
        #end        
        #if(($environmentType == 'sandbox') || ($environmentType =='hybrid' && ! $endpoint_config.get("production_endpoints") ))
        #draw_endpoint( "sandbox" $endpoint_config )
        #else
        #draw_endpoint( "production" $endpoint_config )
        #end
        #if($apiStatus != 'PROTOTYPED')
    </then>
    <else>
        #if($environmentType !='hybrid')
        <payloadFactory>
            <format>
                <error xmlns="">
                    #if($environmentType == 'production')
                    <message>Sandbox Key Provided for Production Gateway</message>
                    #elseif($environmentType == 'sandbox')
                    <message>Production Key Provided for Sandbox Gateway</message>
                    #end
                </error>
            </format>
        </payloadFactory>
        <property name="ContentType" value="application/xml" scope="axis2"/>
        <property name="RESPONSE" value="true"/>
        <header name="To" action="remove"/>
        <property name="HTTP_SC" value="401" scope="axis2"/>
        <property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>
        <send/>
        #else
        #if($endpoint_config.get("production_endpoints") && $endpoint_config.get("sandbox_endpoints"))
        #draw_endpoint( "sandbox" $endpoint_config )
        #elseif($endpoint_config.get("production_endpoints"))
        <sequence key="_sandbox_key_error_"/>
        #elseif($endpoint_config.get("sandbox_endpoints"))
        <sequence key="_production_key_error_"/>
        #end
        #end
    </else>
</filter>
#end
</inSequence>

Then restart the server. Changing this file will affect future APIs created in API manager. If you want to disable chunking in old APIs as well, you have to republish old APIs.

Method 02 :
If you want to disable chunking only for certain APIs, you can use a custom mediation extension.

1. Create a sequence to disable chunking like below and save it in the file system.


<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse"
          name="chunk-disable-sequence">
        <property name="DISABLE_CHUNKING" value="true" scope="axis2" />
</sequence>

2. Edit the API from API Publisher.

3. Go to "Implement" Tab and check "Enable Message Mediation".

4. Upload above created sequence to "In Flow" under "Message Mediation Policies"

5. Then save API.

Now chunking is disabled for that particular API.

If you send a "PUT" or "POST" request,  you will see "Content-Length" header instead of "Transfer-Encoding: chunked" header like below in outgoing request.

[2016-12-30 13:22:18,877] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "POST /am/sample/pizzashack/v1/api/order HTTP/1.1[\r][\n]"
[2016-12-30 13:22:18,877] DEBUG - wire HTTPS-Sender I/O dispatcher-1 <<<< "Accept-Language: en-US,en;q=0.8[\r][\n]"
[2016-12-30 13:22:18,877] DEBUG - wire HTTPS-Sender I/O dispatcher-1  "Accept-Encoding: gzip, deflate[\r][\n]"
[2016-12-30 13:22:18,877] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Origin: https://localhost:9443[\r][\n]"
[2016-12-30 13:22:18,877] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Content-Type: application/json; charset=UTF-8[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Accept: application/json[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Content-Length: 135[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Host: localhost:9443[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "Connection: Keep-Alive[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "User-Agent: Synapse-PT-HttpComponents-NIO[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "[\r][\n]"
[2016-12-30 13:22:18,878] DEBUG - wire HTTPS-Sender I/O dispatcher-1 << "{"customerName":"string","delivered":true,"address":"string","pizzaType":"string","creditCardNumber":"string","quantity":0,"orderId":0}"
[2016-12-30 13:22:19,084] DEBUG - wire HTTPS-Sender I/O dispatcher-1 >> "HTTP/1.1 201 Created[\r][\n]"


Saturday, December 10, 2016

Adding a New Store Logo to Enterprise Mobility Manager(EMM)

I explained how to change styles (background colors, fonts etc.) of Store of WSO2 EMM from this

By default, WSO2 EMM Store comes with WSO2 logo. But you can change it easily. 




Today, from this post I'm going to show how to change the logo of Store of  EMM. 

First, Create a directory called "carbon.super/themes" inside <EMM_HOME>/repository/deployment/server/jaggeryapps/store/themes/.

Then, Create a directory called "custom/libs/theme-wso2_1.0/images" inside <EMM_HOME>/repository/deployment/server/jaggeryapps/store/themes/carbon.super/themes.

Copy your logo to <EMM_HOME>/repository/deployment/server/jaggeryapps/store/themes/carbon.super/themes/custom/libs/theme-wso2_1.0/images. Let's assume the image name is "myimage.png".

Then, Add image name to <EMM_HOME>/repository/deployment/server/jaggeryapps/store/themes/store/partials/header.hbs. Change the Image name of <img> tag with a class name of "logo"


<img src="{{customThemeUrl "/libs/theme-wso2_1.0/images/myimage.png"}}" alt="apps-store"
                     title="apps-store" class="logo" />

If you want to change the Store name, change the value of <h1> with a class name "display-block-xs".


<h1 class="display-block-xs">Google Apps Store</h1>


Refresh store. Store will look like below.




Thursday, December 8, 2016

Adding a New Store Theme to Enterprise Mobility Manager(EMM)

A theme consists of UI elements such as logos, images, background colors etc. WSO2 EMM Store comes with a default theme.



You can extend the existing theme by writing a new one.

From this blog post I'm going to show how to change styles (background colours, fonts etc.)
First, Create a directory called "carbon.super/themes" inside <EMM_HOME>/repository/deployment/server/jaggeryapps/store/themes/.

Then, Create a directory called "css" inside <EMM_HOME>/repository/deployment/server/jaggeryapps/store/themes/carbon.super/themes.
Add below two css files to above created css directory. You can change it's value based on your preferences.

1. appm-left-column-styles.css


/*========== MEDIA ==========*/
@media only screen and (min-width: 768px) {
    .page-content-wrapper.fixed {
        min-height: calc(100% - 130px);
        max-height: calc(100% - 130px);
    }
}

.media {
    margin-top: 0;
}

.media-left {
    padding-right: 0;
}

.media-body {
    background-color: #EFEFEF;
}

/**/
/*========== NAVIGATION ==========*/


.section-title {
    background-color: #444444;
    border: 1px solid #444444;
    height: 40px;
    padding-top: 5px;
    width: 200px;
    padding-left: 10px;
    font-size: 18px;
    color: #fff;
}

/**/
/*========== TAGS ==========*/
.tags {
    word-wrap: break-word;
    width: 200px;
    padding: 5px 5px 5px 5px;
    background-color: #ffffff;
    display: inline-block;
    margin-bottom: 0;
}

.tags > li {
    line-height: 20px;
    font-weight: 400;
    cursor: pointer;
    border: 1px solid #E4E3E3;
    font-size: 12px;
    float: left;
    list-style: none;
    margin: 5px;
}

.tags > li a {
    padding: 3px 6px;
}

.tags > li:hover,
.tags > li.active {
    color: #ffffff;
    background-color: #7f8c8d;
    border: 1px solid #7f8c8d;
}

.tags-more {
    float: right;
    margin-right: 11px;
}

/**/
/*=========== RECENT APPS ==========*/
.recent-app-items {
    list-style: none;
    width: 200px;
    padding: 5px 0 5px 0;
    background-color: #ffffff;
    margin-bottom: 10px;
}

.recent-app-items > li {
    padding: 6px 6px 6px 6px;
}
.recent-app-items .recent-app-item-thumbnail {
    width: 60px;
    height: 45px;
    line-height: 45px;
    float: left;
    text-align: center;
}

.recent-app-items .recent-app-item-thumbnail > img {
    max-height: 45px;
    max-width: 60px;
}

.recent-app-items .recent-app-item-thumbnail > div {
    height: 45px;
    width: 60px;
    color: #ffffff;
    font-size: 14px;
}

.recent-app-items .recent-app-item-summery {
    background-color: transparent;
    padding-left: 3px;

    width:127px;
}

.recent-app-items .recent-app-item-summery, .recent-app-items .recent-app-item-summery > h4 {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

nav.navigation > ul{
    background: #525252;
    color: #fff;
    position: relative;
    -moz-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
    -ms-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
    -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    list-style: none;
    padding:0px;
    margin: 0px;
}

nav.navigation ul li {
    min-height: 40px;
    color: #fff;
    text-decoration: none;
    font-size: 16px;
    font-weight: 100;
    position: relative;
}

nav.navigation a:after{
    content: " ";
    display: block;
    height: 0;
    clear: both;
}

nav.navigation ul li a i {
    line-height: 100%;
    font-size: 21px;
    vertical-align: middle;
    width: 40px;
    height: 40px;
    float: left;
    text-align: center;
    padding: 9px;
}

nav.navigation .left-menu-item {
    text-align: left;
    vertical-align: middle;
    padding-left: 10px;
    line-height: 38px;
    width: 160px;
    height: 40px;
    font-size: 14px;
    display: table;
    margin-left: 40px;
}

nav.navigation .left-menu-item i{
    float: none;
    position: relative;
    left: 0px;
    font-size: 10px;
    display: table-cell;
}

ul.sublevel-menu {
    padding: 0px ;
    list-style: none;
    margin: 0px;
    display: block;
    background-color: rgb(108, 108, 108);

}

ul.sublevel-menu li{
    line-height: 40px;
}

ul.sublevel-menu li a{
    display:block;
    font-size: 14px;
    text-indent:10px;
}
ul.sublevel-menu li a:hover{
    background-color: #626262;
}
nav.navigation ul > li .sublevel-menu li .icon{
    background-color: rgb(108, 108, 108);
}
nav.navigation ul > li ul.sublevel-menu li a:hover .icon{
    background-color: #626262;
}
ul.sublevel-menu .icon {
    background-color: none;
    font-size: 17px;
    padding: 11px;
}

nav a.active .sublevel-menu {
    display: block;
}

nav .sublevel-menu {
    display: none;
}

nav.navigation.sublevel-menu{
    display: none;
}

nav.navigation ul > li.home .icon {
    background: #c0392b;
    color: white;
}

nav.navigation ul > li.home.active {
    background: #c0392b;
    color: white;
}

nav.navigation ul > li.home.active > .left-menu-item {
    background: #c0392b;
}

nav.navigation ul > li.green .icon {
    background: #63771a;
    color: white;
}

nav.navigation ul > li.green:hover > .icon {
    background: #63771a;
    color: white;
}

nav.navigation ul > li.green:hover .left-menu-item, nav.navigation ul > li.green.active .left-menu-item, nav.navigation ul > li.green.hover .left-menu-item {
    background: #63771a;
    color: white;

}

nav.navigation ul > li.red .icon {
    background: #c0392b;
    color: white;
}

nav.navigation ul > li.red:hover > .icon {
    background: #c0392b;
    color: white;
}

nav.navigation ul > li.red:hover .left-menu-item, nav.navigation ul > li.red.active  .left-menu-item, nav.navigation ul > li.red.hover .left-menu-item {
    background: #c0392b;
    color: white;

}

nav.navigation ul > li.orange .icon {
    background: #0a4c7f;
    color: white;
}

nav.navigation ul > li.orange:hover > .icon {
    background: #0a4c7f;
    color: white;
}

nav.navigation ul > li.orange:hover  .left-menu-item, nav.navigation ul > li.orange.active  .left-menu-item, nav.navigation ul > li.orange.hover  .left-menu-item {
    background: #0a4c7f;
    color: white;

}

nav.navigation ul > li.yellow .icon {
    background: #f39c12;
    color: white;
}

nav.navigation ul > li.yellow:hover > .icon {
    background: #f39c12;
    color: white;
}

nav.navigation ul > li.yellow:hover .left-menu-item, nav.navigation ul > li.yellow.active  .left-menu-item, nav.navigation ul > li.yellow.hover  .left-menu-item {
    background: #f39c12;
    color: white;

}

nav.navigation ul > li.blue .icon {
    background: #2980b9;
    color: white;
}

nav.navigation ul > li.blue:hover > .icon {
    background: #2980b9;
    color: white;
}

nav.navigation ul > li.blue:hover  .left-menu-item, nav.navigation ul > li.blue.active .left-menu-item, nav.navigation ul > li.blue.hover  .left-menu-item {
    background: #2980b9;
    color: white;

}

nav.navigation ul > li.purple .icon {
    background: #766dde;
    color: white;
}

nav.navigation ul > li.purple:hover > .icon {
    background: #766dde;
    color: white;
}

nav.navigation ul > li.purple:hover  .left-menu-item, nav.navigation ul > li.purple.active  .left-menu-item, nav.navigation ul > li.purple.hover  .left-menu-item {
    background: #766dde;
    color: white;

}

nav.navigation ul > li.grey .icon {
    background: #2c3e50;
    color: white;
}

nav.navigation ul > li.grey:hover > .icon {
    background: #2c3e50;
    color: white;
}

nav.navigation ul > li.grey:hover  .left-menu-item, nav.navigation ul > li.grey.active .left-menu-item, nav.navigation ul > li.grey.hover .left-menu-item {
    background: #2c3e50;
    color: white;

}

nav.navigation .second_level {
    display: none;
}

nav.navigation .second_level a {
    line-height: 20px;
    padding: 8px 0 8px 10px;
    box-sizing: border-box;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
}

nav.navigation .second_level a:hover {
    background-color: rgba(0, 0, 0, 0.05);
}

nav.navigation .second_level > .back {
    height: 100%;
    padding: 0 3px;
    background: #FFF;
    vertical-align: middle;
    font-size: 13px;
    width: 5px;
}

nav.navigation .second_level > .left-menu-item {
    padding: 6px 0;
    text-align: left;
    width: 100%;
    vertical-align: middle;
}

@media (min-width: 320px) and (max-width: 991px) {
    ul.sublevel-menu li a {
        text-indent:0px;
    }
}

.page-content-wrapper.fixed .sidebar-wrapper.sidebar-nav,
.page-content-wrapper.fixed .sidebar-wrapper.sidebar-options {
    width: 250px;
    background: #373e44;
    overflow-y: auto;
    overflow: visible;
}

.page-content-wrapper.fixed .sidebar-wrapper.sidebar-nav-sub {
    height: 100%;
    z-index: 1000000;
    background: #272c30;
}


.page-content-wrapper.fixed .sidebar-wrapper.sidebar-options {
    width: 235px;
    max-height: calc(100% - 85px);
}
.sidebar-wrapper.toggled .close-handle.close-sidebar {
    display: block;
}

#left-sidebar{
    background-color: inherit;
    color: inherit;
}

#left-sidebar.sidebar-nav li a{
    color:inherit;
}

@media (min-width: 768px){
    .visible-side-pane{
        position: relative;
        left: 0px;
        width: initial;
    }
}

.mobile-sub-menu-active {
    color: #63771a !important;
}

2. appm-main-styles.css

/*========== HEADER ==========*/
header {
    background: #242c63;
}

header .header-action {
    display: inline-block;
    color: #ffffff;
    text-align: center;
    vertical-align: middle;
    line-height: 30px;
    padding: 10px 10px 10px 10px;
}

header .header-action:hover,
header .header-action:focus,
header .header-action:active {
    background: #4d5461;
}

/**/
/*========== BODY ==========*/
.body-wrapper a:hover {
    text-decoration: none;
}

.body-wrapper > hr {
    border-top: 1px solid #CECDCD;
    margin-top: 50px;
}

/**/
/*=========== nav CLASS ========*/
.actions-bar {
    background: #2c313b;
}

.actions-bar .navbar-nav > li a {
    line-height: 50px;
}

.actions-bar .navbar-nav > li a:hover,
.actions-bar .navbar-nav > li a:focus,
.actions-bar .navbar-nav > li a:active {
    background: #4d5461;
    color: #ffffff;
}

.actions-bar .navbar-nav > .active > a,
.actions-bar .navbar-nav > .active > a:hover,
.actions-bar .navbar-nav > .active > a:focus,
.actions-bar .navbar-nav > .active > a:active {
    background: #4d5461;
    color: #ffffff;
}

.actions-bar .dropdown-menu {
    background: #2c313b;
}

.actions-bar .dropdown-menu > li a {
    line-height: 30px;
}

.navbar-search, .navbar-search .navbar {
    min-height: 40px;
}

.navbar-menu-toggle {
    float: left;
    height: 40px;
    padding: 0;
    line-height: 47px;
    font-size: 16px;
    background:#1A78D8;
    color: #ffffff;
}
.navbar-menu-toggle:hover, .navbar-menu-toggle:focus, .navbar-menu-toggle:active {
    color: #ffffff;
    background: #0F5296;
}
/**/
/*========== SEARCH ==========*/
.search-bar {
    background-color: #035A93;
}

.search-box .input-group, .search-box .input-group > input,
.search-box .input-group-btn, .search-box .input-group-btn > button {
    min-height: 40px;
    border: none;
    margin: 0;
    background-color: #004079;
    color: #ffffff;
}

.search-box .input-group-btn > button {
    opacity: 0.8;
}

.search-box .input-group-btn > button:hover,
.search-box .input-group-btn > button:active,
.search-box .input-group-btn > button:focus {
    opacity: 1;
}

.search-box .search-field::-webkit-input-placeholder {
    /* WebKit, Blink, Edge */
    color: #fff;
    opacity: 0.8;
    font-weight: 100;
}

.search-box .search-field:-moz-placeholder {
    /* Mozilla Firefox 4 to 18 */
    color: #fff;
    opacity: 0.8;
    font-weight: 100;
}

.search-box .search-field::-moz-placeholder {
    /* Mozilla Firefox 19+ */
    color: #fff;
    opacity: 0.8;
    font-weight: 100;
}

.search-box .search-field:-ms-input-placeholder {
    /* Internet Explorer 10-11 */
    color: #fff;
    opacity: 0.8;
    font-weight: 100;
}

.search-field {
    padding-left: 10px;
}
.search-box .search-by, .search-box .search-by-dropdown {
    background-color: #002760 !important;
    color: #fff !important;
}

.search-box .search-by-dropdown {
    margin-top: 0;
    border: none;
}

.search-box .search-by-dropdown li a {
    background-color: #002760;
    color: #fff;
}

.search-box .search-by-dropdown li a:hover,
.search-box .search-by-dropdown li a:active,
.search-box .search-by-dropdown li a:focus {
    background-color: #004D86 !important;
    color: #fff;
}

.search-options {
    position: absolute;
    top: 100%;
    right: 0;
    bottom: auto;
    left: auto;
    float: right;
    z-index: 1000;
    margin: 0 15px 0 15px;
    background-color: #002760;
    color: #fff;
}

/**/
/*========== PAGE ==========*/
.page-header {
    height: auto;
    padding: 10px 0 10px 0;
    border-bottom: none;
    margin: 0;
}

.page-header:after {
    clear: both;
    content: " ";
    display: block;
    height: 0;
}

.page-header .page-title {
    margin: 0;
    padding-top: 6px;
    display: inline-block;
}

.page-header .page-title-setting {
    display: inline-block;
    margin-left: 5px;
    padding-top: 10px;
}

.page-header .page-title-setting > a {
    padding: 5px 5px 5px 5px;
    opacity: 0.7;
}

.page-header .page-title-setting > a:hover,
.page-header .page-title-setting > a:active,
.page-header .page-title-setting > a:focus,
.page-header .page-title-setting.open > a {
    opacity: 1;
    background-color: #e4e4e4;
}

.page-header .sorting-options > button {
    padding: 0 5px 0 5px;
}

.page-content .page-title {
    margin-left: 0px;
}
/**/
/*========== NO APPS ==========*/
.no-apps {
    width: 100%;
}

.no-apps, .no-apps div, .no-apps p {
    background-color: #ffffff;
    text-align: center;
    cursor: help;
}

.no-apps p {
    cursor: help;
}

/**/
/*========== APP THUMBNAIL ITEMS==========*/
.app-thumbnail-ribbon {
    display: block;
    position: absolute;
    top: 0;
    height: 25%;
    color: #ffffff;
    z-index: 500;
    border: 1px solid rgb(255, 255, 255);
    border: 1px solid rgba(255, 255, 255, .5);
    /* for Safari */
    -webkit-background-clip: padding-box;
    /* for IE9+, Firefox 4+, Opera, Chrome */
    background-clip: padding-box;
    border-top-width: 0;
}

.app-thumbnail-type {
    display: block;
    position: absolute;
    bottom: 0;
    left: 0;
    height: 30%;
    color: #ffffff;
    z-index: 500;
    border: 1px solid rgb(255, 255, 255);
    border: 1px solid rgba(255, 255, 255, .5);
    /* for Safari */
    -webkit-background-clip: padding-box;
    /* for IE9+, Firefox 4+, Opera, Chrome */
    background-clip: padding-box;
    border-left-width: 0;
    border-bottom-width: 0;
    font-size: 2em;
}

.app-thumbnail-ribbon > span, .app-thumbnail-type > span {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    -o-transform: translate(-50%, -50%);
}

/**/
/*========== APP TILE ==========*/
.app-tile {
    background-color: #ffffff;
    margin-bottom: 20px;
}

.app-tile .summery {
    padding: 10px 0 10px 10px;
    max-width: 100%;
}

.app-tile .summery > h4 {
    margin-top: 5px;
    margin-bottom: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.app-tile .summery a h4 {
    margin-top: 5px;
    margin-bottom: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.app-tile .summery > h5 {
    margin-top: 0;
}

.app-tile .summery > h4, .app-tile .summery > h5, .app-tile .summery > p {
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    -ms-text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
}

.app-tile .summery > .more-menu {
    /*position: relative;*/
}

.app-tile .summery > .more-menu .more-menu-btn {
    float: right;
    height: auto;
    background-color: #F7F7F7;
    color: #838383;
    padding: 10px;
    margin-top: -10px;
}

.app-tile .summery > .more-menu.open .more-menu-btn {
    background-color: #D2D2D2;
}

.app-tile .summery > .more-menu .more-menu-btn:hover {
    background-color: #e4e4e4;
}

.app-tile .summery > .more-menu .more-menu-items {
    margin-top: 0;
}

/**/
/*========== APP DETAILS ==========*/
.app-details {
    background-color: #ffffff;
}

.app-details .summery > h4, .app-details .summery > p {
    white-space: nowrap;
    overflow: hidden;
}

.app-details .summery > .actions {
    margin: 10px 0 0 0;
}

.app-details .summery > .actions > a {
    margin: 5px 5px 5px 0;
}

.app-details .summery > .actions > a > i {
    padding-right: 5px;
}

.app-details-tabs {
    padding: 0 15px 0 15px;
}

.app-details-tabs > .nav-tabs > li > a {
    border-radius: 0;
}

.app-details-tabs > .nav-tabs > li.active > a,
.app-details-tabs > .nav-tabs > li.active > a:hover,
.app-details-tabs > .nav-tabs > li.active > a:focus,
.app-details-tabs > .nav-tabs > li.active > a:active {
    background-color: #fff;
    border: 1px solid #fff;
    border-radius: 0;
}

.app-details-tabs > .nav-tabs > li > a:hover,
.app-details-tabs > .nav-tabs > li > a:focus,
.app-details-tabs > .nav-tabs > li > a:active {
    background-color: #E8E8E8;
    border: 1px solid #E8E8E8;
    border-radius: 0;
}

.app-details-tabs > .tab-content {
    padding: 20px 17px;
    background-color: #fff;
}

.app-details-tabs > .tab-content > h3 {
    margin-top: 0;
}

/**/
/*========== DEFAULT THUMBNAIL & BANNER ==========*/
.default-thumbnail, .default-banner {
    color: #ffffff;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    -o-transform: translate(-50%, -50%);
}

/**/
/*========== RATING ==========*/
.rating > .one {
    opacity: 1;
}

.rating > .zero {
    opacity: 0.3;
}

/**/
/*========== UTILS ==========*/
a.disabled {
    cursor: default;
}

.absolute-center {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    -o-transform: translate(-50%, -50%);
}

.ratio-responsive-1by1 {
    padding: 100% 0 0 0;
}

.ratio-responsive-4by3 {
    padding: 75% 0 0 0;
}

.ratio-responsive-16by9 {
    padding: 56.25% 0 0 0;
}

.ratio-responsive-1by1, .ratio-responsive-4by3, .ratio-responsive-16by9 {
    width: 100%;
    position: relative;
}

.ratio-responsive-item {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    text-align: center;
}

.ratio-responsive-item:after {
    content: ' ';
    display: inline-block;
    vertical-align: middle;
    height: 100%;
}

.ratio-responsive-img > img {
    display: block;
    position: absolute;
    max-height: 100%;
    max-width: 100%;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto;
}

.hover-overlay {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: none;
    color: #FFF;
}

.hover-overlay-container:hover .hover-overlay {
    display: block;
    background: rgba(0, 0, 0, .6);
    cursor: pointer;
}

.hover-overlay-inactive-container:hover .hover-overlay {
    display: block;
    background: rgba(0, 0, 0, .6);
    cursor: not-allowed;
}

/**/
/*========== COLORS ==========*/
/*
focus : background 5% lighter, border 5% darker
hover: background 10% lighter, border 5% darker
active: background 10% lighter, border 5% darker
*/

/* subscribe  - main color: #603cba */
.background-color-subscribe {
    background-color: #603cba;
}

.background-color-on-hover-subscribe {
    background-color: transparent;
}

.background-color-on-hover-subscribe:hover {
    background-color: #603cba;
}

.btn-subscribe {
    color: #fff;
    background-color: #603cba;
    border-color: #603cba;
}

.btn-subscribe:focus,
.btn-subscribe.focus {
    color: #fff;
    background-color: #6D49C7;
    border-color: #532FAD;
}

.btn-subscribe:hover,
.btn-subscribe:active,
.btn-subscribe.active {
    color: #fff;
    background-color: #7A56D4;
    border-color: #532FAD;
}

/* favorite  - main color: #810847 */
.background-color-favorite {
    background-color: #810847;
}

.background-color-on-hover-favorite {
    background-color: transparent;
}

.background-color-on-hover-favorite:hover {
    background-color: #810847;
}

.btn-favorite {
    color: #fff;
    background-color: #810847;
    border-color: #810847;
}

.btn-favorite:focus,
.btn-favorite.focus {
    color: #fff;
    background-color: #8E1554;
    border-color: #75003B;
}

.btn-favorite:hover,
.btn-favorite:active,
.btn-favorite.active {
    color: #fff;
    background-color: #9B2261;
    border-color: #75003B;
}

/* all apps  - main color: #007A5F */
.background-color-all-apps {
    background-color: #007A5F;
}

.background-color-on-hover-all-apps {
    background-color: transparent;
}

.background-color-on-hover-all-apps:hover {
    background-color: #007A5F;
}

/* advertised  - main color: #C64700 */
.background-color-ad {
    background-color: #C64700;
}

.background-color-inactive {
    background-color: #C10D15;
}

.background-color-deprecated {
    background-color: #FFCC00;
}

/*========== MOBILE PLATFORM COLORS ========*/
.background-color-android {
    background-color: #a4c639;
}

.background-color-apple {
    background-color: #CCCCCC;
}

.background-color-windows {
    background-color: #00bcf2;
}
.background-color-webapps {
    background-color: #32a5f2;
}

/*=============== MOBILE ENTERPRISE INSTALL MODAL =========*/
.ep-install-modal {
    background: white !important;
    color: black !important;
}

.ep-install-modal .dataTables_filter label {
    margin-top: 5px;
    margin-bottom: 5px;
}
.ep-install-modal .dataTables_filter label input {
    margin: 0 0 0 0 !important;
    min-width: 258px !important;
}

.ep-install-modal .dataTables_info {
    float: none !important;
}

.ep-install-modal .dataTables_paginate {
    float: none !important;
}

.ep-install-modal .dataTables_paginate .paginate_enabled_next{
    color: #1b63ff;
    margin-left: 5px;
}

.ep-install-modal .dataTables_paginate .paginate_enabled_previous{
    color: #1b63ff;
}

.ep-install-modal .dataTables_paginate .paginate_disabled_next{
    margin-left: 5px;
}

.ep-install-modal .modal-header button {
    color: #000000;
}

#noty_center_layout_container {
    z-index: 100000001 !important;
}

/*=================MOBILE DEVICE INSTALL MODAL*==============*/
.modal-dialog-devices .pager li>a {
    background-color: transparent !important;
}
.modal-dialog-devices .thumbnail {
    background-color: transparent; !important;
    border: none !important;
}
/*---*/

/*===================HOME PAGE SEE MORE OPTION==============*/
.title {
    width: auto;
    padding: 0 10px;
    height: 50px;
    border-bottom: 3px solid #3a9ecf;
    float: left;
    padding-top: 14px;
    font-size: 20px;
    font-weight: 100;
}

.fav-app-title {
    width: auto;
    padding: 0 10px;
    height: 50px;
    border-bottom: 3px solid #3a9ecf;
    float: left;
    padding-top: 14px;
    font-size: 20px;
    font-weight: 100;
    margin-bottom: 10px;
}

.more {
    color:#000;
    float:right;
    background-image:url(../img/more-icon.png)!important;
    background-position:center left;
    background-repeat:no-repeat;
    text-transform:uppercase;
    padding:23px 3px 16px 36px !important
}

a.more:hover {
    color:#3a9ecf;
    text-decoration:none;
    background-image:url(../img/more-icon-hover-blue.png)!important;
    background-position:center left;
    background-repeat:no-repeat
}

a.more:active {
    background-color:none
}

a.more:focus {
    border:none
}
/*---*/

Refresh store. Store will look like below.