Today I am going to show you how to setup a Maven Project with several modules including Spring Context configuration in conjunction with Tapestry 5 and Hibernate.
Agenda
- Setup Maven Parent Project
- Create the UI Application Project
- Create the Service Modules
- Create the Repository Modules
- Spring contexts from repositories, over services to the application ui
- Test the application
- Review
- Pitfalls
- Project Download
The following technology will be used:
- Eclipse Indigo JEE 3.7 SR2
- Maven 3
- Spring 3.1.0.RELEASE
- Spring Data 1.1.0.RELEASE
- Tapestry 5.3.3
- Hibernate 4.0.1.Final
- HSQLDB 2.2.8
- TestNG 5.14.9
- Jetty 7.6.4 (embedded)
The following steps have to be taken in order to get a fully functional and ready to start coding Project:
- Create Parent Maven Project with used dependencies
- Create UI Project where we will use Tapestry as Frontend Framework
- Create Services fpr repository access
- Create Repositories for Datasource Access
- Check dependencies
- Create Spring Application Contexts
- Connect UI with Services
- Build Tests for Spring and UNit Test for Tapestry Page
1. Setup Maven Parent Project
At first we have to define a parent project with all needed dependencies for the child projects. It will look like the following content from the pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0. xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.boehme.app</groupId> <artifactId>mm-base-parent</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>mm-base Module Reactor</name> <description>Spring, Tapestry, MongoDB or MySQL</description> <modules> <module>application/mm-ui</module> <module>services/mm-data-service</module> <module>services/mm-user-service</module> <module>repositories/mm-data-repository</module> <module>repositories/mm-user-repository</module> </modules> <dependencyManagement> <dependencies> <!-- Too set up an application with a database, change the artifactId below to tapestry-hibernate, and add a dependency on your JDBC driver. You'll also need to add Hibernate configuration files, such as hibernate.cfg.xml. --> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-core</artifactId> <version>${tapestry-release-version}</version> </dependency> <!-- This adds automatic compression of JavaScript and CSS when in production mode. --> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-yuicompressor</artifactId> <version>${tapestry-release-version}</version> </dependency> <!-- Uncomment this to add support for file uploads: <dependency> <groupId>org.apache. tapestry</groupId> <artifactId>tapestry-upload</artifactId> <version>${tapestry-release-version}< /version> </dependency> --> <!-- A dependency on either JUnit or TestNG is required, or the surefire plugin (which runs the tests) will fail, preventing Maven from packaging the WAR. Tapestry includes a large number of testing facilities designed for use with TestNG (http://testng.org/), so it's recommended. --> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>${testng-release-version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-test</artifactId> <version>${tapestry-release-version}</version> <scope>test</scope> </dependency> <!-- Provided by the servlet container, but sometimes referenced in the application code. --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>${servlet-api-release-version}</version> <scope>provided</scope> </dependency> <!-- Provide dependency to the Tapestry javadoc taglet which replaces the Maven component report --> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-javadoc</artifactId> <version>${tapestry-release-version}</version> <scope>provided</scope> </dependency> <!-- mm-dependencies --> <dependency> <groupId>de.boehme.service</groupId> <artifactId>mm-data-service</artifactId> <version>${mm-project-version}</version> </dependency> <dependency> <groupId>de.boehme.service</groupId> <artifactId>mm-user-service</artifactId> <version>${mm-project-version}</version> </dependency> <dependency> <groupId>de.boehme.repository</groupId> <artifactId>mm-data-repository</artifactId> <version>${mm-project-version}</version> </dependency> <dependency> <groupId>de.boehme.repository</groupId> <artifactId>mm-user-repository</artifactId> <version>${mm-project-version}</version> </dependency> <!-- SPRING --> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-spring</artifactId> <version>${tapestry-release-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework.version}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${org.springframework.data.version}</version> </dependency> <!-- DB Dependencies --> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version> ${org.hibernate.hibernate-entitymanager.version} </version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </pluginManagement> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> <repositories> <repository> <id>codehaus.snapshots</id> <url>http://snapshots.repository.codehaus.org</url> </repository> <repository> <id>OpenQA_Release</id> <name>OpenQA Release Repository</name> <url>http://archiva.openqa.org/repository/releases/</url> </repository> <!-- This repository is only needed when the Tapestry version is a preview release, rather than a final release. --> <repository> <id>apache-staging</id> <url>https://repository.apache.org/content/groups/staging/</url> </repository> </repositories> <properties> <tapestry-release-version>5.3.3</tapestry-release-version> <servlet-api-release-version>2.5</servlet-api-release-version> <testng-release-version>5.14.9</testng-release-version> <easymock-release-version>3.0</easymock-release-version> <jetty-version>7.6.4.v20120524</jetty-version> <mm-project-version>0.0.1-SNAPSHOT</mm-project-version> <org.springframework.version>3.1.0.RELEASE</org.springframework.version> <org.springframework.data.version>1.1.0.RELEASE</org.springframework.data.version> <hsqldb.version>2.2.8</hsqldb.version> <org.hibernate.hibernate-entitymanager.version>4.0.1.Final</org.hibernate.hibernate- entitymanager.version> </properties> </project>
Now we can add some directories for our ui application, the services and the repositories. Review the project structure image from the beginning of this post.
2. Create the UI Application Project
Now we can add ther first module to our project. This will be the application containing the UI Components. Therefore we create a new Maven Project from an archetype, called org.apache.tapestry quickstart for version 5.3.3. If you dont find this archetype in your catalogs you have to define a new remote catalog. (Go to Eclipse-->Window --> Preferences --> Maven --> Archetypes --> Add remote catalog). The address to be used is http://tapestry.apache.org .
The POM will look like the following:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <relativePath>../../pom.xml</relativePath> <artifactId>mm-base-parent</artifactId> <groupId>de.boehme.app</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>mm-ui</artifactId> <packaging>war</packaging> <name>mm-ui Tapestry 5 Application</name> <dependencies> <!-- Too set up an application with a database, change the artifactId below to tapestry-hibernate, and add a dependency on your JDBC driver. You'll also need to add Hibernate configuration files, such as hibernate.cfg.xml. --> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-core</artifactId> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <!-- Provided by the servlet container, but sometimes referenced in the application code. --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- mm-dependencies --> <dependency> <groupId>de.boehme.service</groupId> <artifactId>mm-data-service</artifactId> </dependency> <dependency> <groupId>de.boehme.service</groupId> <artifactId>mm-user-service</artifactId> </dependency> <!-- SPRING--> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-spring</artifactId> </dependency> </dependencies> <build> <finalName>mm-ui</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.7.2</version> <configuration> <systemPropertyVariables> <tapestry.execution-mode>Qa</tapestry.execution-mode> </systemPropertyVariables> </configuration> </plugin> <!-- Run the application using "mvn jetty:run" --> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>${jetty-version}</version> <configuration> <connectors> <connector implementation="org.eclipse.jetty.server.nio. SelectChannelConnector"> <port>8080</port> </connector> </connectors> <systemProperties> <systemProperty> <name>tapestry.execution-mode</name> <value>development</value> </systemProperty> </systemProperties> </configuration> </plugin> </plugins> </build> </project>
The important things to notice are that we will use tapestry core, TestNG and spring-test dependencies for integration and unit testing. Furthermore we defined the dependencies for our own service modules, which we will create now. To start the whole application a jetty plugin delivers an embedded instance of the Jetty Servlet Container. This is very useful as you can start the server from within Eclipse with just a simple maven goal jetty:run.
3. Create the Service Modules
Next step will be the adding of our service modules. The DataService is just a template for other services and should give a simple idea how to create and use services in layered architecture projects. The real implementation represents the User Service which will be shown more in detail:
The POM file looks like the following:
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0. xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <relativePath>../../pom.xml</relativePath> <artifactId>mm-base-parent</artifactId> <groupId>de.boehme.app</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>de.boehme.service</groupId> <artifactId>mm-user-service</artifactId> <name>mm-user-service</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>de.boehme.repository</groupId> <artifactId>mm-user-repository</artifactId> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-spring</artifactId> </dependency> </dependencies> </project>
The service should only "know" the repository as dependency. Of course we need spring which comes through the tapestry-spring dependency.
To use this service we have to define an API Interface and the corresponding implementation class. The main purpose of the user service is to create User and retrieve all User from a database.
User.java Model:
@Entity @Table(name="USER") public class User implements Serializable{ /** * */ private static final long serialVersionUID = 4416477374601590502L; public User() { } public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Id @GeneratedValue private Long id; @Column(name="FIRSTNAME") private String firstName; @Column(name="LASTNAME") private String lastName;
The User Model basically consists of an Id and a First- and Lastname attribute.
API Interface IUserService.java
public interface IUserService { void createUser(User user); List<User> getAllUser(); }
The interface defines two methods. One for storing User into the database and one for retrieving all saved Users.
Implementation Class UserService.java
@Service public class UserService implements IUserService{ @Autowired private UserRepository repository; @Override public void createUser(User user) { repository.save(user); } @Override public List<User> getAllUser() { return repository.findAll(); } }
The implementing class is marked with the @Service annotation. This is needed in order to let the UI find this class as a bean. To actually save User into the db we acquire access to the UserRepository via injecting it here.
4. Create the Repository Modules
The repository modules are used to directly access datasources (databases etc.). As we also have here a template repository called DataRepository we will conecntrate on a real implementation the UserRepository.
The UserRepository has some dependencies to hibernate, spring-data, spring-orm and a hsqldb. The POM looks like the following:
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0. xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <relativePath>../../pom.xml</relativePath> <artifactId>mm-base-parent</artifactId> <groupId>de.boehme.app</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>de.boehme.repository</groupId> <artifactId>mm-user-repository</artifactId> <name>mm-user-repository</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tapestry</groupId> <artifactId>tapestry-spring</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> </dependency> </dependencies> </project>
We will take advantage of an embedded hsqldb, that delivers us the needs to start coding without thinking of setting up database environments etc.
Through the usage of spring-data we can just define an interface UserRepository.java that extends JpaRepository which in turn delivers us at runtime all needed CRUD operations.
@Repository public interface UserRepository extends JpaRepository<User, Long>{ }
The interface is marked with the @Repository stereotype annotation from Spring, so that it is recognized as bean and we can inject it into other components like our UserService seen before.
Now that we have all different parts we can wire up the projects with spring configurations.
5. Spring contexts from repositories, over services to the application ui
The repository need to access a datasource. Therefore we have to define our so called jpa-context.xml.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:xsi="http://www.w3. org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0. xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd "> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="packagesToScan"> <list> <value>de/boehme/repository/*/model/</value> </list> </property> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> <property name="database" value="HSQL" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> <prop key="hibernate.format_sql">true</prop> </props> </property> </bean> <jdbc:embedded-database id="dataSource" type="HSQL" /> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> </bean> </beans>
As you can see we use an embedded HSQL db. That means we create a new in-memory databse at each application context start-up. We also define an EntityManagerFactory, that should search for all our models (currently only one: User Model) under a given package via the property "packagesToScan". The automatic generation of then entities is set via the jpa property "hibernate.hbm2ddl.auto". The Transaction Manager is mandatory, although not used in this applciation.
To use the features of spring-data we have to define another context file that searches our packages for repositories.
repositiories.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework. org/schema/data/jpa" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <!-- Spring Data --> <repositories base-package="de.boehme.repository.user" /> </beans:beans>
Putting it all together we define a general repo-context.xml file that imports the repositories.xml and jpa-context.xml files which can be imported by the UserService later.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="de.boehme.repository.user" /> <import resource="classpath:/de/boehme/repository/user/repositories.xml"/> <import resource="classpath:/de/boehme/repository/user/jpa-context.xml"/> </beans>
Service spring context file
The service basically just tells spring to look for annotated class under the given package. It also imports the spring context we earlier defined in the repository.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="de.boehme.service.user" /> <import resource="classpath:/de/boehme/repository/user/repo-context.xml"/> </beans>
Application UI spring context
The application needs to get a reference to the UserService therefore we have to import the service-context. As we are using an embedded database we like to have data ready to be used at startup of the application. So we need to define here the context:component-scan that searches under the given package for our ApplicationContextListener (you can look into the attached sources for more details). Therefore we will use a special bean that listens for the spring context and inserts two Users into the database for us.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="de.boehme.app.ui" /> <import resource="classpath:/de/boehme/service/user/service-context.xml"/> <import resource="classpath:/de/boehme/service/data/service-context.xml"/> <!-- <import resource="classpath:/de/boehme/app/ui/spring-profiles.xml"/> --> </beans>
Now we are ready to use the services and start coding a simple Overview Page that displays all users in a grid.
Overview.java
public class Overview { @org.apache.tapestry5.ioc.annotations.Inject private IUserService userService; @Property private List<User> users; void onActivate() { users = userService.getAllUser(); } }
As we use Tapestry we have to use their @Inject annotation in order to get the Service correctly injected. The only thing left to do is to fetch all users on page loading.
6. Test the application
So far we just have set-up the application, the services and the repositories with their maven poms and the spring contexts.
Now we want to test the application. At first a simple integration test that checks whether the Service dependencies get correctly injected.
SpringContextTest.java
@ContextConfiguration("classpath:/de/boehme/app/ui/applicationContext.xml") public class SpringContextTest extends AbstractTestNGSpringContextTests { @javax.inject.Inject private IDataService dataService; @javax.inject.Inject private IUserService userService; @Test public void startUp(){ assertNotNull(dataService); assertNotNull(userService); } }
This test starts the applicationContext from the UI application and checks if all dependencies are injected and not null. This test should pass.
To test Tapestry pages, we can rely on the tapestry-test module which is earlier defined as dependency.
You can look into the attached sources for more details.
7. Review
We have created a multi-module application that follows a 3-Tier Architecture. It consists of an UI Application module, two services and two repositories. For rapid development we are using an embedded HSQL database. The project setup can be used as simple template for other purposes, such as any JEE application.
8. Pitfalls
If you try to run the project inside of Eclipse you have to set up the maven goals correctly:
That means:
Maven clean install--> Goals:clean install
and set "Resolve workspace artifacts" to true
Jetty Start--> Goals:jetty:run
and set "Resolve workspace artifacts" to true
9. Project Download
You can download the whole project with all its sources under the following URL:
mm-base-parent.zip
hi, Looks very informative and I like the way you explained with example.
ReplyDeleteHowever,I am not able to download the source.
same here... could not d/l the source
ReplyDeleteI was able to
ReplyDeletesame here... could not d/l the source
ReplyDeletevery goooooooooooooooooooood
ReplyDeleteBUTTT I am not able to download the source :(
Great post. Happy to visit your blog. Thanks for sharing.
ReplyDeletehtml training in chennai
Hibernate Training Institutes in Chennai | Hibernate Online Training | Hibernate Training in Chennai
ReplyDeleteGreat article. Thanks for sharing such a useful post.
ReplyDeleteweb design training in chennai
Hello.
ReplyDeleteVery interesting post !
I'm trying to do the project, but all the code is not present in the post.
And it is impossible to download the archive with sources because url http://data.boehme.me/blog/mm-base-parent.zip is broken.
Can you please correct that ?
Thanks.
شركة تسليك مجاري المطبخ بالرياض
ReplyDeleteشركة تسليك مجاري بالرياض
شركة تسليك مجارى الحمام بالرياض
level تسليك المجاري بالرياض
افضل شركة تنظيف بالرياض
تنظيف شقق بالرياض
شركة تنظيف منازل بالرياض
شركة غسيل خزنات بالرياض
افضل شركة مكافحة حشرات بالرياض
رش مبيدات بالرياض
شركة تخزين عفش بالرياض
شركة تنظيف مجالس بالرياض
تنظيف فلل بالرياض
ابى شركة تنظيف بالرياض
Thank you for this wonderful post, It really nice and informative. Keep sharing!!
ReplyDeleteHadoop Training in Chennai
Android Training in Chennai
Selenium Training in Chennai
Digital Marketing Training in Chennai
JAVA Training in Chennai
German Classes in chennai
android development course in chennai
android course in chennai with placement
android training center in chennai
Thanks for taking the time to discuss this, I feel happy about it and love to learn more on this topic.web design company in velachery
ReplyDeleteI liked your blog.Thanks for your interest in sharing the information.keep sharing.
ReplyDeleteandroid training in chennai
android online training in chennai
android training in bangalore
android training in hyderabad
android Training in coimbatore
android training
android online training
This comment has been removed by the author.
ReplyDeleteVRay Crack for SketchUp 2022 is a powerful 3D rendering tool. It is compatible with most of the main applications to create digital content. V-Ray Serial Key
ReplyDeleteIt means you could leave as many as 32 agents for every note. It is the best software with a large selection of components that are helpful for music production requirements.Sylenth1 License Code
ReplyDeleteMake your loved ones' holiday even better with these merry Christmas greetings. “Wishing you and those that you love dearly a very happy Christmas filled with . Christmas Birthday Wishes
ReplyDelete