Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Know what is REST Web Services and how to work with it

This article will discuss about REST Web Services, it importance and its usage along with an example.

Introduction:

Representational State Transfer is the method used to create and in order to communicate with the web services. REST is a simple, light-weight and a fast web service as compared to the web services of WSDL and SOAP. REST is platform independent like the web services and language independent. Restful web API is a web API which is implemented with the use of HTTP and the principles of REST. The architecture of REST is designed in order to use the stateless protocol of HTTP.

Description:

The Universal Resource Identifiers (URI) in REST and are used through the header operations of HTTP. HTTP is the protocol used in REST. The HTTP requests are used in order to read and write data. The four methods which are GET, PUT, POST and DELETE are used in REST based web services. Therefore, the HTTP protocol is used by REST in order to perform the four operations which are create, read, update and delete (CRUD). In order to interact with the resource the standard methods of HTTP are used. The use of different methods present in HTTP protocol are used for the following purpose; GET method is used to retrieve the required resource, POST method is used to create the resource successfully, in order to update the resource PUT method is used and to remove the resource that is no more required can be removed using the method known as DELETE. The document and a process modeled using a unique URI and as a web resource. There are cases when a web browser serves as a client. The message exchange and communication can be done by using the formats including XML, HTML and others. JAX-RS is an API used in the programming language of java and the Restful web services are created and the communication is done by using it. The specification of JAX-RS is present in an enterprise edition of java and the distributed web services are designed and developed in java.

The REST service utilizes web standards. As the resource can be represented in multiple formats and is usually defined using the type of media and this type of media provides related information and the procedure about the requested media type generation. The client and server both can easily decide about the content type of resource using the standard methods available in this web service. The uniform interface is used in order to differentiate client from the server. As both client and server need to perform different tasks and their jobs are separate so the work being done in the server side will be the responsibility of server to handle the task and the work at the client side to request and abut its state will be the responsibility of client. There is a possibility that the servers and clients are developed independently depending on the type of work to be done. When the client waits for the response from server then it is at that particular point of time is at the state of transition. The @Path is used in order to define a web service that is Restful. The session state is held in the client side when the request from client side consists of related information which is important in order to service the request from server side.

The clients are able to cache the response on the World Wide Web (WWW) and the response from server side should be checked based on the type of task and prevent the clients in order to reuse the state and the well managed cache available will prevent the interactions between client and server respectively and this improves the performance in the system.

Overview:

The difference between SOAP and REST is that the SOAP is a protocol while REST is an architectural style. The REST design principle has the ability to establish the one to one mapping between CRUD operations and the methods of HTTP. The developers are allowed to create the new and advanced applications using REST as the API in this web service are platform independent and the standard libraries are available. This method is simple and the calls between different machines can be made using HTTP protocol. The REST-based applications use HTTP requests in order to post the data which is either to add or update, read the data and delete the non-required data. The uniform interface and stateless communication protocol is used in order to exchange the resources between the clients and servers. The client requests for some service to the server and in response the server first process the request and then sends the most appropriate and matched response to the client.

Importance:

The requests which are usually needed can be bookmarked and the usual and common responses can be cached easily. The administrator can track about the processes and the tasks which are on-going by looking at the headers of HTTP with this REST-based web service. This type of web service is identified by URIs and consists of the collection of the resources of web. The web applications are developed by the developers in a much easy manner as compared with SOAP. The Restful services of web works in an excellent manner on the Web and these are built in this way. The functionality and data in REST are usually accessed with the use of URIs. The resources which are basically identified using URIs provide an excellent global space of addressing in order to discover the service and the respective resource. In order to identify the resource as a URI in a successful manner, the standard principles available in REST are used and it contains a set of methods for performing different functionalities which includes accessing the resource and multiple representation of a resource in different formats. The web services are designed in an excellent manner with the use of REST style after having the complete understanding of it.

Screenshots:

In order to test the RESTful application developed using Netbeans, following screen appears. When the OK button is presses, it shows the output in the browser and that screen is also shown after this one.

Configure REST Test Client

Figure 1: Configure REST Test Client

This screen appears and asks to click on the URI given in front of WADL where this is written.

Test RESTful Web Services

Figure 2: Test RESTful Web Services

The following screen appears when the click in URI is done from the above screen.

The following screen is to show that the test is now being performed in the CustomerFacadeREST [restful.customer]. The left side of this screen shows the hierarchy of files available in REST_work application.

REST_work Web Application Interface

Figure 3: REST_work Web Application Interface

The following screen appears in the browser when the above test is performed in a successful manner.

Web Resources

Figure 4: Web Resources

Services of Database System used in JAX-RS

