Tuesday, July 8, 2014

Secured file transfer mechanism

As an assignment for the Information Security and Cryptography module, we were assigned to implement a secured file transfer mechanism for large files. The utility must ensure confidentiality and integrity. It is not required to authenticate sender or provide non-repudiation  as the sender can be anonymous or pseudonymous and only the receiver identity needs to be known.

Since the task in the project is to create the secured file transfer mechanism, my approach was to use an encryption mechanism. Confidentiality can is achieved using the encryption mechanism and integrity can be achieved using through the hash values.

By using a public key cryptographic algorithm such as RSA to encrypt the the file I can safely transfer keys without violating the requirements. But as the number of bits encrypt must be smaller than the key size, encrypting a large file using RSA is not much efficient. On the other hand, using a symmetric algorithm such as AES (Advanced Encryption Standard) is much efficient when encrypting a large file, exchanging the keys is a real concern. So I combined the both mechanisms. I encrypted the file using AES and then encrypted the symmetric key with RSA. By doing so, I can efficiently encrypt the file and safely transfer the symmetric key through the network. To ensure the integrity, I calculate the hash value of file using MD5 hashing mechanism.

First the sender will calculate the hash value of the file using MD5 hashing. Then both the file and the hash value is encrypted using the AES. The key of the AES is then encrypted using the RSA with the public key of the receiver. Then the 3 values are sent to the receiver over the network. He will decrypt the AES symmetric key with his private key of him. Then he will decrypt the file and the hash value using the decrypted AES key. He will then calculate the hash value using MD5 and then compare with the hash value he received. If the hash values are matched with each other, he can be certain that the received file is not tempered with.
To implement this design I used java.security and javax.crypto packages. Simple socket connections are used to transfer files as the security is handled using encryption.

Note - It is assumed that the sender knows the public key of the receiver.

  • Enforcing the confidentiality.
The confidentiality means that the information is not exposed to the wrong people. Informations should only go to the intended people. In this case the file should be viewed only by the receiver. This is achieved using the encryption mechanism. Since the sender encrypts the data, any party other than the receiver (the person who who hold the private key) will be not be able to decrypt the file. Only the receiver can decrypt the AES key and so he is the only person who can decrypt the file. The channel is not secured, the confidentiality of the file is protected by the above method.
  • Enforcing the Integrity.
Integrity in this context means that the file should not be changed in the time between the sender sending the file and the receiver receiving the file. To ensure this property, I have used hashing. Before sending the file, the sender will compute the hash value of the file using the MD5 hashing and send it with the file (securely as the file is sent). Then the receiver will also compute the hash value of the decrypted file and compare those hash values to ensure that the file is not changed.
A hash function has the following properties.
    • It is relatively easy to compute the hash value of a given set of bit string (in this case the file)
    • It is computationally infeasible to calculate the inverse of the hash function. 
    • It has a negligible possibility to find two bit strings that has same hash values.
So because of these properties it is not possible to change the message so that the hash values will be the same.
  • Why use both symmetric key cryptography and public key cryptography.
As explained earlier, using public key cryptography, it is easy to exchange the keys. As the user does not have transmit any secret information over the network. But encrypting a very large file using this method is not efficient.
By using symmetric key cryptography, a large file can be efficiently encrypted but transmitting the secret information (which is the symmetric key) over the network is a big issue.
So by combining these two methods we can come up with an efficient mechanism that is efficient and is easy to exchange the key. Encrypt the file with symmetric key and encrypt the symmetric key with the public key of the receiver.
  • Why use MD5 hashing.
When the file size increases, MD5 hashing is efficient compared to the other mechanisms, as can be seen in figure bellow

  • Why use AES.
As you can see, AES is very efficient when encrypting large files compared to the algorithms such as DES. Though there are similarly efficient algorithms such as Blowfish, it is considered that the AES is relatively secure than that.


  • Why use RSA.
