Gradle – wprowadzenie

Gradle – wprowadzenie

Gradle to obok Apache Maven jedno z najbardziej popularnych narzędzi które służą do budowania projektu. Narzędzie to pozwala zautomatyzować proces budowania aplikacji z użyciem języka domenowego DSL. Gradle w porównaniu do Mavena daje nam większą elastyczność – o czym w dalszej części artykułu. Poznawanie gradle’a zaczniemy od utworzenia nowego projektu w Intellij Idea:

po zdefiniowaniu parametrów group, name, version zostanie wygenerowana struktura projektu zawierająca pliki (w mavenie odpowiednikiem jest groupId, artifactId, version):

  • build.gradle – zawiera opis budowania projektu w języku Groovy,
  • gradlew.sh – Gradle Wrapper dla systemu linux, m.in. weryfikuje czy gradle jest poprawnie zainstalowany, pozwala uniezależnić się od wersji gradle’a zainstalowanej na komputerze programisty.
  • gradlew.bat – Gradle Wrapper dla MS Windows, m.in. weryfikuje czy gradle jest poprawnie zainstalowany, pozwala uniezależnić się od wersji gradle’a zainstalowanej na komputerze programisty.
  • settings.gradle – zawiera konfigurację projektu, po wygenerowaniu projektu plik ten zawiera tylko jego nazwę.

W katalogu projektu wydajemy następujące polecenie:

gradlew build --console=verbose

w wyniku otrzymujemy:

Welcome to Gradle 4.8!
 
Here are the highlights of this release:
 - Dependency locking
 - Maven Publish and Ivy Publish plugins improved and marked stable
 - Incremental annotation processing enhancements
 - APIs to configure tasks at creation time
 
For more details see https://docs.gradle.org/4.8/release-notes.html
 
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details
> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar
> Task :assemble
> Task :compileTestJava NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test NO-SOURCE
> Task :check UP-TO-DATE
> Task :build
 
BUILD SUCCESSFUL in 21s
1 actionable task: 1 executed

parametr -console=verbose pozwala wyświetlić więcej informacji na konsoli. Została wykonana cała seria zadań która składa się na budowania projektu. Zawartość pliku build.gradle – zależności pobierane widać są z centralnego repozytorium mavena. Poniżej dodana jest tylko jedna zależność do biblioteki junit 4.12.

plugins {
    id 'java'
}
 
group 'pl.javaleader'
version '1.0-SNAPSHOT'
 
sourceCompatibility = 1.8
 
repositories {
    mavenCentral()
}
 
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

Gradle działa na zasadzie zadań, zadaniem może być utworzenie pliku JAR czy uruchomienie testów. Dodajmy teraz pierwsze własne zadanie do pliku build.gradle:

task javaLeaderMsgHelloWorld
 
javaLeaderMsgHelloWorld.doFirst( {
    println "This is done first JavaLeader.pl"
} );
 
javaLeaderMsgHelloWorld.doLast( {
    println "This is done last JavaLeader.pl"
} );

uruchamiamy zadanie:

gradlew javaLeaderMsgHelloWorld

wynik:

> Task :javaLeaderMsgHelloWorld
This is done first JavaLeader.pl
This is done last JavaLeader.pl

Używając Apache Maven nie byłoby to takie proste ze względu na sztywny proces budowania (lifecycle)…

Projekt wielomodułowy:

Wielomodułowy projekt pozwala lepiej podzielić poszczególne warstwy aplikacji, utwórzmy zatem nowy moduł:

podając nazwę: name = api (grupa oczywiście jest ta sama bo to podprojekt, nie musimy jej ponownie podawać).

Plik settings.gradle głównego projektu przedstawia się następująco:

rootProject.name = 'gradle-intro'
include 'api'

dodajmy interfejs do modułu api:

public interface MsgService {
    String showMsg();
}

oraz jego implementację:

public class MsgServiceImpl implements MsgService {
    @Override
    public String showMsg() {
        return "JavaLeader.pl";
    }
}

w pliku build.gradle głównego projektu dodajemy zależność do podmodułu – api:

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile project(":api")
}

Do głównego projektu dodajemy klasę która wykorzystuje klasy z modułu – api:

public class MessagePrinter {
    public static void main(String[] args) {
        MsgService msgService = new MsgServiceImpl();
        msgService.showMsg();
        System.out.println();
    }
}