Figure 5: Services of Database System used in JAX-RS

The screen shows the table and database at the left end and the query is executed to see the data present in the Customer table.

Implementation & Code:

Listing 1 Customer.java:

package restful;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
@Entity
@Table(name = "CUSTOMER")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c"),
    @NamedQuery(name = "Customer.findById", query = "SELECT c FROM Customer c WHERE c.id = :id"),
    @NamedQuery(name = "Customer.findByCId", query = "SELECT c FROM Customer c WHERE c.cId = :cId"),
    @NamedQuery(name = "Customer.findByCName", query = "SELECT c FROM Customer c WHERE c.cName = :cName"),
    @NamedQuery(name = "Customer.findByCGender", query = "SELECT c FROM Customer c WHERE c.cGender = :cGender"),
    @NamedQuery(name = "Customer.findByCAddress", query = "SELECT c FROM Customer c WHERE c.cAddress = :cAddress"),
    @NamedQuery(name = "Customer.findByCDateofbirth", query = "SELECT c FROM Customer c WHERE c.cDateofbirth = :cDateofbirth"),
    @NamedQuery(name = "Customer.findByCProfession", query = "SELECT c FROM Customer c WHERE c.cProfession = :cProfession")})
public class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
    @Basic(optional = false)
    @NotNull
    @Column(name = "ID")
    private int id;
    @Id
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 20)
    @Column(name = "C_ID")
    private String cId;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 60)
    @Column(name = "C_NAME")
    private String cName;
    @Basic(optional = false)
    @NotNull
    @Column(name = "C_GENDER")
    private char cGender;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 400)
    @Column(name = "C_ADDRESS")
    private String cAddress;
    @Basic(optional = false)
    @NotNull
    @Column(name = "C_DATEOFBIRTH")
    @Temporal(TemporalType.DATE)
    private Date cDateofbirth;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 20)
    @Column(name = "C_PROFESSION")
    private String cProfession;
    public Customer() {
    }
    public Customer(String cId) {
        this.cId = cId;
    }
    public Customer(String cId, int id, String cName, char cGender, String cAddress, Date cDateofbirth, String cProfession) {
        this.cId = cId;
        this.id = id;
        this.cName = cName;
        this.cGender = cGender;
        this.cAddress = cAddress;
        this.cDateofbirth = cDateofbirth;
        this.cProfession = cProfession;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public String getCId() {
        return cId;
    }
    public void setCId(String cId) {
        this.cId = cId;
    }
    public String getCName() {
        return cName;
    }
    public void setCName(String cName) {
        this.cName = cName;
    }
    public char getCGender() {
        return cGender;
    }
    public void setCGender(char cGender) {
        this.cGender = cGender;
    }
    public String getCAddress() {
        return cAddress;
    }
    public void setCAddress(String cAddress) {
        this.cAddress = cAddress;
    }
    public Date getCDateofbirth() {
        return cDateofbirth;
    }
    public void setCDateofbirth(Date cDateofbirth) {
        this.cDateofbirth = cDateofbirth;
    }
    public String getCProfession() {
        return cProfession;
    }
    public void setCProfession(String cProfession) {
        this.cProfession = cProfession;
    }
    @Override
    public int hashCode() {
        int hash = 0;
        hash += (cId != null ? cId.hashCode() : 0);
        return hash;
    }
    @Override
    public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Customer)) {
            return false;
        }
        Customer other = (Customer) object;
        if ((this.cId == null && other.cId != null) || (this.cId != null && !this.cId.equals(other.cId))) {
            return false;
        }
        return true;
    }
    @Override
    public String toString() {
        return "restful.Customer[ cId=" + cId + " ]";
    } 
}

In this Customer.java class, the related columns from the table present in database system comes in this class and all the necessary information related to the data types and the functions including getters and setters appears in the form of separate functions when this REST application is created and all the other related important methods appear successfully.

Listing 2 AbstractFacade.java:

package restful.service;
import java.util.List;
import javax.persistence.EntityManager;
public abstract class AbstractFacade<T> {
    private Class<T> entityClass;

    public AbstractFacade(Class<T> entityClass) {
        this.entityClass = entityClass;
    }
    protected abstract EntityManager getEntityManager();
    public void create(T entity) {
        getEntityManager().persist(entity);
    }
    public void edit(T entity) {
        getEntityManager().merge(entity);
    }
    public void remove(T entity) {
        getEntityManager().remove(getEntityManager().merge(entity));
    }
    public T find(Object id) {
        return getEntityManager().find(entityClass, id);
    }
    public List<T> findAll() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return getEntityManager().createQuery(cq).getResultList();
    }
    public List<T> findRange(int[] range) {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(range[1] - range[0]);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }
    public int count() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    } 
}

