<?xml version="1.0" encoding="UTF-8"?> <appendix annotations="slide" version="5.1" xml:id="sd1_appendix" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:svg="http://www.w3.org/2000/svg" xmlns:ns="http://docbook.org/ns/transclusion" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:db="http://docbook.org/ns/docbook"> <title xml:id="sd1Appendix">Appendix</title> <section xml:id="sd1_sect_exercisingPastExaminations"> <title>Exercising past examinations</title> <para>Lecture notes exercise solutions are being provided as <xref linkend="glo_Maven"/> projects below <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/tree/master/P/Sd1">P/Sd1</link> by the <link xlink:href="https://gitlab.mi.hdm-stuttgart.de">MI Gitlab</link> repository. Comparing these solutions to your own solution may be easily accomplished by importing them into your <xref linkend="glo_IDE"/>.</para> <para>Moreover the final examination and some exercises require importing <xref linkend="glo_Maven"/> skeleton projects to start from. The general procedure during an examination with respect to programming components is being described subsequently.</para> <figure xml:id="sd1_fig_examRemoteGuacamole"> <title>Exam training by Guacamole</title> <mediaobject> <imageobject> <imagedata fileref="Fig/guacamoleExam.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_examRemoteGuacamoleHints"> <title>Environment hints:</title> <para>The following installations allow for exercises with respect to the final examination:</para> <itemizedlist> <listitem> <para>E-Exam 113105 <foreignphrase xml:lang="de">Softwareentwicklung</foreignphrase> 1 Test / Network blocked:</para> <para>This installation closely resembles the real examination environment: Local firewall rules restrict internet access to a small number of allowed sites.</para> </listitem> <listitem> <para>E-Exam 113105 <foreignphrase xml:lang="de">Softwareentwicklung</foreignphrase> 1 Test / <productname>Teamviewer</productname>:</para> <para>This installation does not block internet access at all. In addition you may use <link xlink:href="https://www.teamviewer.com/en/products/teamviewer"><productname>Teamviewer</productname></link> to get external help in case of trouble.</para> </listitem> </itemizedlist> </figure> <section xml:id="sd1_sect_exam_prepare"> <title>Starting an exam</title> <figure xml:id="sd1_fig_examPrepare"> <title>Preparing an examination</title> <mediaobject> <imageobject> <imagedata fileref="Fig/prepare.multi.svg"/> </imageobject> </mediaobject> </figure> </section> <section xml:id="sd1_sect_exam_workUnitTest"> <title>Implementing the project skeleton</title> <figure xml:id="sd1_fig_examGenJavadoc"> <title>Generating <xref linkend="glo_Javadoc"/>.</title> <itemizedlist> <listitem> <para>HTML is difficult to read:</para> <programlisting language="java"> * <td><code>a = -3</code>, <code>b = 4</code>, <code>c = 3</code></td> * <td>4 - (-3) = 7</td> * </tr> * </table> ... */ static public int getMaxAbsoluteDiff(int a, int b, int c) { return 42; // TODO: Implement me correctly</programlisting> </listitem> <listitem> <para>⇒ Generate <xref linkend="glo_Javadoc"/> by <acronym linkend="sd1_fig_mavenJavadoc">CLI</acronym> or <link linkend="sd1_fig_mavenIdeaExecuteJavadocGoal">Idea</link>.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_examProgrammingHints"> <title>Programming hints</title> <itemizedlist> <listitem> <para>The debugger is your friend ...</para> </listitem> <listitem> <para>... but only if acquiring prior proficiency.</para> </listitem> <listitem> <para>Train <link linkend="sd1_fig_primeDebugError">prime or related example</link>.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_examImplementTestCycle"> <title>The implement - test - implement cycle</title> <mediaobject> <imageobject> <imagedata fileref="Fig/implement.multi.svg"/> </imageobject> </mediaobject> </figure> </section> <section xml:id="sd1_sect_examFinish"> <title>Finish the exam</title> <figure xml:id="sd1_fig_examFinish"> <title>Finishing the exam</title> <mediaobject> <imageobject> <imagedata fileref="Fig/finish.multi.svg"/> </imageobject> </mediaobject> </figure> </section> </section> <section xml:id="sd1ExaminationHints"> <title>Examination hints</title> <figure xml:id="sd1_fig_examCheatSheet"> <title>Personal examination cheat sheets</title> <itemizedlist> <listitem> <para>Username based Zip archive of <emphasis role="red">10 MB max</emphasis> e.g. <filename>xy123.zip</filename>.</para> <caution> <para>Use standard formats e.g. <acronym>png</acronym>, <acronym>gif</acronym>, <acronym>pdf</acronym>. Proprietary stuff like <acronym>.doc</acronym>, <acronym>.docx</acronym>, <acronym>.rtf</acronym> possibly lack tool support by exam environment.</para> </caution> </listitem> <listitem> <para>Link for <link xlink:href="https://freedocs.mi.hdm-stuttgart.de/uploadCheat">cheat sheet uploads</link>.</para> </listitem> <listitem> <para>Freeze during exam periods.</para> </listitem> <listitem> <para>Cheat sheets available at <uri xlink:href="https://freedocs.mi.hdm-stuttgart.de/cheat/">https://freedocs.mi.hdm-stuttgart.de/cheat/</uri> until end of exam period.</para> </listitem> <listitem> <para><emphasis role="red">Check your archive's content in the exam environment!</emphasis></para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_examByJunit"> <title>Unit tests in examinations</title> <itemizedlist> <listitem> <para>Task definitions by <xref linkend="glo_Javadoc"/>.</para> </listitem> <listitem> <para>Corresponding Unit tests.</para> </listitem> <listitem> <para>Automated evaluation scoring your achievements.</para> </listitem> <listitem> <para>Individual weights reflecting a test's significance.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_interfaceDefExample"> <title>Example interface definition</title> <programlisting language="java">/** * Finde das n-te ungerade Element einer Wertefolge. * * <p>Beispiel: Im Array {3, 2, 0, 1, 4} ist der Wert «1» an der Index- * position «3» das zweite ungerade Element.</p> * * @param werte Die zu durchsuchenden Werte. * @param n Die gewünschte Position, Start bei 1. * * @return Den Index des n-ten ungeraden Wertes falls es mindestens n * ungerade Werte gibt, ... */ static public int getNtesUngeradesElement(final int[] werte, final int n){ return 12345; // TODO: Implementiere mich korrekt! }</programlisting> </figure> <figure xml:id="sd1_fig_testExample"> <title>Corresponding test</title> <programlisting language="java">@Test @Marking(points=1) /* 1 point if test passes */ public void test_400() { Assert.assertEquals( 2, /* Expected result */ Helper.getNtesUngeradesElement(new int[]{-4, 6, 1, -2, 8}, 1)); }</programlisting> </figure> <figure xml:id="sd1_fig_testDontCheat"> <title>Don't cheat!</title> <informaltable border="1"> <tr> <th>Unit Tests</th> <th>Your solution</th> </tr> <tr> <td valign="top"><programlisting language="none">assertFalse(isPrime(1)); assertTrue (isPrime(<emphasis role="red">2</emphasis>)); assertTrue (isPrime(<emphasis role="red">3</emphasis>)); assertFalse(isPrime(4)); assertTrue (isPrime(<emphasis role="red">5</emphasis>)); assertFalse(isPrime(6)); assertTrue (isPrime(<emphasis role="red">7</emphasis>)); assertFalse(isPrime(8)); assertFalse(isPrime(9)); assertFalse(isPrime(10));</programlisting></td> <td valign="top"><programlisting language="none">... boolean isPrime(final int p) { switch (p) { case <emphasis role="red">2</emphasis>: case <emphasis role="red">3</emphasis>: case <emphasis role="red">5</emphasis>: case <emphasis role="red">7</emphasis>: return true; default: return false; }</programlisting></td> </tr> </informaltable> <para>Will be treated as an <emphasis role="red">attempt at deception</emphasis> / <foreignphrase xml:lang="de"><emphasis role="red">Täuschungsversuch</emphasis></foreignphrase>.</para> </figure> <figure xml:id="sd1_fig_unitTestsStrategy"> <title>Unit tests strategy in examinations</title> <itemizedlist> <listitem> <para>Unit testing is relentless: You are no longer at high school where a result having <quote>just</quote> a wrong sign used to matter next to nothing.</para> </listitem> <listitem> <para>Focus on completing units of work rather than <quote>nearly</quote> finishing a large number of tasks.</para> </listitem> <listitem> <para>Watching a test fail just happens. Learn to <emphasis>systematically</emphasis> fix bugs:</para> <orderedlist> <listitem> <para>Use your <xref linkend="glo_IDE"/>'s debugger. Practise debugging <xref linkend="glo_Junit"/> tests individually addressing failures one by one.</para> </listitem> <listitem> <para>Insert log statements using <productname xlink:href="https://logging.apache.org/log4j/2.x/">log4j</productname>.</para> </listitem> </orderedlist> </listitem> </itemizedlist> </figure> </section> <section xml:id="sd1SectExamAmendingProjects"> <title>Examination bonus point projects</title> <figure xml:id="sd1_fig_collaborativeEfforts"> <title>Collaborative efforts</title> <informaltable border="1"> <tr> <td><mediaobject> <imageobject> <imagedata fileref="Fig/soDumm2.png"/> </imageobject> </mediaobject></td> <td><para>Is this your <emphasis role="red">TEAM</emphasis>?</para><para><emphasis role="red">T</emphasis>oll</para><para><emphasis role="red">E</emphasis>in</para><para><emphasis role="red">A</emphasis>nderer</para><para><emphasis role="red">M</emphasis>achts</para></td> </tr> </informaltable> </figure> <figure xml:id="sd1_fig_projectRules"> <title>Project rules</title> <orderedlist> <listitem> <para>You are expected to work as a <emphasis role="bold">team of three partners</emphasis>.</para> </listitem> <listitem> <para>Using the <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/">MI Gitlab SCM</link> is a plus with respect to project evaluation. See table below.</para> </listitem> <listitem> <para>Your team is expected to supply a Maven project based on the MI <quote>Maven archetype quickstart</quote> available from <uri xlink:href="https://maven.mi.hdm-stuttgart.de/nexus/repository/mi-maven/archetype-catalog.xml">https://maven.mi.hdm-stuttgart.de/nexus/repository/mi-maven/archetype-catalog.xml</uri>.</para> </listitem> <listitem> <para>You are expected to provide good internal code documentation with respect both to method signatures (<xref linkend="glo_Javadoc"/>) and method implementation.</para> </listitem> </orderedlist> </figure> <figure xml:id="sd1_fig_projectCodeDoc"> <title>Internal code documentation</title> <para>You are expected to provide good internal code documentation with respect both to method signatures (<xref linkend="glo_Javadoc"/>) and method implementation. Possible problems involve:</para> <mediaobject> <imageobject> <imagedata fileref="Fig/bonusJavadocCompileTimeProblems.png"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_projectCodeDocHint"> <title>Internal code documentation hints</title> <glosslist> <glossentry> <glossterm>Compile time warnings</glossterm> <glossdef> <para>Activate most compiler warnings at <guimenuitem>Editor</guimenuitem> --> <guimenuitem>Inspections</guimenuitem>. This will show potential compile time problems like dead / unnecessary / unreachable code, unused variable values, shadowing conflicts and so on.</para> </glossdef> </glossentry> </glosslist> </figure> <figure xml:id="sd1_fig_projectJavadoc"> <title><xref linkend="glo_Javadoc"/> mismatches</title> <para>Your method's formal parameters, their type and a method's return type must match your documentation.</para> <mediaobject> <imageobject> <imagedata fileref="Fig/bonusJavadocMismatch.png"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_projectUnitTests"> <title>(Automated) tests</title> <para>You are expected to provide meaningful unit tests:</para> <itemizedlist> <listitem> <para>Try to cover all your implementation code and not just isolated modules / methods.</para> </listitem> <listitem> <para>If methods allow for null values write suitable tests.</para> </listitem> <listitem> <para>Test special cases: If a method expects <abbrev>i.e.</abbrev> an array of strings it may be allowed having zero length.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_projectDeployRun"> <title>Deployment and execution</title> <para>Your resulting project should be easily installable and runnable.</para> <itemizedlist> <listitem> <para>Maven is a good starting point with respect both to testing and cross platform (Unix / Windows / Apple) portability.</para> </listitem> <listitem> <para>Avoid dependencies to local file system resources like <filename>c:\users\xyz\testdata.txt</filename>.</para> </listitem> </itemizedlist> <tip> <para>Test your application's deployability by installing it on an untouched target platform (possibly of a different hard/software architecture) and execute <command>mvn</command> <option>test</option> (provided you do have written meaningful unit tests).</para> </tip> </figure> <figure xml:id="sd1_fig_projectEvalCriteria"> <title>Marking criteria</title> <informaltable border="1"> <colgroup width="62%"/> <colgroup width="38%"/> <tr> <th>Criterion</th> <th>Percentage</th> </tr> <tr> <td valign="top">Overall code quality</td> <td valign="top">20%</td> </tr> <tr> <td valign="top">Code documentation</td> <td valign="top">20%</td> </tr> <tr> <td valign="top">Unit tests</td> <td valign="top">10%</td> </tr> <tr> <td valign="top">Deployment</td> <td valign="top">5%</td> </tr> <tr> <td valign="top">SCM usage</td> <td valign="top">5%</td> </tr> <tr> <td valign="top">Software functionality</td> <td valign="top">40%</td> </tr> </informaltable> </figure> <para>Some of these terms require further explanations:</para> <glosslist> <glossentry> <glossterm>Overall code quality</glossterm> <glossdef> <para>Subject to personal preferences unfortunately.</para> </glossdef> </glossentry> <glossentry> <glossterm>Code documentation</glossterm> <glossdef> <para>This involves writing <xref linkend="glo_Javadoc"/> describing your classes. Furthermore you may want to add a project / architecture description. A <filename>Readme.md</filename> in your project's root is a good starting point.</para> </glossdef> </glossentry> <glossentry> <glossterm>Unit tests</glossterm> <glossdef> <para>Ideally your whole code could be covered by sensible unit tests. Albeit guaranteeing absence of errors it may help minimizing them.</para> </glossdef> </glossentry> <glossentry> <glossterm>Deployment</glossterm> <glossdef> <para>How difficult is your software's deployment? Sample:</para> <screen>git clone git@gitlab.mi.hdm-stuttgart.de:.../yourProject.git cd yourProject mvn package # possible configuration according to documentation java -jar target/yourArchive.jar </screen> </glossdef> </glossentry> <glossentry> <glossterm>SCM usage</glossterm> <glossdef> <para>Using <abbrev>e.g.</abbrev> git supports team oriented project development. It also allows for tracing the participants individual contributions. For this reason your tutor is required to have read access to your project.</para> </glossdef> </glossentry> <glossentry> <glossterm>Software functionality</glossterm> <glossdef> <itemizedlist> <listitem> <para>Project specification met?</para> </listitem> <listitem> <para>(User input) Error handling?</para> </listitem> <listitem> <para>Stability</para> </listitem> </itemizedlist> </glossdef> </glossentry> </glosslist> <para>The following sections contain both current and archived project propositions.</para> <section xml:id="sd1_sect_projectWeatherForecast"> <title>Weather forecast</title> <warning> <para>This project aims at using third party software components. It may not be appropriate if you fancy <quote>bottom up</quote> coding yourself.</para> </warning> <para>In this project you'll implement a terminal based weather forecast application. Consider the following invocation:</para> <figure xml:id="sd1_weather_fig_invocationVideo"> <title>Sample forecast session</title> <mediaobject> <videoobject> <videodata fileref="Fig/weather.webm"/> </videoobject> </mediaobject> </figure> <figure xml:id="sd1_weather_fig_invocationExample"> <title>Sample forecast invocation</title> <screen>goik@goiki target> java -jar weather-1.0.jar <emphasis role="red">Stuttgart</emphasis> <co linkends="sd1_weather_fig_invocationExample-1.2" xml:id="sd1_weather_fig_invocationExample-1.2-co"/> 1 = Stadtkreis Stuttgart <co linkends="sd1_weather_fig_invocationExample-2.2" xml:id="sd1_weather_fig_invocationExample-2.2-co"/> <emphasis role="red">2 = Regierungsbezirk Stuttgart</emphasis> 3 = Stuttgart 4 = Stuttgart Feuerbach 5 = Stuttgart Muehlhausen Bitte gültige Auswahl 1 bis 5 treffen:<emphasis role="red">2</emphasis> <co linkends="sd1_weather_fig_invocationExample-3" xml:id="sd1_weather_fig_invocationExample-3-co"/> Vorhersage für <emphasis role="red">Regierungsbezirk Stuttgart</emphasis> <co linkends="sd1_weather_fig_invocationExample-4" xml:id="sd1_weather_fig_invocationExample-4-co"/> Dienstag, 15.05 23:00: 11°C, Leichter Regen Mittwoch, 16.05 02:00: 10°C, Leichter Regen 05:00: 10°C, Leichter Regen 08:00: 11°C, Leichter Regen ...</screen> </figure> <calloutlist> <callout arearefs="sd1_weather_fig_invocationExample-1.2-co" xml:id="sd1_weather_fig_invocationExample-1.2"> <para>Command line argument <emphasis role="red">Stuttgart</emphasis> acting as a search filter.</para> </callout> <callout arearefs="sd1_weather_fig_invocationExample-2.2-co" xml:id="sd1_weather_fig_invocationExample-2.2"> <para>The filter <emphasis role="red">Stuttgart</emphasis> yields five matching towns / regions.</para> </callout> <callout arearefs="sd1_weather_fig_invocationExample-3-co" xml:id="sd1_weather_fig_invocationExample-3"> <para>User choosing second match.</para> </callout> <callout arearefs="sd1_weather_fig_invocationExample-4-co" xml:id="sd1_weather_fig_invocationExample-4"> <para>Forecast corresponding to <emphasis role="red">Regierungsbezirk Stuttgart</emphasis>.</para> </callout> </calloutlist> <para>The actual weather data is being provided by a web service:</para> <figure xml:id="sd1_weather_fig_underlyingData"> <title>Underlying data provider</title> <screen>https://api.openweathermap.org/data/2.5/forecast?lang=de& <co linkends="sd1_weather_fig_underlyingData-1" xml:id="sd1_weather_fig_underlyingData-1-co"/> APPID=7cufdhdcgdhsgdhgfcgsdss67b3&units=metric&<emphasis role="red">id=3214105</emphasis> {"cod":"200","message":0.0042,"cnt":40,"list":[ <co linkends="sd1_weather_fig_underlyingData-2" xml:id="sd1_weather_fig_underlyingData-2-co"/> {"dt":1526428800,"main":{"temp":10.29,"temp_min":10.29, "temp_max":12.45,"pressure":985.75,"sea_level":1027.48, "grnd_level":985.75,"humidity":80,"temp_kf":-2.16}, "weather":[{"id":500,"main":"Rain", "description":"Leichter Regen","icon":"10n"}],"clouds": {"all":88},"wind":{"speed":1.59,"deg":313.503},"rain": {"3h":0.315},"sys":{"pod":"n"},"dt_txt":"2018-05-16 00:00:00"}, {"dt":1526439600,"main": ...</screen> <calloutlist> <callout arearefs="sd1_weather_fig_underlyingData-1-co" xml:id="sd1_weather_fig_underlyingData-1"> <para>An <xref linkend="glo_URL"/> containing an id value corresponding to a uniquely defined town or region. We identify the following components:</para> <glosslist> <glossentry> <glossterm><code>lang=de</code></glossterm> <glossdef> <para>Provide German localization e.g. «<foreignphrase xml:lang="de">Leichter Regen</foreignphrase>» in favour of «light rain».</para> </glossdef> </glossentry> <glossentry> <glossterm><abbrev>APPID</abbrev>=7cufdhdcgdhsgdhgfcgsdss67b3</glossterm> <glossdef> <para>This parameter allows for accessing the service: «7cufdhdcgdhsgdhgfcgsdss67b3» is actually a fake value. Your project requires <link xlink:href="https://openweathermap.org/appid">obtaining an APPID token</link>.</para> </glossdef> </glossentry> <glossentry> <glossterm>units=metric</glossterm> <glossdef> <para>Favour metric over imperial units.</para> </glossdef> </glossentry> <glossentry> <glossterm>id=3214105</glossterm> <glossdef> <para>«3214105» identifies «<foreignphrase xml:lang="de">Regierungsbezirk Stuttgart</foreignphrase>», see line 262703 in <filename xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/main/resources/cities.list.json">cities.list.json</filename>:</para> <programlisting language="json">"id": 3214105, "name": "Regierungsbezirk Stuttgart", "country": "DE", "coord": { "lon": 9.66667, "lat": 49.083328 }</programlisting> </glossdef> </glossentry> </glosslist> </callout> <callout arearefs="sd1_weather_fig_underlyingData-2-co" xml:id="sd1_weather_fig_underlyingData-2"> <para><uri xlink:href="https://openweathermap.org/api">https://openweathermap.org/api</uri>'s reply providing <abbrev>JSON</abbrev> based weather data.</para> </callout> </calloutlist> </figure> <figure xml:id="sd1_weather_fig_citiesFile"> <title><filename xlink:href="http://bulk.openweathermap.org/sample/city.list.json.gz">cities.list.json.gz</filename> providing cities</title> <programlisting language="json">[ { "id": 2886241, "name": "Regierungsbezirk Köln", "country": "DE", "coord": { "lon": 7.16667, "lat": 50.833328 } }, { "id": 3247452, "name": "Kreis Euskirchen", ... ]</programlisting> </figure> <figure xml:id="sd1_weather_fig_urlToFileHelper"> <title>ma/Copy URL result to file</title> <programlisting language="java">FileUtils.<link xlink:href="https://commons.apache.org/proper/commons-io/javadocs/api-2.6/org/apache/commons/io/FileUtils.html#copyURLToFile(java.net.URL,java.io.File)">copyURLToFile</link>( "https://api.openweathermap.org/data/2.5/forecast...", new File("weatherData.json"));</programlisting> <programlisting language="xml"><dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency></programlisting> </figure> <figure xml:id="sd1_weather_fig_parseCityData"> <title>Parse city data</title> <programlisting language="java"><link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/main/java/de/hdm_stuttgart/mi/sd1/weather/cities/Cities.java">public class Cities</link> { static public final City[] cities; ... }</programlisting> <programlisting language="java">@Test <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/test/java/de/hdm_stuttgart/mi/sd1/weather/CityParserTest.java">public void testParsedCityCount()</link> { Assert.assertEquals(209579, Cities.cities.length); }</programlisting> </figure> <figure xml:id="sd1_weather_fig_parseWeatherData"> <title>Parse weather data</title> <programlisting language="java"><link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/main/java/de/hdm_stuttgart/mi/sd1/weather/WeatherDataParser.java">public class WeatherDataParser</link> { static public final <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/main/java/de/hdm_stuttgart/mi/sd1/weather/model/Weather.java">Weather</link> parse(final String jsonWeatherDataFilename) throws IOException { return ...; } }</programlisting> <programlisting language="java">@Test <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/test/java/de/hdm_stuttgart/mi/sd1/weather/WeatherDataParseTest.java">public void testParseWeatherData()</link> { ... Weather weather = WeatherDataParser.parse( "src/main/resources/stuttgart.weather.json"); ...</programlisting> </figure> <figure xml:id="sd1_weather_fig_weatherRequirements"> <title>Requirements</title> <orderedlist> <listitem> <para>The application shall accept a command line parameter like e.g. «Stuttgart» to filter matching cities from <filename xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/Doc/Sd1/Ref/Projects/WeatherSkeleton/src/main/resources/cities.list.json">cities.list.json</filename>.</para> </listitem> <listitem> <para>If a given filter matches multiple locations the user shall have an option for choosing the desired one.</para> </listitem> <listitem> <para>The most recent city id value shall be cached in a file. Subsequent invocations without command line parameter shall provide a current forecast corresponding to this value.</para> </listitem> <listitem> <para>Weather data belonging to a given id value shall be cached locally for 10 minutes. Subsequent weather queries within this period shall be read from cache rather than by accessing <uri>https://api.openweathermap.org/...</uri> .</para> <para>Provide logging to a file rather than to the console to avoid cluttering the user interface. Log cache handling.</para> </listitem> </orderedlist> </figure> <figure xml:id="sd1_weather_fig_weatherLogging"> <title>Logging</title> <itemizedlist> <listitem> <para>Provide logging to a file rather than to the console to avoid cluttering the user interface.</para> </listitem> <listitem> <para>Log cache handling.</para> </listitem> </itemizedlist> <screen>main INFO weather.Forecast - Re-using cache file '/ma/goik/Forecast/6930414.json' from 196 seconds ago</screen> </figure> <tip> <para>See the related <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/-/tree/master/Doc/Sd1/Ref/Projects/WeatherSkeleton">skeleton project</link>.</para> </tip> </section> <section xml:id="sd1_sect_projectRpnCalculator"> <title>Reverse Polish notation (<abbrev><abbrev xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</abbrev></abbrev>) calculator</title> <para>In a nutshell <abbrev xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</abbrev> allows for compact arithmetic expressions requiring no parentheses to express operator priorities. The introductory article <link xlink:href="http://www.calculator.org/rpn.aspx">What is Reverse Polish Notation?</link> explains the underlying idea.</para> <para>This project's comprises two different goals:</para> <orderedlist> <listitem> <para>Parse and evaluate <abbrev xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</abbrev> expressions:</para> <informaltable border="1"> <colgroup width="24%"/> <colgroup width="28%"/> <colgroup width="14%"/> <colgroup width="34%"/> <tr> <th><abbrev xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</abbrev> expression</th> <th>Conventional expression</th> <th>Result</th> <th>Comment</th> </tr> <tr> <td>1 2 +</td> <td><inlineequation> <m:math display="inline"> <m:mrow> <m:mi>1</m:mi> <m:mo>+</m:mo> <m:mi>2</m:mi> </m:mrow> </m:math> </inlineequation></td> <td>3</td> <td/> </tr> <tr> <td>1 2 3 4 + - +</td> <td><inlineequation> <m:math display="inline"> <m:mrow> <m:mi>1</m:mi> <m:mo>+</m:mo> <m:mrow> <m:mo>(</m:mo> <m:mrow> <m:mi>2</m:mi> <m:mo>-</m:mo> <m:mrow> <m:mo>(</m:mo> <m:mrow> <m:mi>3</m:mi> <m:mo>+</m:mo> <m:mi>4</m:mi> </m:mrow> <m:mo>)</m:mo> </m:mrow> </m:mrow> <m:mo>)</m:mo> </m:mrow> </m:mrow> </m:math> </inlineequation></td> <td>-4</td> <td/> </tr> <tr> <td>1 2 + 4 *</td> <td><inlineequation> <m:math display="inline"> <m:mrow> <m:mrow> <m:mo>(</m:mo> <m:mrow> <m:mi>1</m:mi> <m:mo>+</m:mo> <m:mi>2</m:mi> </m:mrow> <m:mo>)</m:mo> </m:mrow> <m:mo>×</m:mo> <m:mi>4</m:mi> </m:mrow> </m:math> </inlineequation></td> <td>12</td> <td>No parentheses required</td> </tr> <tr> <td>pi</td> <td><inlineequation> <m:math display="inline"> <m:mi>π</m:mi> </m:math> </inlineequation></td> <td>3.1415926...</td> <td>symbolic constant pi</td> </tr> <tr> <td>pi sin</td> <td><inlineequation> <m:math display="inline"> <m:mrow> <m:mi>sin</m:mi> <m:mo></m:mo> <m:mrow> <m:mo>(</m:mo> <m:mi>π</m:mi> <m:mo>)</m:mo> </m:mrow> </m:mrow> </m:math> </inlineequation></td> <td>0</td> <td/> </tr> <tr> <td>4 sqrt ~</td> <td><inlineequation> <m:math display="inline"> <m:mrow> <m:mo>-</m:mo> <m:msqrt> <m:mi>4</m:mi> </m:msqrt> </m:mrow> </m:math> </inlineequation></td> <td>-2</td> <td>«~» is Unary minus</td> </tr> <tr> <td>0x1a 0b1101 +</td> <td><inlineequation> <m:math display="inline"> <m:mn>0</m:mn> </m:math> </inlineequation></td> <td>3</td> <td>Binary and Hexadecimal literals</td> </tr> </informaltable> </listitem> <listitem> <para>Providing a user interface. Your are expected to write a terminal based application. This leaves different choices:</para> <glosslist> <glossentry> <glossterm>The easy way</glossterm> <glossdef> <para>Ask the user for line by line input as you already did in e.g. <xref linkend="sd1QandaAtTheBar"/>. This is straightforward but offers little responsiveness.</para> </glossdef> </glossentry> <glossentry> <glossterm>The hard way</glossterm> <glossdef> <para>Use a library for creating text-based GUIs like <productname xlink:href="https://github.com/mabe02/lanterna">Lanterna</productname>. This requires some time diving into the API.</para> </glossdef> </glossentry> <glossentry> <glossterm>The really hard way</glossterm> <glossdef> <para>Depending on your <xref linkend="glo_Java"/> skills you may favour a <xref linkend="glo_GUI"/> application over the proposed terminal based approach. However do not forget to implement the calculator's logic when fiddling with the user interface and be aware to reserve (possibly a lot of) extra time for the project.</para> </glossdef> </glossentry> </glosslist> </listitem> </orderedlist> <para>A prototype is available at <link xlink:href="/Sd1/Ref/Src/rpncalculator-0.1.jar">rpncalculator-0.1.jar</link>. Executing <command>java</command> <option>-jar</option> <option>rpncalculator-0.1.jar</option> allows for evaluating <abbrev xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</abbrev> expressions in a terminal:</para> <mediaobject> <videoobject> <videodata fileref="Fig/rpnCalculator.webm" format="video/webm"/> </videoobject> </mediaobject> <section xml:id="sd1_sect_projectRpnCalculatorFunctionality"> <title>Desired functionality</title> <para>Implement the following elements:</para> <glosslist> <glossentry> <glossterm>Literals</glossterm> <glossdef> <informaltable border="1"> <colgroup width="24%"/> <colgroup width="76%"/> <tr> <th>Example</th> <th>Description</th> </tr> <tr> <td>2, 3.0, -1.82 1.3E-23</td> <td><link xlink:href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">IEEE 754 Double-precision</link></td> </tr> <tr> <td>0b100, -0B101</td> <td>Integer Binary representation</td> </tr> <tr> <td>0xa7, -0xB2FF</td> <td>Integer Hexadecimal representation</td> </tr> <tr> <td>pi</td> <td>Math <link xlink:href="https://en.wikipedia.org/wiki/Pi"><inlineequation> <m:math display="inline"> <m:mi>π</m:mi> </m:math> </inlineequation> 3.1415926...</link></td> </tr> <tr> <td>e</td> <td><link xlink:href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">Euler's number</link> 2.71828...</td> </tr> </informaltable> </glossdef> </glossentry> <glossentry> <glossterm>Unary operators / functions</glossterm> <glossdef> <informaltable border="1"> <colgroup width="18%"/> <colgroup width="82%"/> <tr> <th>Example</th> <th>Description</th> </tr> <tr> <td>~</td> <td>Unary minus</td> </tr> <tr> <td>sin, cos, tan</td> <td>Trigonometric functions</td> </tr> <tr> <td>exp</td> <td>Exponential to <link xlink:href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">base e</link></td> </tr> <tr> <td>ln</td> <td>Natural logarithm (<link xlink:href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">base e</link>)</td> </tr> <tr> <td>sqrt</td> <td>Square root</td> </tr> </informaltable> </glossdef> </glossentry> <glossentry> <glossterm>Binary operators / functions</glossterm> <glossdef> <informaltable border="1"> <colgroup width="25%"/> <colgroup width="75%"/> <tr> <th>Example</th> <th>Description</th> </tr> <tr> <td>+</td> <td>Binary plus</td> </tr> <tr> <td>-</td> <td>Binary minus</td> </tr> <tr> <td>*</td> <td>Binary multiply</td> </tr> <tr> <td>/</td> <td>Binary divide</td> </tr> <tr> <td>pow</td> <td>Power function</td> </tr> </informaltable> </glossdef> </glossentry> </glosslist> </section> <section xml:id="sd1_sect_rpnImplementationHints"> <title>Implementation hints</title> <para>Every <abbrev xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</abbrev> expression may be viewed as a sequence of tokens each having both a type and a content. Consider the following expression <code>-1.34 0.34 + sqrt</code> consisting of four tokens:</para> <screen>Type: Double | Type: Double| Type: Binary plus | Type: square Function | | operator | root Value: -1.34 | Value: 0.34 | Value: + | value: sqrt</screen> <para>The following scanner application utilizes <link xlink:href="http://www.vogella.com/tutorials/JavaRegularExpressions/article.html">pattern matching</link> for decomposing expressions like <code>-1.34 0.34 + sqrt</code> into a token sequence:</para> <figure xml:id="sd1_fig_minimalTokenScanner"> <title>Minimalist token scanner</title> <programlisting language="java">final String[] patterns = new String[] { "sqrt", "[-]?([0-9]+[.]?[0-9]*|[.][0-9]+)(E[-]?[0-9]+)?",// Matches e.g. -1.5E-33 "\\+"};// Escape required avoiding regular expression syntax clash. final String expression = "2.1 -3.4 sqrt"; try (final Scanner scanner = new Scanner(expression)) { while (scanner.hasNext()) { for (final String p: patterns) { if (scanner.hasNext(p)) { System.out.println("Token '" + scanner.next(p) + "' matched by '" + p + "'"); break; } } } }</programlisting> </figure> <para>The current example expression <code>-1.34 0.34 + sqrt</code> results in:</para> <figure xml:id="sd1_fig_simpleScannerOutput"> <title>Scanner output <code>-1.34 0.34 + sqrt</code></title> <screen>Token '2.1' matched by '[-]?([0-9]+[.]?[0-9]*|[.][0-9]+)(E[-]?[0-9]+)?' Token '-3.4' matched by '[-]?([0-9]+[.]?[0-9]*|[.][0-9]+)(E[-]?[0-9]+)?' Token 'sqrt' matched by 'sqrt'</screen> </figure> <para>We provide error handling capabilities dealing with erroneous input <coref linkend="sd1_fig_errorHandlingBogusInput"/>:</para> <figure xml:id="sd1_fig_scannerErrorHandling"> <title>Adding error detection</title> <programlisting language="none">... final String[] patterns = new String[] { "sqrt", "[-]?([0-9]+[.]?[0-9]*|[.][0-9]+)(E[-]?[0-9]+)?",// Matches e.g. -1.5E-33 "\\+"};// Escape required avoiding regular expression syntax clash. try (final Scanner scanner = new Scanner("2.1 -3.4 <emphasis role="red">pbck</emphasis>") <co xml:id="sd1_fig_errorHandlingBogusInput"/>) { while (scanner.hasNext()) { boolean foundToken = false; for (final String p: patterns) { if (scanner.hasNext(p)) { foundToken = true; System.out.println("Token '" + scanner.next(p) + "' matched by '" + p + "'"); break; } } if (!foundToken) { System.out.println("Parsing error at '" + scanner.nextLine() + "'"); System.exit(1); } ...</programlisting> </figure> <para>Bogus input like <code>2.1 -3.4 <emphasis role="red">pbck</emphasis></code> will now be detected as such:</para> <figure xml:id="sd1_fig_scannerErrorResult"> <title>Error message</title> <screen>Token '2.1' matched by '[-]?([0-9]+[.]?[0-9]*|[.][0-9]+)(E[-]?[0-9]+)?' Token '-3.4' matched by '[-]?([0-9]+[.]?[0-9]*|[.][0-9]+)(E[-]?[0-9]+)?' <emphasis role="red">Parsing error at 'pbck'</emphasis></screen> </figure> <para>A token sequence may then be evaluated using the <link xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation#Postfix_evaluation_algorithm">postfix evaluation algorithm</link> (Read it!).</para> </section> </section> <section xml:id="sd1BonusCurrency"> <title>Currency converter, Summer 2017</title> <para>A set of predefined currencies shall be mutually converted. The user will be able to choose both a <quote>buy</quote> and a <quote>sell</quote> currency. The desired <quote>buy</quote> amount shall than be converted into the corresponding <quote>sell</quote> currency amount. The following video illustrates its intended usage:</para> <figure xml:id="sd1FigCurrencyConverterVideo"> <title>Running the currency converter terminal application.</title> <mediaobject> <videoobject> <videodata fileref="Fig/currencyConverter.webm"/> </videoobject> </mediaobject> </figure> <para>You may follow the subsequent steps:</para> <orderedlist> <listitem> <para>Choose a suitable representation which allows for defining currencies among with their corresponding conversion rates. A good starting point is being provided by <link xlink:href="https://en.wikipedia.org/wiki/Special_drawing_rights">IMF SDR currency units</link> leading to <link xlink:href="https://www.imf.org/external/np/fin/data/rms_mth.aspx?SelectDate=2017-03-31&reportType=CVSDR">flat currency table structures</link>. These allow for a simple text file representation e.g. <filename>currencies.txt</filename>:</para> <screen>Chinese Yuan: 9.348070 Euro: 1.264280 Japanese Yen: 153.778000 U.K. Pound Sterling: 1.102940 U.S. Dollar: 1.356070 Algerian Dinar: 148.618000 ...</screen> <para>The rates being provided here allow for converting each currency to its <link xlink:href="https://en.wikipedia.org/wiki/Special_drawing_rights">SDR</link> value. Thus converting e.g. Euro to U.S. Dollar requires a conversion to its <link xlink:href="https://en.wikipedia.org/wiki/Special_drawing_rights">SDR</link> value followed by a second conversion to U.S. Dollar.</para> <para>This way we only have to store a single rate for each currency in question.</para> </listitem> <listitem> <para>Write a class reading the above table and storing all currencies among with their rates into a suitable array. <link xlink:href="https://www.javatpoint.com/how-to-open-a-file-in-java">How to Open a File in Java</link> provides different methods reading and scanning file input.</para> </listitem> <listitem> <para>Create user interface dialogs providing the »glue« to access currency information.</para> </listitem> </orderedlist> </section> <section xml:id="sd1SectProjectAddressDatabase"> <title>An address database, Winter 2016</title> <para>Consider executing <command>java</command> <option>-jar <link xlink:href="/Sd1/Ref/Src/address.zip">address.zip</link></option>:</para> <screen>Current person records: 0: Frank Bone, email: bone@re.org, phone: 885-11-87 1: Julius Caesar, email: -, phone: - 2: Clive Freemantle, email: freemantle@spam.org, phone: 112-22 33 12 3: Eve Gardener, email: -, phone: 32-44-2234 -------------------------------------- Main options: 0: Browse person entries <co linkends="sd1CalloutAddressDbProject-1" xml:id="sd1CalloutAddressDbProject-1-co"/> 1: Toggle filtering person entries <co linkends="sd1CalloutAddressDbProject-2" xml:id="sd1CalloutAddressDbProject-2-co"/> 2: Create new person entry <co linkends="sd1CalloutAddressDbProject-3" xml:id="sd1CalloutAddressDbProject-3-co"/> 3: Delete person entry <co linkends="sd1CalloutAddressDbProject-4" xml:id="sd1CalloutAddressDbProject-4-co"/> 4: Exit Your choice: 2</screen> <calloutlist> <callout arearefs="sd1CalloutAddressDbProject-1-co" xml:id="sd1CalloutAddressDbProject-1"> <para>List all person records within your database. In presence of a filter (See <coref linkend="sd1CalloutAddressDbProject-2-co"/>) only a subset of entries will be displayed.</para> </callout> <callout arearefs="sd1CalloutAddressDbProject-2-co" xml:id="sd1CalloutAddressDbProject-2"> <para>For large person databases listing all entries becomes cumbersome. You may want to define filters limiting your visible result output by examining the last name attribute.</para> <para><xref linkend="glo_Java"/> allows for matching strings by <link xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html">regular expressions</link> to be used with e.g. <link xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#matches(java.lang.String)">String.matches(...)</link>. Examples:</para> <glosslist> <glossentry> <glossterm>^A.*</glossterm> <glossdef> <para>Strings starting (^) with capital A followed by an arbitrary number of arbitrary characters.</para> </glossdef> </glossentry> <glossentry> <glossterm>^[G-J].*</glossterm> <glossdef> <para>Strings starting with a capital letters G,H,I or J followed by an arbitrary number of arbitrary characters.</para> </glossdef> </glossentry> <glossentry> <glossterm>.*s$</glossterm> <glossdef> <para>Strings ending with character s.</para> </glossdef> </glossentry> </glosslist> </callout> <callout arearefs="sd1CalloutAddressDbProject-3-co" xml:id="sd1CalloutAddressDbProject-3"> <para>Creating a new person record</para> </callout> <callout arearefs="sd1CalloutAddressDbProject-4-co" xml:id="sd1CalloutAddressDbProject-4"> <para>Deleting a person record from your database</para> </callout> </calloutlist> <para>In order for entries to survive program termination we need a <link xlink:href="https://en.wikipedia.org/wiki/Persistence_(computer_science)">persistence</link> mechanism. The example application will create a new file <filename>address.txt</filename> on first invocation when adding at least one person:</para> <screen>Frank,Bone, ,885-11-87 Julius,Caesar, , Ee,Ff,hh,45 A,B, ,24 Eve,Gardener, ,32-44-2234</screen> <para>Each person record is being represented by four values being separated by three commas. Empty attribute values require a single space. The method <link xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#split(java.lang.String)">String.split(...)</link> is your friend when reading this file on program startup using <quote>,</quote> as separator split token.</para> <para>The following resources may help you getting started with <xref linkend="glo_Java"/> file input/output:</para> <itemizedlist> <listitem> <para><link xlink:href="https://www.tutorialspoint.com/java/java_files_io.htm">TutorialsPoint Java - Files and I/O</link></para> </listitem> <listitem> <para><link xlink:href="https://docs.oracle.com/javase/tutorial/essential/io/">Oracle The Java™ Tutorials Lesson: Basic I/O</link></para> </listitem> <listitem> <para><link xlink:href="http://tutorials.jenkov.com/java-io/index.html">jenkov.com Java IO Tutorial</link></para> </listitem> </itemizedlist> </section> <section xml:id="sd1SectProjectGrep"> <title>Poor man's <xref linkend="glo_UNIX"/> <command xlink:href="https://linux.die.net/man/1/grep">grep</command>, Summer 2016</title> <para>The <xref linkend="glo_UNIX"/> operating system provides a command <command xlink:href="https://linux.die.net/man/1/grep">grep</command> which allows for retrieving occurrences of a given string in text files. We consider an example text file <filename>input.txt</filename> containing four lines:</para> <literallayout linenumbering="numbered">Roses are nice flowers. Red wine is tasty The red cross acts worldwide Mayflower used to be a ship.</literallayout> <para>We search this file <filename>input.txt</filename> for the occurrence of the string <quote>flower</quote> being contained in lines 1 and 4:</para> <screen>> grep <emphasis role="bold">flower</emphasis> input.txt Roses are nice <emphasis role="bold">flower</emphasis>s. May<emphasis role="bold">flower</emphasis> used to be a ship.</screen> <para>Thus the <command xlink:href="https://linux.die.net/man/1/grep">grep</command> command echoes all lines containing the search string in question to standard output. Adding the command line option <option>-i</option> allows for case insensitive searches:</para> <screen linenumbering="unnumbered">> grep <option>-i</option> <emphasis role="bold">red</emphasis> input.txt <emphasis role="bold">Red</emphasis> wine is tasty The <emphasis role="bold">red</emphasis> cross acts worldwide</screen> <para>This time all possible variants like <quote>Red</quote>, <quote>red</quote>, <quote>RED</quote> and so on will match.</para> <para><command xlink:href="https://linux.die.net/man/1/grep">grep</command> also allows for searching multiple files. Consider a second file <filename>inputSecond.txt</filename>:</para> <screen linenumbering="numbered">Errors will show up in red. Let's start bug fixing</screen> <para>We may search for case insensitive (<option>-i</option> again) appearances of <quote>red</quote> within both files:</para> <screen linenumbering="unnumbered">> grep -i <emphasis role="bold">red</emphasis> input.txt inputSecond.txt input.txt:<emphasis role="bold">Red</emphasis> wine is tasty input.txt:The <emphasis role="bold">red</emphasis> cross acts worldwide inputSecond.txt:Errors will show up in <emphasis role="bold">red</emphasis>.</screen> <para>Finally the <option>-l</option> option will filter individual appearances just showing filenames containing matches:</para> <screen>> grep -l Red input.txt inputSecond.txt input.txt</screen> <para>In contrast a case insensitive search combining both <option>-i</option> and <option>-l</option> options yields:</para> <screen>> grep -i -l Red input.txt inputSecond.txt input.txt inputSecond.txt</screen> <para>The <command xlink:href="https://linux.die.net/man/1/grep">grep</command> command may read its input from standard input allowing for <link xlink:href="https://en.wikipedia.org/wiki/Pipeline_(Unix)">pipes</link>. This way another command's output feeds into a subsequently executed command. As an example consider a recursive search for HTML files using the <link xlink:href="https://linux.die.net/man/1/find">find</link> command:</para> <screen>> find . -name \*.html ./Sd1/Wc/wc/Testdata/input.html ./Sda1/rdbmsXml2Html/TestData/climbingprice.html ./Sda1/NoCast/src/main/resources/gallery.html ./Sda1/Jdom/Html2Html/src/main/resources/imageExampleNew.html ./Sda1/Jdom/Html2Html/src/main/resources/imageExample.html ./Sda1/VerifyImgAccess/fileextref.html</screen> <para>We want to restrict the above list to pathnames containing the string <quote>Example</quote>. This may be achieved by <link xlink:href="https://en.wikipedia.org/wiki/Pipeline_(Unix)">piping</link> the <link xlink:href="https://linux.die.net/man/1/find">find</link> command's output as input to <command xlink:href="https://linux.die.net/man/1/grep">grep</command> searching for the occurrence of the string <quote>Example</quote>. Technically both processes get connected by means of the pipe symbol <quote>|</quote>:</para> <screen>> find . -name \*.html|grep Example ./Sda1/Jdom/Html2Html/src/main/resources/imageExampleNew.html ./Sda1/Jdom/Html2Html/src/main/resources/imageExample.html</screen> <tip> <orderedlist> <listitem> <para>Read about reading from files by using instances of <classname xlink:href="http://www.tutorialspoint.com/java/io/bufferedreader_readline.htm">java.io.BufferedReader</classname>.</para> </listitem> <listitem> <para>Reading from standard input may be achieved by:</para> <programlisting language="java">final BufferedReader source = new BufferedReader(new InputStreamReader(System.in)); ...</programlisting> </listitem> <listitem> <para>You may create an executable jar archive using Maven. Starting from the <code>mi-mavem-archetype-quickstart</code> your <filename>pom.xml</filename> already contains a blueprint. Just insert your class containing the entry <methodname>main(...)</methodname> method (i.e. <classname>de.hdm_stuttgart.mi.sd1.grep.Grep</classname> in the current example) accordingly:</para> <programlisting language="xml"><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.1</version> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class><emphasis role="bold">de.hdm_stuttgart.mi.sd1.grep.Grep</emphasis></Main-Class> </manifestEntries> </transformer> </transformers> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin></programlisting> <para>Running <command>mvn</command> <option>install</option> will create an executable jar file like e.g. <filename>~/.m2/repository/de/hdm-stuttgart/mi/sd1/grep/0.9/grep-0.9.jar</filename> with <quote>~</quote> denoting your home directory:</para> <screen>> mvn install [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building grep 0.9 [INFO] ------------------------------------------------------------------------ ... ------------------------------------------------------- T E S T S ------------------------------------------------------- Running de.hdm_stuttgart.mi.sd1.grep.CommandLineTest Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.757 sec Results : Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 ... [INFO] Installing /home/goik/workspace/sd-project-summer/grep/target/grep-0.9.jar to /home/goik/.m2/repository/de/hdm-stuttgart/mi/sd1/grep/0.9/grep-0.9.jar ... </screen> <para>Due to our <code><Main-Class>de.hdm_stuttgart.mi.sd1.grep.Grep</Main-Class></code> declaration in <filename>pom.xml</filename> this jar file is executable:</para> <screen>> java -jar ~/.m2/repository/de/hdm-stuttgart/mi/sd1/grep/0.9/grep-0.9.jar No search string given Usage: grep [-i] [-l] searchString [file 1] [file 2] ...</screen> <para>There are further simplification steps:</para> <orderedlist> <listitem> <para>Making the jar file executable using <command xlink:href="https://linux.die.net/man/1/chmod">chmod</command> allows for omitting the <command>java</command> command:</para> <screen>> <command>chmod</command> <option>+x</option> <filename>~/.m2/repository/de/hdm-stuttgart/mi/sd1/grep/0.9/grep-0.9.jar</filename> > <filename>~/.m2/repository/de/hdm-stuttgart/mi/sd1/grep/0.9/grep-0.9.jar</filename> No search string given Usage: grep [-i] [-l] searchString [file 1] [file 2] ...</screen> <para>Notice <quote>~</quote> representing a user's home directory.</para> </listitem> <listitem> <para>We may copy the jar archive to a standard location containing executable commands:</para> <screen>> <command>mkdir</command> <filename>~/bin</filename> > > <command>cp</command> <filename>~/.m2/repository/de/hdm-stuttgart/mi/sd1/grep/0.9/grep-0.9.jar</filename> <filename>~/bin/jgrep</filename> > > <command>~/bin/jgrep</command> No search string given Usage: grep [-i] [-l] searchString [file 1] [file 2] ...</screen> </listitem> <listitem> <para>We may add this directory to the set of directories being searched by the operating system's command line interpreter for executable commands. This is being achieved by either creating or modifying a file <filename>~/.profile</filename> in the user's home directory using a text editor. <filename>~/.profile</filename> should contain:</para> <screen>PATH="$HOME/bin:$PATH"</screen> <para>After logging out and on again your PATH environment variable should contain your <filename>~/bin</filename> component:</para> <screen>> <command>echo</command> <envar>$PATH</envar> <emphasis role="bold">/home/goik/bin</emphasis>:/usr/local/sbin:/usr/local/bin:/usr/...</screen> <para>You should now be able to call <command>jgrep</command> from arbitrary filesystem locations:</para> <screen>> cd Desktop/ > cat Testdata/input.txt | ./bin/mygrep red The red cross acts worldwide</screen> </listitem> </orderedlist> </listitem> <listitem xml:id="sd1ProjectGrepUnitTestingHint"> <para>Testing requires capturing of output being generated by e.g. <methodname xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#out">System.out</methodname><code>.</code><methodname xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/PrintStream.html#println(java.lang.String)">println(...)</methodname> calls. Consider the following code writing the string <quote>Hello World!</quote> to standard output:</para> <programlisting language="java">public class App { /** * @param args Unused */ public static void main( String[] args ) { System.out.print( "Hello World!" ); } }</programlisting> <para>We want to set up a <productname>Junit</productname> test which captures the output to compare it with the expected string value <code language="java">"Hello World!"</code>. Following <uri xlink:href="https://stackoverflow.com/questions/1119385/junit-test-for-system-out-println">http://stackoverflow.com/questions/1119385/junit-test-for-system-out-println</uri> we redefine the standard output stream by a private instance of <classname xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/ByteArrayOutputStream.html">java.io.ByteArrayOutputStream</classname>. Due to <productname>Junit</productname>'s <classname xlink:href="http://junit.sourceforge.net/javadoc/org/junit/Before.html">@Before</classname> and <classname xlink:href="http://junit.sourceforge.net/javadoc/org/junit/After.html">@After</classname> annotations this instance replaces <classname xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#out">System.out</classname> during our tests:</para> <programlisting language="java">import java.io.ByteArrayOutputStream; import java.io.PrintStream; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * Unit test for simple App. */ public class AppTest { private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); <classname xlink:href="http://junit.sourceforge.net/javadoc/org/junit/Before.html">@Before</classname> public void setUpStreams() { System.setOut(new PrintStream(outContent)); } <classname xlink:href="http://junit.sourceforge.net/javadoc/org/junit/After.html">@After</classname> public void cleanUpStreams() { System.setOut(null); outContent.reset(); } /** * Test method accessing output generated by System.out.println(...) calls. */ @Test public void testApp() { App.main(new String[]{}); // Calling main() method printing "Hello World!" Assert.assertEquals("Hello World!", outContent.toString()); } }</programlisting> </listitem> </orderedlist> </tip> </section> <section xml:id="sd1ProjectSieveErathostenes"> <title>Project <link xlink:href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Euler.27s_Sieve">Euler's sieve</link>, Winter 2015</title> <para>The project's goal is about:</para> <itemizedlist> <listitem> <para>Generating all prime numbers of type <code language="java">int</code>.</para> </listitem> <listitem> <para>Using this set of prime numbers for decomposing arbitrary <code language="java">int</code> values into prime factors e.g.:</para> <para>1050 = 2 * 3 * 5 * 5 * 7</para> </listitem> </itemizedlist> <para>We start with the first task by implementing the <link xlink:href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Euler.27s_Sieve">Euler sieve algorithm</link>. We may use a boolean array representing e.g. the first 100 primes:</para> <programlisting language="java">boolean[] nonPrimes = new boolean[100];</programlisting> <para>This array will initially be filled with 100 <code language="java">false</code> values. The idea is using the <link xlink:href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Euler.27s_Sieve">Euler sieve algorithm</link> to turn all non-prime value to <code>true (t)</code> and leaving al prime index values at <code>false (f)</code>:</para> <screen>Index | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| 11| 12| 13| 14 ... ------+--+--+--+--+--+--+--+--+--+--+---+---+---+---+--- ... value | t| t| f| f| t| f| t| f| t| t| t| f| t| f| t ...</screen> <para>Since we intend to deal with a large number <code xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#MAX_VALUE">Integer.MAX_VALUE</code> of values (rather than just 100 ) we only consider odd values since even numbers are never prime except for the value 2. Thus 0 will represent 1, 1 will represent 3 and n will represent 2 * n + 1:</para> <screen>Index | 0| 1| 2| 3| 4| 5| 6| 7| ... -----------+--+--+--+--+--+---+---+---+ ... represents | 1| 3| 5| 7| 9| 11| 13| 15| ... -----------+--+--+--+--+--+---+---+---+ ... value | t| f| f| f| t| f| f| t| ...</screen> <para>This requires a boolean array of just <code xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#MAX_VALUE">Integer.MAX_VALUE / 2</code>. Start from the following skeleton:</para> <programlisting language="java">public class Sieve { final boolean[] nonPrimes; final int numPrimesFound; /** * Creating a prime number Euler variant sieve * * @param limit The last value to be considered. May or may not be prime. * */ public Sieve(final int limit) { ...// initialize nonPrimes and numPrimesFound. } ... /** * Test if a given value is prime or not * * @param candidate The value in question. * * @return True if value is prime, false otherwise. */ public boolean isPrime(final int candidate) { // Based on nonPrimes array ... } }</programlisting> <tip> <para>Decompose the <link xlink:href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Euler.27s_Sieve">Euler sieve algorithm</link> into smaller tasks and write appropriate tests. Start with small <code language="java">limit</code> values (like 20) and extend to <code xlink:href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#MAX_VALUE">Integer.MAX_VALUE</code> step by step.</para> </tip> <para>Once you've finished implementing the sieve continue by implementing the second task of prime decomposition. We recall the initial example:</para> <para>1050 = 2 * 3 * 5 * 5 * 7</para> <para>So the primes 2, 3 and 7 appear with frequency 1 whereas 5 appears with frequency 2. For meaningful operations we introduce a new class combining a prime number and its corresponding frequency of appearance to represent prime factor decompositions appropriately:</para> <programlisting language="java">/** * Representing a single prime factor among with its * frequency. * */ public class PrimeFrequency { /** * The prime's immutable value. */ public final int prime; /** * The prime's frequency of appearance. */ int frequency; /** * @param prime {@link #prime} * @param frequency The prime's frequency of appearance. */ public PrimeFrequency(final int prime, final int frequency) { this.prime = prime; this.frequency = frequency; } /** * @return The prime factor's frequency of appearance. */ public int getFrequency() { return frequency; } /** * change the given frequency value. * @param frequency change by this value. */ public void addFrequency(@SuppressWarnings("hiding") final int frequency) { this.frequency += frequency; } @Override public boolean equals(final Object obj) { ... } }</programlisting> <para>Now 2 * 3 * 5 * 5 * 7 may be represented by three (not four!) instances of <code language="java">PrimeFrequency</code>. In order to represent the whole product implement a second container:</para> <programlisting language="java">/** * Representing integer values by an ordered set of prime factors among with * their frequencies of appearance. * */ public class PrimeFrequencySet { private final static int initialCapacity = 16; private PrimeFrequency[] store = new PrimeFrequency[initialCapacity]; // May grow due to new factors. /** * Searching for the existence of a {@link PrimeFrequency} with a matching * prime value (frequency may be different!). * * Example: If the set contains the prime values {3, 7, 11} (we ignore * frequencies) then searching for: * <ul> * <li>3 return index 0</li> * <li>11 returns index 2</li> * <li>5 return -2</li> * <li>13 returns -3</li> * <li>2 returns -1</li> * </ul> * * @param primeFrequency The candidate to be looked up. * * @return If a prime value match exists return its index. If no match * exists return the negative value of the first index belonging to a set * value having a larger prime value than the candidate (the "natural" * insertion point) minus 1 (see example). */ public int find(final PrimeFrequency primeFrequency) { ... } /** * Either add a new prime or just add a prime's frequency to an existing one. * * @param primeFrequency If the prime is already in the current set, add its * frequency. Otherwise insert the new element preserving the order of prime * values. */ public void add(final PrimeFrequency primeFrequency) { ... } /** * @return The count of all prime factors. */ public int getLength() { ... } /** * The prime factor corresponding to a given index. * @param index . * @return . */ public PrimeFrequency get(int index) { ... } /** * @return All prime factors among with their respective frequencies. */ public PrimeFrequency[] get() { ... } }</programlisting> <para>This allows for decomposing arbitrary <code language="java">int</code> values into their prime factors. We show a unit test example:</para> <programlisting language="java">/** * Prime factor decomposition. */ public class FactorTest { final Sieve sieve = new Sieve(100000); @Test public void test2 () { PrimeFrequencySet pfs = sieve.getPrimeFactors(12); <co linkends="sd1ListingTestPrimeFactorDecompose-1" xml:id="sd1ListingTestPrimeFactorDecompose-1-co"/> Assert.assertEquals(2, pfs.getLength()); <co linkends="sd1ListingTestPrimeFactorDecompose-2" xml:id="sd1ListingTestPrimeFactorDecompose-2-co"/> Assert.assertEquals(new PrimeFrequency(2, 2), pfs.get(0)); <co linkends="sd1ListingTestPrimeFactorDecompose-3" xml:id="sd1ListingTestPrimeFactorDecompose-3-co"/> Assert.assertEquals(new PrimeFrequency(3, 1), pfs.get(1)); <co linkends="sd1ListingTestPrimeFactorDecompose-4" xml:id="sd1ListingTestPrimeFactorDecompose-4-co"/> } }</programlisting> <calloutlist> <callout arearefs="sd1ListingTestPrimeFactorDecompose-1-co" xml:id="sd1ListingTestPrimeFactorDecompose-1"> <para>Decomposing 12 into 2 * 2 * 3.</para> </callout> <callout arearefs="sd1ListingTestPrimeFactorDecompose-2-co" xml:id="sd1ListingTestPrimeFactorDecompose-2"> <para>We expect two factors:</para> <orderedlist> <listitem> <para>Prime factor 2 having frequency 2.</para> </listitem> <listitem> <para>Prime factor 3 having frequency 1.</para> </listitem> </orderedlist> </callout> <callout arearefs="sd1ListingTestPrimeFactorDecompose-3-co" xml:id="sd1ListingTestPrimeFactorDecompose-3"> <para>Check for first prime factor 2 of frequency 2.</para> </callout> <callout arearefs="sd1ListingTestPrimeFactorDecompose-4-co" xml:id="sd1ListingTestPrimeFactorDecompose-4"> <para>Check for second prime factor 3 of frequency 1.</para> </callout> </calloutlist> <para>For better illustration we provide the following example:</para> <programlisting language="java">public class Driver { /** * @param args Unused */ public static void main( String[] args ) { final Sieve sieve = new Sieve(Integer.MAX_VALUE); int value = Integer.MAX_VALUE; System.out.println("Value " + value + " is " + (sieve.isPrime(value)? "" : "not") + " prime."); value--; // Even Value System.out.println("Value " + value + " is " + (sieve.isPrime(value)? "" : "not") + " prime."); value --; // Non-prime as well. System.out.println("Value " + value + " is " + (sieve.isPrime(value)? "" : "not") + " prime."); value = Integer.MAX_VALUE - 1; final PrimeFrequencySet pfs = sieve.getPrimeFactors(value); System.out.print(value + " = "); String separator = ""; for (PrimeFrequency pf : pfs.get()) { for (int i = 0; i < pf.getFrequency(); i++) { System.out.print(separator + pf.prime); separator = " * "; } } System.out.println(); } }</programlisting> <para>This yields the following output:</para> <screen>Value 2147483647 is prime. Value 2147483646 is not prime. Value 2147483645 is not prime. 2147483646 = 2 * 3 * 3 * 7 * 11 * 31 * 151 * 331</screen> <para>Depending on your implementation you may encounter the following heap memory related error:</para> <screen>Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at de.hdm_stuttgart.mi.prim.sieve.Sieve.initSieve(Sieve.java:75) at de.hdm_stuttgart.mi.prim.sieve.Sieve.<init>(Sieve.java:60) at de.hdm_stuttgart.mi.prim.Driver.main(Driver.java:19)</screen> <para>The <xref linkend="glo_Java"/> virtual machine by default may not provide enough heap space. You may enlarge the default allocation by setting the <option>-Xmx</option>. Eclipse provides a convenient way by means of a run time configuration (Menu Run --> Run Configurations):</para> <informalfigure> <mediaobject> <imageobject> <imagedata fileref="Fig/primDriverRunConfigOverview.bmp"/> </imageobject> </mediaobject> </informalfigure> <para>Clicking the <quote>Arguments</quote> tab allows for setting the respective VM heap setting:</para> <informalfigure> <mediaobject> <imageobject> <imagedata fileref="Fig/primDriverRunConfigVmOptions.bmp"/> </imageobject> </mediaobject> </informalfigure> <para>This allows for running your application with a maximum of 4 gigabytes of heap space.</para> <para>With respect to automated unit testing (<command>mvn</command> <option>test</option>)you may want to set the same value in you <filename>pom.xml</filename> file</para> <programlisting language="xml"><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19</version> <configuration> <emphasis role="bold"><argLine>-Xmx4G</argLine></emphasis> </configuration> </plugin></programlisting> </section> </section> <section xml:id="sd1_exams"> <title>Past Software Development 1 examinations</title> <para>Solutions to implementation tasks are being provided below <link xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/-/tree/master/Klausuren/Sd1">https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/-/tree/master/Klausuren/Sd1</link>. Each sub folder contains:</para> <orderedlist> <listitem> <para>An <filename>Exam</filename> Folder containing the original implementation tasks.</para> </listitem> <listitem> <para>A <filename>Solve</filename> folder containing a proposed solution.</para> </listitem> </orderedlist> <xi:include href="Exam/2022/Winter/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2021/Winter/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2021/Summer/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2020/Winter_1/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2020/Winter_2/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2020/Summer/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2019/Winter/tasks.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2019/Summer/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2018/Winter/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> <xi:include href="Exam/2018/Summer/exam.xml" xpointer="element(/1)" ns:idfixup="auto"/> </section> <section xml:id="sect_Git"> <title>Working with git</title> <para><uri xlink:href="https://gitlab.mi.hdm-stuttgart.de">https://gitlab.mi.hdm-stuttgart.de</uri> provides an interface creating a new <xref linkend="glo_Soft_Git"/> project. This involves:</para> <figure xml:id="sd1_fig_media_gitlabCreateProjectSteps"> <title>Steps creating a new project</title> <orderedlist> <listitem> <para>Creating an empty new project itself.</para> </listitem> <listitem> <para>Adding fellow project users for participation.</para> </listitem> </orderedlist> </figure> <figure xml:id="sd1_fig_media_gitlabCreateProject"> <title>Creating a project at MI <productname>gitlab</productname></title> <mediaobject> <imageobject> <imagedata fileref="Fig/gitlabNewProject.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_media_gitClone"> <title>Cloning a git project</title> <screen>>git clone git@gitlab.mi.hdm-stuttgart.de:goik/vcintro.git Cloning into 'vcintro'... warning: <emphasis role="red">You appear to have cloned an empty repository</emphasis>.</screen> </figure> <figure xml:id="sd1_fig_media_gitCdIntoProject"> <title>Enter project folder, add <filename>Readme.md</filename></title> <informaltable border="0"> <tr> <td valign="top"><screen>>cd vcintro/ >vim Readme.md >git add Readme.md</screen></td> <td valign="top"><screen># Initial project description. Will be extended when adding more assets.</screen></td> </tr> </informaltable> </figure> <figure xml:id="sd1_fig_media_gitCommitChangeset"> <title>Committing change set</title> <informaltable border="0"> <tr> <td valign="top"><screen>EDITOR=vim git commit Readme.md </screen></td> </tr> <tr> <td valign="top"><screen><emphasis role="red">Adding Readme file in Markdown format</emphasis> # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # # Initial commit # # Changes to be committed: # new file: Readme.md</screen></td> </tr> </informaltable> </figure> <figure xml:id="sd1_fig_media_gitPush"> <title>Push to upstream repository</title> <screen>>git push Counting objects: 3, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 306 bytes | 306.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) <emphasis role="red">To gitlab.mi.hdm-stuttgart.de:goik/vcintro.git</emphasis> * [new branch] master -> master</screen> </figure> <figure xml:id="sd1_fig_gitCreateMaven"> <title>Inserting a Maven project</title> <screen>>mvn --batch-mode -e archetype:generate -Dversion=0.9 \ > -DgroupId=de.hdm_stuttgart.mi.sd1 \ > -DartifactId=first -DarchetypeGroupId=de.hdm_stuttgart.mi \ > -DarchetypeArtifactId=mi-maven-archetype-quickstart -DarchetypeVersion=1.2.1 </screen> <screen>>find first/ -type f first/.gitignore first/src/test/java/de/hdm_stuttgart/mi/sd1/AppTest.java first/src/main/java/de/hdm_stuttgart/mi/sd1/App.java first/src/main/resources/log4j2.xml first/pom.xml</screen> </figure> <figure xml:id="sd1_fig_gitInsertMavenStatus_1"> <title>git status 1</title> <screen>> git status On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "<emphasis role="red">git add <file></emphasis>..." to include in what will be committed) <emphasis role="red">first/</emphasis> nothing added to commit <emphasis role="red">but untracked files present</emphasis> (use "git add" to track)</screen> </figure> <figure xml:id="sd1_fig_gitAddMavenFiles"> <title>Adding Maven files to repository</title> <screen>><emphasis role="red">git add `find first/ -type f`</emphasis> >git status On branch master Your branch is up to date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: first/.gitignore new file: first/pom.xml new file: first/src/main/java/de/hdm_stuttgart/mi/sd1/App.java new file: first/src/main/resources/log4j2.xml new file: first/src/test/java/de/hdm_stuttgart/mi/sd1/AppTest.java</screen> </figure> <figure xml:id="sd1_fig_gitInsertMavenStatus_2"> <title>git status 2</title> <screen>>git status On branch master Your branch is up to date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: first/.gitignore new file: first/pom.xml new file: first/src/main/java/de/hdm_stuttgart/mi/sd1/App.java new file: first/src/main/resources/log4j2.xml new file: first/src/test/java/de/hdm_stuttgart/mi/sd1/AppTest.java</screen> </figure> <figure xml:id="sd1_fig_media_gitCommitMavenProjectFiles"> <title>Commit Maven project files</title> <informaltable border="0"> <tr> <td valign="top"><screen>EDITOR=vim git commit -a</screen></td> </tr> <tr> <td valign="top"><screen><emphasis role="red">Adding a Maven project.</emphasis> # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is up to date with 'origin/master'. # # Changes to be committed: # new file: first/.gitignore # new file: first/pom.xml # new file: first/src/main/java/de/hdm_stuttgart/mi/sd1/App.java # new file: first/src/main/resources/log4j2.xml # new file: first/src/test/java/de/hdm_stuttgart/mi/sd1/AppTest.java </screen></td> </tr> </informaltable> </figure> <figure xml:id="sd1_fig_gitInsertMavenStatus_3"> <title>git status 3</title> <screen>>git status On branch master <emphasis role="red">Your branch is ahead of 'origin/master' by 1 commit.</emphasis> (use "git push" to publish your local commits) nothing to commit, working tree clean</screen> </figure> <figure xml:id="sd1_fig_media_gitPushMaven"> <title>Push to upstream again</title> <screen>>git push Counting objects: 22, done. Delta compression using up to 8 threads. Compressing objects: 100% (10/10), done. Writing objects: 100% (22/22), 3.31 KiB | 1.10 MiB/s, done. Total 22 (delta 0), reused 0 (delta 0) <emphasis role="red">To gitlab.mi.hdm-stuttgart.de:goik/vcintro.git</emphasis> 32da2ff..4e19142 master -> master</screen> </figure> <figure xml:id="sd1_fig_media_gitUndoChanges"> <title>Reverting changes</title> <screen>>git status ... Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Readme.md </screen> <screen>git checkout -- Readme.md</screen> <screen>>git status Your branch is up to date with 'origin/master'. nothing to commit, working tree clean</screen> </figure> <figure xml:id="sd1_fig_media_gitPull"> <title>Pull changes from upstream</title> <screen>>git pull remote: Enumerating objects: 10, done. remote: Counting objects: 100% (10/10), done. remote: Compressing objects: 100% (6/6), done. remote: Total 6 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (6/6), done. From gitlab.mi.hdm-stuttgart.de:goik/vcintro 3751344..83bd7b9 master -> origin/master Updating 3751344..83bd7b9 Fast-forward Readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)</screen> </figure> </section> <section xml:id="sd1_sect_Maven"> <title>Apache Maven</title> <figure xml:id="sd1_fig_mavenReading"> <title>Maven: Recommended reading</title> <itemizedlist> <listitem> <para><xref linkend="bib_MavenRef2010"/></para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_whatIsMaven"> <title>What is Maven anyway?</title> <itemizedlist> <listitem> <para>Build tool</para> </listitem> <listitem> <para>Project management tool</para> <itemizedlist> <listitem> <para>Create reports</para> </listitem> <listitem> <para>Continuous integration support</para> </listitem> </itemizedlist> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_factsAndBenefits"> <title>Maven: Facts and benefits</title> <itemizedlist> <listitem> <para>Build tool</para> </listitem> <listitem> <para>Dependency management</para> </listitem> <listitem> <para>Repository system</para> </listitem> <listitem> <para>Plugin framework</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_maven_fig_conventionOverConfiguration"> <title>Convention Over Configuration</title> <itemizedlist> <listitem> <para>Sensible default values:</para> <itemizedlist> <listitem> <para>Source below <code>${basedir}/src/main/java</code></para> </listitem> <listitem> <para>Tests below <code>${basedir}/src/test</code></para> </listitem> <listitem> <para>Bytecode, jar/war archives below <code>${basedir}/target</code></para> </listitem> <listitem> <para>...</para> </listitem> </itemizedlist> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_maven_ProjectLayout"> <title>Maven project layout</title> <mediaobject> <imageobject> <imagedata fileref="Fig/projectLayout.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_maven_pomFile"> <title>The project object model file</title> <programlisting language="xml"><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 <uri xlink:href="http://maven.apache.org/xsd/maven-4.0.0.xsd">http://maven.apache.org/xsd/maven-4.0.0.xsd</uri>"> <modelVersion>4.0.0</modelVersion> ... </project></programlisting> </figure> <figure xml:id="sd1_maven_fig_pomFeatures"> <title>pom.xml characteristics</title> <itemizedlist> <listitem> <para>Declarative project description</para> <itemizedlist> <listitem> <para>Dependencies</para> </listitem> <listitem> <para>Builds</para> </listitem> <listitem> <para>Artifacts</para> </listitem> </itemizedlist> </listitem> <listitem> <para>No explicit instructions</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_maven_fig_pomVsMake"> <title>pom.xml vs. Makefile</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="xml"><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <configuration> <Main-Class>org.devel.App</Main-Class> ... <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> ...</programlisting></td> <td valign="top"><programlisting language="xml">module.o: module.c gcc -c -g module.c</programlisting></td> </tr> </informaltable> </figure> <section xml:id="sd1_maven_sect_pomBasiss"> <title>The project object model <filename>pom.xml</filename></title> <figure xml:id="sd1_fig_maven_helloWorldPom"> <title>«Hello, world» pom.xml</title> <programlisting language="xml"><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.hdm_stuttgart.mi</groupId> <artifactId>first</artifactId> <version>0.9</version> </project></programlisting> </figure> <figure xml:id="sd1_fig_maven_helloWorldPomExec"> <title>Executing «compile» phase</title> <screen>mkdir -p src/main/java <co linkends="sd1_fig_maven_helloWorldPomExec-1" xml:id="sd1_fig_maven_helloWorldPomExec-1-co"/> vim src/main/java/Hello.java <co linkends="sd1_fig_maven_helloWorldPomExec-2" xml:id="sd1_fig_maven_helloWorldPomExec-2-co"/> first> mvn compile ... [WARNING] <emphasis role="red">File encoding has not been set, using platform encoding UTF-8</emphasis>, <co linkends="sd1_fig_maven_helloWorldPomExec-3" xml:id="sd1_fig_maven_helloWorldPomExec-3-co"/> i.e. build is platform dependent! ... [ERROR] error: <emphasis role="red">Source option 5 is no longer supported</emphasis>. <co linkends="sd1_fig_maven_helloWorldPomExec-4" xml:id="sd1_fig_maven_helloWorldPomExec-4-co"/> Use 6 or later. [ERROR] error: <emphasis role="red">Target option 1.5 is no longer supported</emphasis>. <co linkends="sd1_fig_maven_helloWorldPomExec-5" xml:id="sd1_fig_maven_helloWorldPomExec-5-co"/> Use 1.6 or later. </screen> <calloutlist role="slideExclude"> <callout arearefs="sd1_fig_maven_helloWorldPomExec-1-co" xml:id="sd1_fig_maven_helloWorldPomExec-1"> <para>Create an empty folder designated for hosting <xref linkend="glo_Java"/> sources. See <xref linkend="sd1_fig_maven_ProjectLayout"/> for reference.</para> </callout> <callout arearefs="sd1_fig_maven_helloWorldPomExec-2-co" xml:id="sd1_fig_maven_helloWorldPomExec-2"> <para>Create an executable «Hello,world ...» class:</para> <programlisting language="java">public class Hello { public static void main(String[] args) { System.out.println("Hello!"); }</programlisting> </callout> <callout arearefs="sd1_fig_maven_helloWorldPomExec-3-co" xml:id="sd1_fig_maven_helloWorldPomExec-3"> <para>Ask <xref linkend="glo_Maven"/> for execution of the «compile» phase.</para> </callout> <callout arearefs="sd1_fig_maven_helloWorldPomExec-4-co" xml:id="sd1_fig_maven_helloWorldPomExec-4"> <para>We have not yet specified an encoding on project level. Thus two platforms using different encoding may provide different artifact outcomes.</para> </callout> <callout arearefs="sd1_fig_maven_helloWorldPomExec-5-co" xml:id="sd1_fig_maven_helloWorldPomExec-5"> <para>The world has moved: <xref linkend="glo_Java"/> 1.5 compilers are outdated.</para> </callout> </calloutlist> </figure> <figure xml:id="sd1_fig_maven_simplePomJavaVersionPromlem"> <title>Examining the <xref linkend="glo_Java"/> version culprit</title> <screen>>mvn <emphasis role="red">help:effective-pom</emphasis> <project ...> ... <plugin> <artifactId>maven-compiler-plugin</artifactId> ... > find ~/.m2/repository/ -name maven-compiler-plugin\* ... >jar -xf ~/.m2/repository/org/apache/maven/plugins/maven-compiler-plugin/3.7.0/maven-compiler-plugin-3.7.0.jar >cat <emphasis role="red">META-INF/maven/plugin.xml</emphasis></screen> <programlisting language="xml"><encoding implementation="java.lang.String" default-value="${<emphasis role="red">project.build.sourceEncoding</emphasis>}">${encoding}</encoding> <source implementation="java.lang.String" default-value="1.5">${<emphasis role="red">maven.compiler.source</emphasis>}</source> <target implementation="java.lang.String" default-value="1.5">${<emphasis role="red">maven.compiler.target</emphasis>}</target></programlisting> </figure> <figure xml:id="sd1_fig_maven_simplePom"> <title>Resolving encoding / <xref linkend="glo_Java"/> version issues</title> <programlisting language="none"><project ... xsd/maven-4.0.0.xsd"> ... <properties> <project.build.sourceEncoding><emphasis role="red">UTF-8</emphasis></project.build.sourceEncoding> <maven.compiler.source><emphasis role="red">1.8</emphasis></maven.compiler.source> <maven.compiler.target><emphasis role="red">1.8</emphasis></maven.compiler.target> </properties> ...</programlisting> <screen>rm -rf ~/.m2/repository/ mvn compile; find ~/.m2/repository/ -type f|wc -l <emphasis role="red">220</emphasis></screen> </figure> <figure xml:id="sd1_maven_fig_superPomInherit"> <title>POM inheritance</title> <mediaobject> <imageobject> <imagedata fileref="Fig/superPomInherit.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_maven_fig_superPom"> <title>The Super POM</title> <programlisting language="bash">jar -tf /usr/share/apache-maven-3.0.5/lib/<emphasis role="red">maven-model-builder-3.0.5.jar</emphasis> ... org/apache/maven/model/plugin/ReportingConverter.class org/apache/maven/model/<emphasis role="red">pom-4.0.0.xml</emphasis> org/apache/maven/model/profile/activation/FileProfileActivator$1.class ...</programlisting> </figure> <figure xml:id="sd1_maven_fig_superPomContent"> <title>pom-4.0.0.xml content</title> <programlisting language="xml">... <emphasis role="red"><!-- Does this ring a (security related?) bell? --></emphasis> <repositories> <repository> <id>central</id> <name>Central Repository</name> <url><emphasis role="red">http://repo.maven.apache.org/maven2</emphasis></url> ... <pluginRepositories> <pluginRepository> <id>central</id> <name>Central Repository</name> <url><emphasis role="red">http://repo.maven.apache.org/maven2</emphasis></url> ...</programlisting> </figure> <figure xml:id="sd1_maven_fig_favourHttps"> <title>Favour <abbrev>https</abbrev> in <filename>~/.m2/settings.xml</filename></title> <programlisting language="xml"><settings ... settings-1.0.0.xsd"> <mirrors> <mirror> <id>central-secure</id> <mirrorOf>central</mirrorOf> <name>Maven Central: Favour https over http.</name> <url><emphasis role="red">https</emphasis>://repo.maven.apache.org/maven2</url> </mirror> </mirrors> ...</programlisting> </figure> <figure xml:id="sd1_fig_maven_getEffectivePom"> <title>Resolving to effective pom.xml</title> <screen>first> <emphasis role="red">mvn help:effective-pom</emphasis> ... Effective POMs, after inheritance, interpolation, and profiles are applied: <project xmlns="http://maven.apache.org/POM/4.0.0" ...> <modelVersion>4.0.0</modelVersion> ... <plugin> <artifactId>maven-jar-plugin</artifactId> <version>2.3.2</version> <executions> <execution> <id>default-jar</id> ...</screen> </figure> </section> <section xml:id="sd1_maven_sect_plugins"> <title>Plugins</title> <figure xml:id="sd1_fig_maven_pluginArchitecture"> <title>Plugin architecture</title> <itemizedlist> <listitem> <para>Tiny core</para> </listitem> <listitem> <para>Plugin extensible</para> </listitem> </itemizedlist> <para>Hint: <command>mvn</command> <option>help:effective-pom</option> lists included plugins.</para> </figure> <figure xml:id="sd1_fig_maven_pluginSamples"> <title>Sample plugins</title> <itemizedlist> <listitem> <para>Testing: <code>maven-surefire-plugin</code></para> </listitem> <listitem> <para>Library dependencies: <code>maven-dependency-plugin</code></para> </listitem> <listitem> <para>Packaging: <code>maven-jar-plugin</code></para> </listitem> <listitem> <para>Documentation: <code>maven-javadoc-plugin</code></para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_maven_javadocPlugin"> <title>Example: The <code>maven-javadoc-plugin</code></title> <screen>first> <emphasis role="red">jar -tf ~/.m2/repository/org/apache/maven/plugins/\ maven-javadoc-plugin/3.0.0/maven-javadoc-plugin-3.0.0.jar</emphasis> ... META-INF/LICENSE META-INF/maven/org.apache.maven.plugins/maven-javadoc-plugin/plugin-help.xml javadoc-report.properties META-INF/maven/plugin.xml log4j.properties META-INF/NOTICE org/apache/maven/plugins/javadoc/AbstractFixJavadocMojo$JavaEntityTags.class org/apache/maven/plugins/javadoc/AggregatorTestJavadocReport.class ...</screen> </figure> </section> <section xml:id="sd1_maven_sect_dependencies"> <title>Dependencies</title> <figure xml:id="sd1_fig_maven_addingTestCapabilities"> <title>Adding test capabilities</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="none"><project ... maven-4.0.0.xsd"> ... <dependencies> <dependency> <groupId><emphasis role="red">junit</emphasis></groupId> <artifactId><emphasis role="red">junit</emphasis></artifactId> <version><emphasis role="red">4.12</emphasis></version> <emphasis role="red"><scope>test</scope></emphasis> </dependency>...</programlisting></td> <td valign="top"><programlisting language="none">import <emphasis role="red">org.junit.Test</emphasis>; import <emphasis role="red">org.junit.Assert</emphasis>; public class AppTest { <emphasis role="red">@Test</emphasis> public void doTest() { <emphasis role="red">Assert</emphasis>.assertEquals(1, 1); }</programlisting></td> </tr> <tr> <td colspan="2"><screen>first> mvn test [INFO] Scanning for projects... Running de.hdm_stuttgart.mi.sd1.AppTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.053 sec</screen></td> </tr> </informaltable> </figure> <figure xml:id="sd1_fig_maven_testListDeps"> <title>Dependency listing</title> <screen>first> <emphasis role="red">mvn dependency:tree</emphasis> ... [INFO] de.hdm_stuttgart.mi.sd1:first:jar:0.9 [INFO] \- <emphasis role="red">junit</emphasis>:<emphasis role="red">junit</emphasis>:<emphasis role="red">jar</emphasis>:<emphasis role="red">4.12</emphasis>:test [INFO] \- <emphasis role="red">org.hamcrest</emphasis>:<emphasis role="red">hamcrest-core</emphasis>:<emphasis role="red">jar</emphasis>:<emphasis role="red">1.3</emphasis>:test</screen> <itemizedlist> <listitem> <para><filename>~/.m2/repository/<emphasis role="red">junit</emphasis>/<emphasis role="red">junit</emphasis>/<emphasis role="red">4.12</emphasis>/<emphasis role="red">junit</emphasis>-<emphasis role="red">4.12</emphasis>.<emphasis role="red">jar</emphasis></filename></para> </listitem> <listitem> <para><filename>~/.m2/repository/<emphasis role="red">org/hamcrest</emphasis>/<emphasis role="red">hamcrest-core</emphasis>/<emphasis role="red">1.3</emphasis>/<emphasis role="red">hamcrest-core</emphasis>-<emphasis role="red">1.3</emphasis>.<emphasis role="red">jar</emphasis></filename></para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_fig_maven_whyHamcrestAbsence"> <title>Absence of <productname>hamcrest</productname> in <filename>pom.xml</filename></title> <programlisting language="xml"><project ... maven-4.0.0.xsd"> ... <emphasis role="red"><!-- no such entry --></emphasis> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> <scope>test</scope> </dependency> ...</programlisting> </figure> <figure xml:id="sd1_fig_maven_depJunitToHamcrest"> <title><filename>~/.m2/repository/junit/junit/4.12/junit-4.12.pom</filename></title> <programlisting language="xml"><dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> </dependencies></programlisting> </figure> <figure xml:id="sd1_fig_maven_hamcrestTransitiveDep"> <title>Transitive dependencies</title> <mediaobject> <imageobject> <imagedata fileref="Fig/transitiveDep.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_maven_switchTestNg"> <title>Oblivious to test implementation: TestNG</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="none"><project ... maven-4.0.0.xsd"> ... <dependencies> <dependency> <groupId><emphasis role="red">org.testng</emphasis></groupId> <artifactId><emphasis role="red">testng</emphasis></artifactId> <version><emphasis role="red">6.14.3</emphasis></version> <emphasis role="red"><scope>test</scope></emphasis> </dependency>...</programlisting></td> <td valign="top"><programlisting language="none">import <emphasis role="red">org.testng.annotations.Test</emphasis>; import <emphasis role="red">org.testng.Assert</emphasis>; public class AppTest { <emphasis role="red">@Test</emphasis> public void doTest() { <emphasis role="red">Assert</emphasis>.assertEquals(1, 1); }</programlisting></td> </tr> <tr> <td colspan="2"><screen>testng> <emphasis role="red">mvn test</emphasis> Running de.hdm_stuttgart.mi.sd1.AppTest Configuring TestNG with: org.apache.maven.surefire... Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.372 sec</screen></td> </tr> </informaltable> </figure> </section> <section xml:id="sd1_maven_sect_PhasesAndGoals"> <title>Lifecycle, phases and goals</title> <figure xml:id="sd1_fig_maven_prjPhases"> <title>Phases</title> <mediaobject> <imageobject> <imagedata fileref="Fig/phases.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_fig_maven_lifeCycle"> <title>Maven lifecyles and phases</title> <glosslist> <glossentry> <glossterm>Phases:</glossterm> <glossdef> <para>clean, compile, test, package, deploy, ...</para> </glossdef> </glossentry> <glossentry> <glossterm>Lifecycle</glossterm> <glossdef> <para>Sequence of <link xlink:href="http://maven.apache.org/ref/current/maven-core/lifecycles.html">named phases</link></para> </glossdef> </glossentry> <glossentry> <glossterm>Example: <command>mvn</command> <option>clean</option> (Lifecycle)</glossterm> <glossdef> <itemizedlist> <listitem> <para><code>pre-clean</code></para> </listitem> <listitem> <para><code>clean</code> (Phase)</para> </listitem> <listitem> <para><code>post-clean</code></para> </listitem> </itemizedlist> </glossdef> </glossentry> </glosslist> <para>See <link xlink:href="https://books.sonatype.com/mvnref-book/reference/lifecycle-sect-structure.html#lifecycle-sect-default">Default Lifecycle</link>.</para> </figure> <figure xml:id="sd1_fig_maven_pluginHook"> <title>hooking into phase</title> <programlisting language="none"><plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <emphasis role="red"><phase>generate-sources</phase></emphasis> <configuration> <outputDirectory>${project.build.directory}/metamodel</outputDirectory> <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor> </configuration> ...</programlisting> </figure> </section> </section> </appendix>