tworzymy nowy moduł – WebPage – który będzie aplikacją webową z pakowaniem war. Niestety w gradle nie ma archetypów a wsparcie do tworzenia projektów według zdefiniowanego szablonu jest ubogie – https://docs.gradle.org/current/userguide/build_init_plugin.html. Należy zatem “ręcznie” utworzyć strukturę projektu aplikacji web.

Plik gradle.build aplikacji webowej:

plugins {
    id 'java'
    id 'war'
    id 'org.gretty' version '2.2.0'
}

dodajemy sposób pakowania projektu typowy dla aplikacji webowych – war – oraz plugin który pozwala w łatwy sposób uruchomić aplikację z użyciem serwera Jetty lub serwera Apache Tomcat. Dodajemy również zależność do modułu – api i obsługę servletów – javax.servlet-api:

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile project(":api")
}

należy również dodać nazwę repozytorium – jcenter() – inaczej nie uda się dodać zależności do pluginu org.gretty:

repositories {
    mavenCentral()
    jcenter()
}

Tworzymy teraz przykładowy servlet:

@WebServlet(name = "JavaLeaderHomeServlet", urlPatterns = {"home"}, loadOnStartup = 1)
public class JavaLeaderHomeServlet extends HttpServlet {
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        MsgService msgService = new MsgServiceImpl();
        response.getWriter().print(msgService.showMsg());
    }
 
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        if (name == null) {
            name = "noname";
        }
        request.setAttribute("user", name);
        request.getRequestDispatcher("response.jsp").forward(request, response); 
    }
}

Plik webapp/index.html:

<html>
<head>
    <title>Web Demo</title>
</head>
<body>
<form method="post" action="home">
    <h2>Name:</h2>
    <input type="text" id="say-hello-text-input" name="name" />
    <input type="submit" id="say-hello-button" value="Say Hello" />
</form>
</body>
</html>

Plik webapp/response.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Hello Page</title>
    </head>
    <body>
        <h2>Hello, ${user}!</h2>
    </body>
</html>

Uruchomienie aplikacji sprowadza się do wykonania zadania:

gradlew appRun

Pod adresem:

http://localhost:8080/web-app/

widoczny jest formularz z pliku webapp/index.html:

Pod adresem:

http://localhost:8080/web-app/home

widać odpowiedź z servletu dla mapowania adresu /home dla metody get:

JavaLeader.pl

Pod adresem:

http://localhost:8080/web-app/home

widać odpowiedź z servletu dla mapowania adresu /home dla metody post.

Hello, JavaLeader.pl!

Testy jednostkowe z użyciem biblioteki Mockito:

Dodajemy zależność do biblioteki Mockito celem przetestowania Servletu:

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testCompile 'org.mockito:mockito-core:2.7.19'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile project(":api")
}

tworzymy klasę testową w pakiecie ./test/java która pozwoli przetestować Servlet:

public class JavaLeaderTestServlet {
 
    @Mock
    private HttpServletRequest request;
 
    @Mock
    private HttpServletResponse response;
 
    @Mock
    private RequestDispatcher requestDispatcher;
 
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }
 
    @Test
    public void doGet() throws Exception {
 
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter   = new PrintWriter(stringWriter);
 
        when(response.getWriter()).thenReturn(printWriter);
 
        new JavaLeaderHomeServlet().doGet(request, response);
 
        assertEquals("JavaLeader.pl", stringWriter.toString());
    }
 
    @Test
    public void doPostWithoutName() throws Exception {
        when(request.getRequestDispatcher("response.jsp")).thenReturn(requestDispatcher);
 
        new JavaLeaderHomeServlet().doPost(request, response);
 
        verify(request).setAttribute("user", "noname");
        verify(requestDispatcher).forward(request,response);
    }
 
    @Test
    public void doPostWithName() throws Exception {
        when(request.getParameter("name")).thenReturn("JavaLeader.pl");
        when(request.getRequestDispatcher("response.jsp")).thenReturn(requestDispatcher);
 
        new JavaLeaderHomeServlet().doPost(request, response);
 
        verify(request).setAttribute("user", "JavaLeader.pl");
        verify(requestDispatcher).forward(request,response);
    }
}

Servlet przetestowany jest na 3 różne sposoby z użyciem odpowiednio spreparowanych obiektów – Mock. Po uruchomieniu testów poleceniem:

gradlew test

dostępny jest ich wynik w pliku test/index.html:

jak widać wszystkie testy przeszły prawidłowo.

Zobacz kod na GitHubie i zapisz się na bezpłatny newsletter!

.

Leave a comment

Your email address will not be published.


*