This class AbstractFacade.java is generated automatically and it contains many abstract and public functions in the form of setters and getters which basically set the related values to be used in the application.

Listing 3 ApplicationConfig.java:

package restful.service;
import java.util.Set;
import javax.ws.rs.core.Application;
@javax.ws.rs.ApplicationPath("webresources")
public class ApplicationConfig extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
        // following code can be used to customize Jersey 1.x JSON provider:
        try {
            Class jacksonProvider = Class.forName("org.codehaus.jackson.jaxrs.JacksonJsonProvider");
            resources.add(jacksonProvider);
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        addRestResourceClasses(resources);
        return resources;
    }    /**
     * Do not modify addRestResourceClasses() method.
     * It is automatically re-generated by NetBeans REST support to populate
     * given list with all resources defined in the project.
     */
    private void addRestResourceClasses(Set<Class<?>> resources) {
        resources.add(restful.service.CustomerFacadeREST.class);
    } 
}

This ApplicationConfig.java class contains the configuration functions and this is also automatically generated. The web resources are utilized and REST application is tested successfully when this class is generated after the work of web application gets completed.

Listing 4 CustomerFacadeRest.java:

package restful.service;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import restful.Customer;
@Stateless
@Path("restful.customer")
public class CustomerFacadeREST extends AbstractFacade<Customer> {
    @PersistenceContext(unitName = "REST_workPU")
    private EntityManager em;

    public CustomerFacadeREST() {
        super(Customer.class);
    }

    @POST
    @Override
    @Consumes({"application/xml", "application/json"})
    public void create(Customer entity) {
        super.create(entity);
    }

    @PUT
    @Override
    @Consumes({"application/xml", "application/json"})
    public void edit(Customer entity) {
        super.edit(entity);
    }

    @DELETE
    @Path("{id}")
    public void remove(@PathParam("id") String id) {
        super.remove(super.find(id));
    }

    @GET
    @Path("{id}")
    @Produces({"application/xml", "application/json"})
    public Customer find(@PathParam("id") String id) {
        return super.find(id);
    }

    @GET
    @Override
    @Produces({"application/xml", "application/json"})
    public List<Customer> findAll() {
        return super.findAll();
    }

    @GET
    @Path("{from}/{to}")
    @Produces({"application/xml", "application/json"})
    public List<Customer> findRange(@PathParam("from") Integer from, @PathParam("to") Integer to) {
        return super.findRange(new int[]{from, to});
    }

    @GET
    @Path("count")
    @Produces("text/plain")
    public String countREST() {
        return String.valueOf(super.count());
    }
    @Override
    protected EntityManager getEntityManager() {
        return em;
    } 
}

This class generates the functionality of HTTP protocol which basically helps in getting the results and setting the results as this is the application in which database is utilized and the JAX-RS is used and accordingly the web application with respect to database is created. In this class, the HTTP methods perform the respective functionality.

Configuration Files

Listing 5 persistence.xml:

<?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="REST_workPU" transaction-type="JTA">
    <jta-data-source>REST_working</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties/>
  </persistence-unit>
</persistence>

This configuration files provides the related information of application and it shows that when there is a need to exclude the unused classes set the value to false and when there is a need to not exclude but include the unused classes then set the value to true. All related tags in this file perform this type of functionality and the application works with respect to the information present in configuration files.

Listing 6 glassfish-resources.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
    <jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.ClientDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="derby_net_Management_usePool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
        <property name="serverName" value="localhost"/>
        <property name="portNumber" value="1527"/>
        <property name="databaseName" value="Management"/>
        <property name="User" value="use"/>
        <property name="Password" value="system"/>
        <property name="URL" value="jdbc:derby://localhost:1527/Management"/>
        <property name="driverClass" value="org.apache.derby.jdbc.ClientDriver"/>
    </jdbc-connection-pool>
    <jdbc-resource enabled="true" jndi-name="REST_working" object-type="user" pool-name="derby_net_Management_usePool"/>
</resources>

This class shows the basics which the web application will access depending on the functionality including the port name, database name, the database connection string, username and password and all this kind of information is provided in this file and using this file, the web application access the database related operations and the REST utilizes the web resources accordingly to these provided configurations.

Client Application of Rest

Listing 7 Main_Class.java:

package restpackclient;
public class Main_Class {
    public static void main(String[] args){
        Rest_Client rc=new Rest_Client();
        String gresponse=rc.find_XML(String.class,"1");
        System.out.println("Response = "+gresponse);
        rc.close();
    }
}