Well known public key cryptographic algorithms such as RSA and DSA considered to be almost same in security but RSA is considered better as it takes much less time to decrypt compared with DSA. But it does not have to be considered a problem as the data encrypted using public key cryptography is very small, only a 256bit key. So selecting RSA algorithms does not much affect the security or efficiency of the scheme.
  • Why use socket connection for the file transferring.
As the security is handled by encryption, the we do not have to bother about the security of the channel. As it is relatively simple to establish a socket connection, I used it as the mechanism of transferring the files

Source code can be found in

Building and running the code

To run the code you will need to replace the  local_policy.jar and US_export_policy.jar in JAVA_HOME/jre/lib/security with the files that can be downloaded from

To build the code, go to the project folder and run the command
mvn clean install
It will create a jar file file.transfer-1.0-jar-with-dependencies.jar in the target folder.

To generate the the RSA key pair run the command
java -jar file.transfer-1.0-jar-with-dependencies.jar -k <File to save public key> <File to save private key>

To receive the files and decrypt run the command
java -jar file.transfer-1.0-jar-with-dependencies.jar -r <Private key input file> <Location to save downloaded file> <Listening port>

To encrypt and send the file run the following command
java -jar file.transfer-1.0-jar-with-dependencies.jar -s <Public key input file> <File to send> <Host to connect> <Port to connect>


Saturday, June 28, 2014

Simple Web Crawler with crawler4j

A web crawler is an Internet Bot that systematically browser the web. The purpose of this browsing may vary, typically for web indexing or collecting a specific form of articles. Typical approach of the crawler is first some seed urls are added and the crawler will repeatedly browse all the links in the initial list and add the links to the list and so on.

Crawler4j is a java library that will extremely simplify the process of creating the web crawler. crawler4j library and its dependencies can be downloaded from the bellow page.

https://code.google.com/p/crawler4j/downloads/list

Basically you have to create the crawler by extending the WebCrawler and create a controller for that. In the crawler you have to override two basic method. They are,

  • shouldVisit - this method is called when visiting a given URl to determine whether it should be visited or not.
  • visit - this method is called when the contents of the given URL is downloaded successfully. You can easily access the URl and the contents of the page from this method.
Bellow is simple implementation of the shouldVisit method and it will access the pages in the same domain as the added seed and will avoid from css, js and media files. First you can create a pattern to avoid such types of pages.



1
Pattern filters = Pattern.compile(".*(\\.(css|js|bmp|gif|jpe?g" + "|png|tiff?|mid|mp2|mp3|mp4" + "|wav|avi|mov|mpeg|ram|m4v|pdf" + "|rm|smil|wmv|swf|wma|zip|rar|gz))$");

Now we can override the shouldVisit method.


1
2
3
4
5
@Override
public boolean shouldVisit(WebURL url) {
        String href = url.getURL().toLowerCase();
        return !filters.matcher(href).matches() && href.startsWith("http://www.lankadeepa.lk/");
}

You can override the visit method and print the details of the accessed pages.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Override
public void visit(Page page) {
        String url = page.getWebURL().getURL();
        System.out.println("Visited: " + url);

        if (page.getParseData() instanceof HtmlParseData) {
                HtmlParseData htmlParseData = (HtmlParseData) page.getParseData();
                String text = htmlParseData.getText();
                String html = htmlParseData.getHtml();
                List<WebURL> links = htmlParseData.getOutgoingUrls();

                System.out.println("Text length: " + text.length());
                System.out.println("Html length: " + html.length());
                System.out.println("Number of outgoing links: " + links.size());
        }

}

Now you have to create the controller class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Controller {

        public static void main(String[] args) throws Exception {
                String rootFolder = "data/crowler";
                int numberOfCrawlers = 1;

                CrawlConfig config = new CrawlConfig();
                config.setCrawlStorageFolder(rootFolder);
                config.setMaxPagesToFetch(4);
                config.setPolitenessDelay(1000);
                config.setMaxDepthOfCrawling(10);
                config.setProxyHost("cache.mrt.ac.lk");
                config.setProxyPort(3128);

                PageFetcher pageFetcher = new PageFetcher(config);
                RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
                RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);
                CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);

                controller.addSeed("http://www.lankadeepa.lk/");
                controller.start(Crawler.class, numberOfCrawlers);

        }

}