This is a sample class created in order to check the access of the provided functionality in the main application. This application is basically created to test the web services in the side of client that either they work in a successful manner or not and if there any problem appears so it can be corrected in a successful manner.

Listing 8 Rest_Client.java:

package restpackclient;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
/**
 * Jersey REST client generated for REST resource:CustomerFacadeREST
 * [restful.customer]<br>
 * USAGE:
 * <pre>
 *        Rest_Client client = new Rest_Client();
 *        Object response = client.XXX(...);
 *        // do whatever with response
 *        client.close();
 * </pre>
*/
public class Rest_Client {
    private WebResource webResource;
    private Client client;
    private static final String BASE_URI = "https://localhost:8080/REST_work/webresources";

    public Rest_Client() {
        com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig(); // SSL configuration
        config.getProperties().put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new com.sun.jersey.client.urlconnection.HTTPSProperties(getHostnameVerifier(), getSSLContext()));
        client = Client.create(config);
        webResource = client.resource(BASE_URI).path("restful.customer");
    }

    public void remove(String id) throws UniformInterfaceException {
        webResource.path(java.text.MessageFormat.format("{0}", new Object[]{id})).delete();
    }

    public String countREST() throws UniformInterfaceException {
        WebResource resource = webResource;
        resource = resource.path("count");
        return resource.accept(javax.ws.rs.core.MediaType.TEXT_PLAIN).get(String.class);
    }

    public <T> T findAll_XML(Class<T> responseType) throws UniformInterfaceException {
        WebResource resource = webResource;
        return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
    }

    public <T> T findAll_JSON(Class<T> responseType) throws UniformInterfaceException {
        WebResource resource = webResource;
        return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType);
    }

    public void edit_XML(Object requestEntity) throws UniformInterfaceException {
        webResource.type(javax.ws.rs.core.MediaType.APPLICATION_XML).put(requestEntity);
    }

    public void edit_JSON(Object requestEntity) throws UniformInterfaceException {
        webResource.type(javax.ws.rs.core.MediaType.APPLICATION_JSON).put(requestEntity);
    }

    public void create_XML(Object requestEntity) throws UniformInterfaceException {
        webResource.type(javax.ws.rs.core.MediaType.APPLICATION_XML).post(requestEntity);
    }

    public void create_JSON(Object requestEntity) throws UniformInterfaceException {
        webResource.type(javax.ws.rs.core.MediaType.APPLICATION_JSON).post(requestEntity);
    }

    public <T> T findRange_XML(Class<T> responseType, String from, String to) throws UniformInterfaceException {
        WebResource resource = webResource;
        resource = resource.path(java.text.MessageFormat.format("{0}/{1}", new Object[]{from, to}));
        return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
    }

    public <T> T findRange_JSON(Class<T> responseType, String from, String to) throws UniformInterfaceException {
        WebResource resource = webResource;
        resource = resource.path(java.text.MessageFormat.format("{0}/{1}", new Object[]{from, to}));
        return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType);
    }

    public <T> T find_XML(Class<T> responseType, String id) throws UniformInterfaceException {
        WebResource resource = webResource;
        resource = resource.path(java.text.MessageFormat.format("{0}", new Object[]{id}));
        return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
    }

    public <T> T find_JSON(Class<T> responseType, String id) throws UniformInterfaceException {
        WebResource resource = webResource;
        resource = resource.path(java.text.MessageFormat.format("{0}", new Object[]{id}));
        return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType);
    }

    public void close() {
        client.destroy();
    }

    public void setUsernamePassword(String username, String password) {
        client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(username, password));
    }

    private HostnameVerifier getHostnameVerifier() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
                return true;
            }
        };
    }

    private SSLContext getSSLContext() {
        javax.net.ssl.TrustManager x509 = new javax.net.ssl.X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {
                return;
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {
                return;
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        SSLContext ctx = null;
        try {
            ctx = SSLContext.getInstance("SSL");
            ctx.init(null, new javax.net.ssl.TrustManager[]{x509}, null);
        } catch (java.security.GeneralSecurityException ex) {
        }
        return ctx;
    } 
}

This class is created when it is specified that this is the client application and it wants to use the web services from REST so at the time of creation of this application, the path of the exact application that has been tested and it is assured that the main application works successfully is provided then the functions for this class are generated and the output appears accordingly when everything in the application are fine and the configurations are handled in a proper manner.

After this you must be comfortable with making and understanding of Rest Webservices, hope you liked it, see you next time.

See also



My main area of specialization is Java and J2EE. I have worked on many international projects like Recorders,Websites,Crawlers etc.Also i am an Oracle Certified java professional as well as DB2 certified

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login