From the controller class you can control the number of crawlers created, maximum number of pages to visit, maximum depth of crawling and add proxy settings of needed. And from here you can add the seeds of crawling. Seeds is the initial list of crawling. When the pages are visited, all the links in the are added to the list and list will grow eventually.

Now you can start crawling. If you want to access any details of the crawled pages, it can be easily done in the visit method. As you can see it contains all the details of the web page including the html of the page. If you want to extract and details from the page, this is the place to do it.

Saturday, May 17, 2014

Test Java Database Connection Using JDBC Driver

In any software, if a user is needed to add a database connection details, a test connection button will come in handy. In this blog, I'm trying to test a database connection using a JDBC driver. First we need to download a driver. From the bellow link you can download the MySql driver.

http://dev.mysql.com/downloads/connector/j/

When we are working with the driver, first it need to be added to the CLASSPATH. It can be done as described in,
http://dev.mysql.com/doc/connector-j/en/connector-j-installing.html

For just test purposes, adding it to the build path of the IDE will be sufficient.

If we need to do some operation on the database using the driver, first we need to create a connection to the database. For this you will need driver name (for MySql, driver name is com.mysql.jdbc.Driver ), database url (for MySql database running in the localhost, the url will be jdbc:mysql://localhost:3306/db_name ), username and the password. When creating the connection, if the driver couldn't establish a valid connection, it will throw an exception depending on the course. We can easily use it to test the connection.

First we need to create a custom exception.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class DataSourceException extends Exception {
 /**
  * 
  */
 private static final long serialVersionUID = 1L;

 public DataSourceException(String message) {
  super(message);
 }

 public DataSourceException(String message, Throwable throwable) {
  super(message, throwable);
 }
}

Now we can create a method to get the connection. This method will return a connection to the database. But to test the connection we are not much interested about the connection. We are checking whether it is throwing any exception or not and if it is the error message. Bellow method can be used to get the connection.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Connection getConnection(String driver, String url, String uname,
   String pwd) throws DataSourceException {
  Connection conn = null;

  try {
   Class.forName(driver).newInstance();
   conn = DriverManager.getConnection(url + "?user=" + uname + "&password=" + pwd);

  } catch (SQLException ex) {
   throw new DataSourceException(
     "Error establishing data source connection: "
       + ex.getMessage(), ex);
  } catch (InstantiationException e) {
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   e.printStackTrace();
  } catch (ClassNotFoundException e) {
   throw new DataSourceException("Error establishing data source connection: " + e.getMessage(), e);
  }

  return conn;
 }

Now we want to check if the connection is throwing any exception. If it is not, the connection is healthy. But if it is not, the connection is not valid and we can find the cause from the error message of the exception. test connection method will look like this,


1
2
3
4
5
6
7
8
9
public boolean testConnection(String driver, String url, String uname, String pwd) throws DataSourceException {
  try {
   getConnection(driver, url, uname, pwd);
  } catch (DataSourceException e) {
   throw e;
  }

  return true;
}

So now we can check the validity of the database connection.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
String driverName = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/driver_test";

try {
 boolean valiedConnection = testConnection(driverName, url, "maduranga", "maduranga");
 if (valiedConnection) {
  System.out.println("Connection is Healthy");
 }
}catch (DataSourceException e) {
 System.out.println(e.getMessage());
}


The error messages will be shown like bellow.
  • For a false driver name - Error establishing data source connection: com.mysql.jdbc.Drive
  • For a false connection url - Error establishing data source connection: Unknown database 'userstor'
  • For a false username - Error establishing data source connection: Access denied for user 'madurang'@'localhost' (using password: YES)
  • For a false password - Error establishing data source connection: Access denied for user 'maduranga'@'localhost' (using password: YES)
  • For correct data - Connection is Healthy
As the error messages are really meaningful, this will help a great deal for the user to add the database connection.

Thursday, April 24, 2014

Create a client for WSO2 Admin Service

WSO2 provide a middleware stack that can be used to fulfill any middleware requirement. All the functionality can be controlled via the management console. There is a specific web service created for every feature in the product stack. Normally every component is consist of a back end component, a service stub and a front end component. Front end component is a client for the back end web service. These web services are called Admin Services.
If you get a WSO2 Identity Server, normally the WSDL of these Admin Services are hidden. You can go to IS_HOME/repository/conf/carbon.xml and change HideAdminServiceWSDLs to false (restart the server if it is already running), you can see the WSDL if your required Admin Service. For example you will be able to see the WSDL of the below admin service.

https://localhost:9443/services/StatisticsAdmin?wsdl

Now we are going to create a client for the above mentioned admin service. First create a maven project. Then add the following repository to the pom.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<repositories>
 <repository>
  <id>wso2-nexus</id>
  <name>WSO2 internal Repository</name>
  <url>http://maven.wso2.org/nexus/content/groups/wso2-public/</url>
  <releases>
   <enabled>true</enabled>
   <updatePolicy>daily</updatePolicy>
   <checksumPolicy>ignore</checksumPolicy>
  </releases>
 </repository>
</repositories>

<pluginRepositories>
 <pluginRepository>
  <id>central</id>
  <url>http://repo1.maven.org/maven2</url>
 </pluginRepository>
 <pluginRepository>
  <id>wso2-maven2-repository-1</id>
  <url>http://dist.wso2.org/maven2</url>
 </pluginRepository>
 <pluginRepository>
  <id>wso2-maven2-repository-2</id>
  <url>http://dist.wso2.org/snapshots/maven2</url>
 </pluginRepository>
</pluginRepositories>

Then add the following dependencies.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>3.8.1</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.wso2.carbon</groupId>
  <artifactId>org.wso2.carbon.authenticator.stub</artifactId>
  <version>4.0.0</version>
 </dependency>
 <dependency>
  <groupId>org.apache.axis2.wso2</groupId>
  <artifactId>axis2-client</artifactId>
  <version>1.6.1.wso2v10</version>
 </dependency>
 <dependency>
  <groupId>org.wso2.carbon</groupId>
  <artifactId>org.wso2.carbon.statistics.stub</artifactId>
  <version>4.0.0</version>
 </dependency>
</dependencies>

To get the service from a admin service first we need to authenticate. To do this there is a separate admin service.

Following code will handle the login process and will return a cookie.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import org.apache.axis2.AxisFault; 
import org.apache.axis2.transport.http.HTTPConstants; 
import org.wso2.carbon.authenticator.stub.AuthenticationAdminStub; 
import org.wso2.carbon.authenticator.stub.LoginAuthenticationExceptionException; 
import org.wso2.carbon.authenticator.stub.LogoutAuthenticationExceptionException; 
import org.apache.axis2.context.ServiceContext; 
import java.rmi.RemoteException; 

public class LoginAdminServiceClient { 
  private final String serviceName = "AuthenticationAdmin"; 
    private AuthenticationAdminStub authenticationAdminStub; 
    private String endPoint; 

    public LoginAdminServiceClient(String backEndUrl) throws AxisFault { 
      this.endPoint = backEndUrl + "/services/" + serviceName; 
      authenticationAdminStub = new AuthenticationAdminStub(endPoint); 
    } 

    public String authenticate(String userName, String password) throws RemoteException, 
                                      LoginAuthenticationExceptionException { 

      String sessionCookie = null; 

      if (authenticationAdminStub.login(userName, password, "localhost")) { 
        System.out.println("Login Successful"); 

        ServiceContext serviceContext = authenticationAdminStub. 
            _getServiceClient().getLastOperationContext().getServiceContext(); 
        sessionCookie = (String) serviceContext.getProperty(HTTPConstants.COOKIE_STRING); 
        System.out.println(sessionCookie); 
      } 

      return sessionCookie; 
    } 

    public void logOut() throws RemoteException, LogoutAuthenticationExceptionException { 
      authenticationAdminStub.logout(); 
      System.out.println("Logout");
    } 
}

Now we can access the required admin service. Following class will handle the necessary steps.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.apache.axis2.AxisFault; 
import org.apache.axis2.client.Options; 
import org.apache.axis2.client.ServiceClient;
import org.wso2.carbon.statistics.stub.StatisticsAdminStub;
import org.wso2.carbon.statistics.stub.types.carbon.SystemStatistics;

import java.rmi.RemoteException; 
  
 public class StatisticsAdminClient { 
   private final String serviceName = "StatisticsAdmin"; 
   private StatisticsAdminStub statisticsAdminStub; 
   private String endPoint; 
  
   public StatisticsAdminClient(String backEndUrl, String sessionCookie) throws AxisFault { 
     this.endPoint = backEndUrl + "/services/" + serviceName; 
     statisticsAdminStub = new StatisticsAdminStub(endPoint); 
     //Authenticate Your stub from sessionCooke 
     ServiceClient serviceClient; 
     Options option; 
  
     serviceClient = statisticsAdminStub._getServiceClient(); 
     option = serviceClient.getOptions(); 
     option.setManageSession(true); 
     option.setProperty(org.apache.axis2.transport.http.HTTPConstants.COOKIE_STRING, sessionCookie); 
   } 
  
   public SystemStatistics getSystemStatistics() throws RemoteException { 
     return statisticsAdminStub.getSystemStatistics(); 
   } 
 }

To access the required admin service we need to present the obtained cookie. But you will face some problems as the jvm does not accept the security certificates presented by the server. So we need to tell the jvm that the security certificate presented by the server is trusted. We can do this by setting a system property. You will need to change the property to point to the correct jks file in the server.
You can change the backEndUrl to point to the correct server url.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.wso2.carbon.authenticator.stub.LoginAuthenticationExceptionException;
import org.wso2.carbon.authenticator.stub.LogoutAuthenticationExceptionException;
import org.wso2.carbon.identity.entitlement.stub.EntitlementServiceException;
import org.wso2.carbon.statistics.stub.types.carbon.SystemStatistics;
import java.rmi.RemoteException;

public class GetSystemStatistics {
 public static void main(String[] args)
   throws LoginAuthenticationExceptionException,
   LogoutAuthenticationExceptionException,
   EntitlementServiceException, RemoteException{
  System.setProperty(
    "javax.net.ssl.trustStore",
    "/home/maduranga/WSO2/IS/21-04-2014/wso2is-5.0.0/repository/resources/security/wso2carbon.jks");
  System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
  System.setProperty("javax.net.ssl.t" + "rustStoreType", "JKS");
  String backEndUrl = "https://localhost:9443";

  LoginAdminServiceClient login = new LoginAdminServiceClient(backEndUrl);
  String session = login.authenticate("admin", "admin");
  StatisticsAdminClient statisticsAdminClient = new StatisticsAdminClient(
    backEndUrl, session);
  SystemStatistics stat = statisticsAdminClient.getSystemStatistics();

  System.out.println("Host :" + stat.getServerName());
  System.out.println("Server Start Time :" + stat.getServerStartTime());
  System.out.println("System Up Time :" + stat.getSystemUpTime());
  System.out.print(stat.getUsedMemory().getValue()
    + stat.getUsedMemory().getUnit() + " of ");
  System.out.println(stat.getTotalMemory().getValue()
    + stat.getTotalMemory().getUnit() + " memory used");

  login.logOut();
 }
}

Now you can see the statistics of the server. By doing necessary changes you can access any admin service in the server.

Tuesday, April 15, 2014

Serialize and de-serialize objects with JAXB

Java Architecture for XML Binding (JAXB) is used to serialize java objects to XML (marshal) and to de-serialize XML back to java object (unmarshal). Here I am going to demonstrate how to marshal and unmarshal a simple java object using two methods.

First method is for the classes that some annotations can be added. Bellow is the class we are using to make objects which we are going to serialize.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "student")
class Student {
 private String name;
 private int age;
 
 public Student() {
 }

  public void setName(String name) {
  this.name = name;
 }

  public void setAge(int age) {
  this.age = age;
 }

  public String getName() {
  return name;
 }

  public int getAge() {
  return age;
 }
}

As you can see, a annotation is added to tell that this is a XML root element, which is used in serializing the object.

From the bellow method, you can serialize a object created by the above class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public String marshal(Student student){
  StringWriter writer = new StringWriter();
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Marshaller m = context.createMarshaller();
   m.marshal(student, writer);
   return writer.toString();
   
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

This method will return a XML representation of a student object. Bellow method will return the Student object back from a serialized student object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static Student unMarshal(String input){
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Unmarshaller m = context.createUnmarshaller();
   return (Student)m.unmarshal(new StringReader(input));
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

But if you want to serialize a object of which class signature cannot be changed and it does not have the annotations provided in the class above. Lets see how to use JAXB with the objects of a class like bellow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Student {
 private String name;
 private int age;
 
 public Student() {
 }

  public void setName(String name) {
  this.name = name;
 }

  public void setAge(int age) {
  this.age = age;
 }

  public String getName() {
  return name;
 }

  public int getAge() {
  return age;
 }
}

Bellow code can be used to serialize a Student object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public String marshal(Student student){
  StringWriter writer = new StringWriter();
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Marshaller m = context.createMarshaller();
   
         m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         JAXBElement<Student> rootElement = new JAXBElement<Student>(new QName("student"), Student.class, student);
   m.marshal(rootElement, writer);
   return writer.toString();
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

It will return a XML string as in the previous case. And the bellow code can be used to de-serialize a student object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public Student unMarshal(String input){
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Unmarshaller m = context.createUnmarshaller();
   StreamSource source = new StreamSource(new StringReader(input));
         return m.unmarshal(source, Student.class).getValue();
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

In this method we have provided appropriate JAXB mapped class to hold node's XML data to the unmarshal method. As the unmarshal method which accepts a class parameter does not accept a just a StringReader, we have made a StreamSource using that StringReader. You can will see that there are lots of unmarshal methods overloaded and you will have to use a suitable one according to your requirements.

Thursday, March 27, 2014

Overiding a URL click in android WebView

If you have used the android WebView, you are quite familiar that the if you click on a url on a web page, the link is opened in the default web browser of the device. But this can be easily overridden using the bellow code.

WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    webView = (WebView) findViewById(R.id.webViewPage);
    ........
    webView.setWebViewClient(new WebViewClient() {
        @SuppressLint("NewApi")
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            //page loading logic here
            return true;
        }
    });
}

If you are using the android API prior to API 3, you could have done it easily using 
webview.loadUrl(url);

But if you are using an API level 3 or a later version (which will be the case in the most of the times), loading the url will be bit trickier. To make the application more interactive, it is mandatory to use a separate thread to load the page. If it is done in the same thread which is used to handle user interactions, user interface may not respond to the user. So it cannot be easily done using the loadUrl method. There are several method to do this. First and the worst way of doing it is using a method called StrictMode.

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

If you add this to the onCreate method you will not face problems when running the app. But I guarantee that the application wont be very interactive depending on the internet connection you are having.

To properly do this you will have to use a AsyncTask. AsyncTask create a separate thread which can be used for data loading without making the application not interactive. Below here explains how to create a AsyncTask.

//Create a separate thread to load the web page
private class LoadPageTask extends AsyncTask<String, HtmlPage, HtmlPage> {
    protected HtmlPage doInBackground(String... urls) {
        //page loading logic here.
    }

    protected void onPostExecute(HtmlPage page) {

        //things to do after completing the task defined in doInBackground method.
    }

    @Override

    protected void onPreExecute() {
        //things to do prior to the task defined in doInBackground method.
    }
}

You can use different methods to load the page to the WebView. In my case I used the HttpPost method to get the input stream of the web page and and then parse it using a HTML parser and loading the page to the WebView using the same HTML parser.